diff --git a/src/data/app-demos.json b/src/data/app-demos.json index 5bad29a..057f155 100644 --- a/src/data/app-demos.json +++ b/src/data/app-demos.json @@ -126,57 +126,42 @@ "io.pilot.bowmark": { "skill": "io.pilot.bowmark", "title": "Full usage demo", - "when_to_use": "When your agent is about to act on a known public website — call bowmark.ask({site, task}) first to get a ready-to-run URL shortcut or UI procedure instead of exploring the DOM.", - "metered": true, + "when_to_use": "When the answer only exists on a live website right now, after someone operates it: a price, stock, a fare, a quote, a form.", + "metered": false, "quickstart": { - "goal": "Get a navigation cheatsheet for a site + task", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"sec.gov\",\"task\":\"find Apple's latest 10-K filing\"}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"shortcut\":{\"template\":\"https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={ticker}&type=10-K\"}}" + "goal": "See whether a function already covers the task", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.get_library '{\"query\":\"flights\"}'", + "expect": "markdown: the bowmark.flights capability, with its types, signatures and examples", + "note": "Always call this first. An unrecognized query returns a one-line index, not an error." }, "examples": [ { - "title": "Ask for a UI procedure on a product surface", - "goal": "Scope with a path to skip the ambiguous_scope round-trip", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"google.com/maps\",\"task\":\"get directions from SFO to downtown\"}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"ui_procedure\":{\"steps\":[...]}}" + "title": "Search flights across several aggregators", + "goal": "One capability fans out, dedupes the same flight, sorts by price", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const { flights, warnings } = await bowmark.flights.search({ from: \\\"YYZ\\\", to: \\\"LHR\\\", depart: \\\"2026-09-14\\\" }); return { best: flights[0], warnings };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"best\":{\"price\":412,\"site\":\"google_flights\"},\"warnings\":[]}}", + "note": "These prices exist only after each site's own live poll, so no static fetch reaches them. `warnings` names a site dropped." }, { - "title": "Request the signed-in surface", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"github.com\",\"task\":\"create a new private repo\",\"variants\":{\"auth_state\":\"logged_in\",\"role\":\"owner\"}}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"ui_procedure\":{\"steps\":[...]},\"variants_assumed\":{...}}" + "title": "Read any page as markdown", + "goal": "Cheap GET first, a browser only if the page proves it needs one", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const p = await bowmark.read.page(\\\"https://example.com/pricing\\\"); return { title: p.title, servedBy: p.servedBy, content: p.content };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"title\":\"Pricing\",\"servedBy\":\"fetch\",\"content\":\"# Pricing\\n...\"}}", + "note": "`servedBy` names the cheapest rung that worked, and `wall` names the bot wall when there is one." }, { - "title": "Report the outcome to keep cheatsheets fresh", - "goal": "success=true only if every step ran clean — no retries or extra clicks", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.report_outcome '{\"envelope_id\":\"\",\"success\":true}'", - "expect": "{\"id\":\"...\"}" + "title": "Drill into one site instead of the fan-out", + "goal": "One named site, two dependent calls, one script", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const k = bowmark.providers.kayak; const [best] = await k.search({ from: \\\"SFO\\\", to: \\\"JFK\\\", depart: \\\"2026-09-15\\\" }); const sellers = await k.getBookingOptions(best); return { price: best.price, sellers: sellers.length };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"price\":198,\"sellers\":7}}", + "note": "A provider returns that site's native shape, so you give up the cross-site dedupe and the routing around failures." } ], - "cost": { - "unit": "requests (managed key, currently unmetered)", - "free_budget": "managed — no per-user charge today", - "hard_cap_usd": 0, - "operations": [ - { - "op": "bowmark.ask", - "price": "free", - "note": "the nav-recipe call (POST /v1/ask); managed key, no per-op meter currently" - }, - { - "op": "bowmark.report_outcome", - "price": "free", - "note": "feedback that triggers a re-crawl; no charge" - } - ], - "worked_total": "Managed key with quota 0 — there is no per-user dollar charge today; both methods are free to call." - }, "gotchas": [ - "Put intent in `task`, never a URL — 'find Apple's latest 10-K', not a link.", - "Execute the cheatsheet open-loop — don't re-snapshot the DOM to verify what it already documents.", - "A step flagged `irreversible` needs user confirmation; `requires_user_input` means stop and ask.", - "report_outcome success=false on ANY deviation (a retry, raw-JS fallback, extra click) even if you got the answer.", - "Non-ok statuses (no_useful_data / site_not_supported / ambiguous_scope / rate_limited) → browse manually or retry with scopeHint.", - "Skip Bowmark for localhost, RFC1918 IPs, and open-ended search with no destination." + "Call `get_library` before `run`, always. A script uses names only it supplies, and it touches no website.", + "Check `status` before `ok`. `partial` is a real result, narrower than asked; `incomplete.summary` names what never answered, and `failures[].fixable` true means YOUR argument was wrong, so fix and re-run.", + "`needs_user` is a login pause, not a failure: give the user `meta.handoff.url`, wait, then re-send the script.", + "`bowmark` is the only I/O in a script. No `fetch`, `import`, filesystem or `process`. Write a plain async body and `return`." ], "next": [ "io.pilot.bowmark bowmark.help '{}'" diff --git a/src/data/app-methods.json b/src/data/app-methods.json index 973c59f..831314b 100644 --- a/src/data/app-methods.json +++ b/src/data/app-methods.json @@ -881,16 +881,12 @@ ], "io.pilot.bowmark": [ { - "name": "bowmark.ask", - "summary": "Give it a site and a plain-English task (e.g. 'find Apple's latest 10-K') and it returns a ready-to-run cheatsheet — a URL shortcut to fill and navigate and/or a short ui_procedure of steps. Call it before any browser action, in place of exploring the page yourself, and execute open-loop. status=ok returns an id; no_useful_data/site_not_supported → browse manually; ambiguous_scope → retry with scopeHint; rate_limited → back off. Pass variants:{auth_state:'logged_in'} for signed-in surfaces. Intent, not a URL." + "name": "bowmark.get_library", + "summary": "START HERE. Read-only, and it touches no website. Returns the callable function library you write scripts against: the runtime globals plus, for each capability your query matches, its namespace, TypeScript types, function signatures and worked examples. Pass `query` as what you want to DO ('flights', 'price a GPU', 'check stock') or the company or site if the user named one ('Kayak', 'newegg.com'); a phrase in the user's own words is fine. You get what you asked about and nothing else. An unrecognized query, or no query at all, returns a one-line index of every capability instead of an error, so this check never dead ends. Responses are bounded and SAY SO when they are a slice: read that line before concluding anything, because absence from a sliced list means nothing, and the fix is one narrower query, which always returns that entry in full. Two tiers come back: capabilities like `bowmark.flights.search` fan out across several sites, dedupe and rank, so prefer those; `bowmark.providers.kayak.search` is one named site in its native shape." }, { - "name": "bowmark.report_outcome", - "summary": "After running a cheatsheet from ask, report whether every step ran exactly as written (envelope_id + success, optional evidence). success=true only if it ran clean — no retries, no raw-JS fallback, no extra clicks; false on any deviation, even if you still got the answer. Honest results trigger a re-crawl that keeps the cheatsheet fresh." - }, - { - "name": "bowmark.help", - "summary": "Discovery: every method with params, kind, and latency class." + "name": "bowmark.run", + "summary": "Executes your script on the real websites and returns what came back. Call bowmark.get_library FIRST: it gives the exact function names, argument shapes and return types. The language is plain async JavaScript. `bowmark` is a ready global with no import, every call is awaited, and you get real control flow: if, loops, array methods, and Promise.all for fan-out. `return` a value to get it back, `log(...)` for progress lines. `bowmark` is the ONLY I/O: no fetch, no process, no filesystem, no import or require. Write a plain async body, not a wrapping function, and keep it small and deterministic, because it runs in a hard sandbox with CPU, memory and wall-clock ceilings. `script` is the only argument; there is no site argument. Returns {ok, status, result, logs, error, ms}. CHECK `status` BEFORE `ok`: ok | partial (the script ran and the result is real and usable but narrower than asked, ok stays true, and incomplete.summary names what never answered; this is not a failure. Check incomplete.failures[].fixable: true means YOUR argument was rejected, not the site, so fix it and run again; otherwise re-running returns the same thing) | error | needs_user (a site wants a signed-in session, so the run PAUSED: hand meta.handoff.url to the user, wait, then re-send the identical script). Bowmark drives the sites server side, so your agent never opens a browser." } ], "io.pilot.docker": [ diff --git a/src/data/app-overrides.json b/src/data/app-overrides.json index 00b743c..8ed4001 100644 --- a/src/data/app-overrides.json +++ b/src/data/app-overrides.json @@ -143,33 +143,50 @@ }, "io.pilot.bowmark": { "name": "Bowmark", - "tagline": "Navigation cheatsheets for public websites, so agents run cheaper, faster, and more accurately.", - "description": "Bowmark gives agents a **pre-computed cheatsheet** for the task at hand. A cheatsheet is a compact, ready-to-run answer for one task on one site: a parameterized URL `shortcut` and/or a short `ui_procedure` of UI steps. Instead of burning tokens re-reading a site's DOM and guessing its way through the page, your agent calls `ask({ site, task })` and gets the exact path back. It spends less, finishes sooner, and lands on the right action the first time.\n\nBowmark is **free to use** — no signup and no API key to manage. It's plain request/response REST — no websockets, no server-side browser, no async jobs. Your agent runs the cheatsheet in its own browser; Bowmark only supplies the path.\n\n**Methods.** `bowmark.ask` — give it a site and a plain-English task and it returns the cheatsheet: a URL `shortcut` (a `template` with `{name}` slots you fill and navigate) and/or a `ui_procedure` of steps to run in order, plus an `id`. Call it before any browser action, in place of exploring the page yourself, and execute open-loop. `bowmark.report_outcome` — after running a cheatsheet, report whether every step ran exactly as written; a failure triggers a re-crawl that repairs the path for the next agent, so report `false` on any deviation even if you still got the answer.\n\n**Syntax & edge cases.** `site` is a registrable domain, optionally with a product surface (`google.com`, `docs.stripe.com`, `google.com/maps`); `task` is intent, never a URL. Request the signed-in view with `variants: { auth_state: \"logged_in\", role: \"owner\" }` (also `locale` / `region` / `currency`). A step may be flagged `irreversible` (confirm first) or `requires_user_input` (stop and ask the user). Non-`ok` statuses: `no_useful_data` / `site_not_supported` → browse manually; `ambiguous_scope` → retry with `scopeHint`; `rate_limited` → back off until `error.retry_after` (only new cheatsheet synthesis is capped). Skip it for localhost, RFC1918 IPs, and open-ended search with no destination.", + "tagline": "Live data from real websites: prices, stock, fares and quotes, including what only appears after you operate the page.", + "description": "Bowmark gives agents a **typed function library for the live web**, and runs the script they write against it on the real sites. `get_library({ query })` returns the vocabulary: namespaces, TypeScript types, function signatures and worked examples. `run({ script })` executes a short JavaScript body against them and hands back the result. Ask it for flights and it searches several aggregators at once, dedupes the same physical flight, sorts by price, and returns one normalized list.\n\n**The data it reaches is the kind an index cannot hold.** A fare that only exists after the site's own live poll completes. A price that appears once dates are entered. Stock for one postcode. A quote behind a form. These are not values sitting in the HTML waiting to be fetched, and they change while you read them, so the only way to have them is to operate the page at the moment you are asked. Bowmark does that and hands back structured JSON. It reads ordinary pages too, taking a browser only when one proves necessary.\n\nIt's plain request/response REST: no websockets, no async jobs, and no browser on your side. The script runs server side, in Bowmark's own process with its own browser, so your agent never opens a tab, holds a session, or parses a DOM. A capability like `bowmark.flights.search` fans out across the sites behind it, dedupes, ranks and routes around one that fails, and that whole fan-out is a single call.\n\n**Methods.** `bowmark.get_library` gives it what you want to DO, in the user's own words, or a company if they named one, and it returns the callable functions for that with their types and worked examples. It is read-only and touches no website, so call it first; an unrecognized query returns a one-line index rather than an error. `bowmark.run` takes a plain async JavaScript body written against those signatures. `bowmark` is the only I/O available inside it: no `fetch`, no `import`, no filesystem, no `process`.\n\n**What it covers today.** Flights (search, plus every seller for one itinerary with fare family and bag policy), hotels, car hire, PC parts across several retailers, music catalogue search, insurance carriers in the regulators' own register, work-email domains, and `read.page` / `read.pages` for any page as markdown, text or HTML. Individual sites are callable directly at `bowmark.providers.*` when you want one specific site rather than the fan-out.\n\n**Syntax & edge cases.** Check `status` before `ok`. `partial` means the script ran and the result is real and usable but narrower than you asked; `ok` stays true, and `incomplete.summary` names what never answered. `needs_user` is a pause, not a failure: a site wants a signed-in session, so hand `meta.handoff.url` to the user, wait, then re-send the identical script. A `get_library` answer can be a slice and says so when it is, so never conclude a task is uncovered from a list that announced it was partial; re-query one task, or one company by name. Prefer a capability over `bowmark.providers.*` unless you want one specific site. Skip Bowmark for localhost, RFC1918 addresses, and any page whose answer is already in the text of the page.", "vendor": "Bowmark AI", "vendorUrl": "https://bowmark.ai", "license": "Proprietary", "sourceUrl": "https://github.com/bowmark-ai/plugin", "homepage": "https://bowmark.ai", - "version": "0.1.0", + "version": "1.0.1", "categoriesRaw": [ "web", "browser", "automation" ], "keywords": [ + "live-data", + "prices", + "availability", + "stock", + "fares", + "quotes", + "web", "browser", - "navigation", - "recipes", - "cheatsheets", - "websites", - "playwright", - "puppeteer", - "computer-use", - "scraping" - ], - "bundleBytes": 5070051, - "installedBytes": 9105408, + "booking", + "computer-use" + ], + "bundleBytes": 5072454, + "installedBytes": 9113971, "changelog": [ + { + "version": "1.0.1", + "notes": [ + "Route the app's two methods through /v1/pilot-app/*, so calls arriving from the Pilot Protocol app store are attributable. No change to method names, parameters, or behaviour.", + "Rewrite the product demo to fit a small context window: three worked examples covering three distinct shapes rather than five with overlap, and tighter gotchas.", + "Document incomplete.failures[].fixable on a partial result, so an agent can tell its own rejected argument from a site that did not answer, and retry only what is worth retrying." + ] + }, + { + "version": "1.0.0", + "notes": [ + "Replaces bowmark.ask and bowmark.report_outcome with bowmark.get_library and bowmark.run. Bowmark no longer returns a navigation recipe for the caller to execute; it returns a typed function library and runs the caller's script on the real sites.", + "Execution moved server side. The calling agent no longer needs a browser of its own.", + "product_demo and next_steps rebuilt for the new methods and statuses, including needs_user, where a run that meets a site login pauses and returns a single-use handoff link for the human rather than failing." + ] + }, { "version": "0.1.0", "notes": [ diff --git a/src/data/apps.ts b/src/data/apps.ts index cfe2387..4aceda3 100644 --- a/src/data/apps.ts +++ b/src/data/apps.ts @@ -4301,24 +4301,25 @@ export const apps: App[] = [ { "id": "io.pilot.bowmark", "name": "Bowmark", - "tagline": "Navigation cheatsheets for public websites, so agents run cheaper, faster, and more accurately", - "description": "Bowmark gives agents a **pre-computed cheatsheet** for the task at hand. A cheatsheet is a compact, ready-to-run answer for one task on one site: a parameterized URL `shortcut` and/or a short `ui_procedure` of UI steps. Instead of burning tokens re-reading a site's DOM and guessing its way through the page, your agent calls `ask({ site, task })` and gets the exact path back. It spends less, finishes sooner, and lands on the right action the first time.\n\nBowmark is **free to use** — no signup and no API key to manage. It's plain request/response REST — no websockets, no server-side browser, no async jobs. Your agent runs the cheatsheet in its own browser; Bowmark only supplies the path.\n\n**Methods.** `bowmark.ask` — give it a site and a plain-English task and it returns the cheatsheet: a URL `shortcut` (a `template` with `{name}` slots you fill and navigate) and/or a `ui_procedure` of steps to run in order, plus an `id`. Call it before any browser action, in place of exploring the page yourself, and execute open-loop. `bowmark.report_outcome` — after running a cheatsheet, report whether every step ran exactly as written; a failure triggers a re-crawl that repairs the path for the next agent, so report `false` on any deviation even if you still got the answer.\n\n**Syntax & edge cases.** `site` is a registrable domain, optionally with a product surface (`google.com`, `docs.stripe.com`, `google.com/maps`); `task` is intent, never a URL. Request the signed-in view with `variants: { auth_state: \"logged_in\", role: \"owner\" }` (also `locale` / `region` / `currency`). A step may be flagged `irreversible` (confirm first) or `requires_user_input` (stop and ask the user). Non-`ok` statuses: `no_useful_data` / `site_not_supported` → browse manually; `ambiguous_scope` → retry with `scopeHint`; `rate_limited` → back off until `error.retry_after` (only new cheatsheet synthesis is capped). Skip it for localhost, RFC1918 IPs, and open-ended search with no destination.", + "tagline": "Live data from real websites: prices, stock, fares and quotes, including what only appears after you operate the page", + "description": "Bowmark gives agents a **typed function library for the live web**, and runs the script they write against it on the real sites. `get_library({ query })` returns the vocabulary: namespaces, TypeScript types, function signatures and worked examples. `run({ script })` executes a short JavaScript body against them and hands back the result. Ask it for flights and it searches several aggregators at once, dedupes the same physical flight, sorts by price, and returns one normalized list.\n\n**The data it reaches is the kind an index cannot hold.** A fare that only exists after the site's own live poll completes. A price that appears once dates are entered. Stock for one postcode. A quote behind a form. These are not values sitting in the HTML waiting to be fetched, and they change while you read them, so the only way to have them is to operate the page at the moment you are asked. Bowmark does that and hands back structured JSON. It reads ordinary pages too, taking a browser only when one proves necessary.\n\nIt's plain request/response REST: no websockets, no async jobs, and no browser on your side. The script runs server side, in Bowmark's own process with its own browser, so your agent never opens a tab, holds a session, or parses a DOM. A capability like `bowmark.flights.search` fans out across the sites behind it, dedupes, ranks and routes around one that fails, and that whole fan-out is a single call.\n\n**Methods.** `bowmark.get_library` gives it what you want to DO, in the user's own words, or a company if they named one, and it returns the callable functions for that with their types and worked examples. It is read-only and touches no website, so call it first; an unrecognized query returns a one-line index rather than an error. `bowmark.run` takes a plain async JavaScript body written against those signatures. `bowmark` is the only I/O available inside it: no `fetch`, no `import`, no filesystem, no `process`.\n\n**What it covers today.** Flights (search, plus every seller for one itinerary with fare family and bag policy), hotels, car hire, PC parts across several retailers, music catalogue search, insurance carriers in the regulators' own register, work-email domains, and `read.page` / `read.pages` for any page as markdown, text or HTML. Individual sites are callable directly at `bowmark.providers.*` when you want one specific site rather than the fan-out.\n\n**Syntax & edge cases.** Check `status` before `ok`. `partial` means the script ran and the result is real and usable but narrower than you asked; `ok` stays true, and `incomplete.summary` names what never answered. `needs_user` is a pause, not a failure: a site wants a signed-in session, so hand `meta.handoff.url` to the user, wait, then re-send the identical script. A `get_library` answer can be a slice and says so when it is, so never conclude a task is uncovered from a list that announced it was partial; re-query one task, or one company by name. Prefer a capability over `bowmark.providers.*` unless you want one specific site. Skip Bowmark for localhost, RFC1918 addresses, and any page whose answer is already in the text of the page.", "categories": [ "web" ], "primaryCategory": "web", "keywords": [ + "live-data", + "prices", + "availability", + "stock", + "fares", + "quotes", + "web", "browser", - "navigation", - "recipes", - "cheatsheets", - "websites", - "playwright", - "puppeteer", - "computer-use", - "scraping" + "booking", + "computer-use" ], - "version": "0.1.0", + "version": "1.0.1", "vendor": "Bowmark AI", "vendorUrl": "https://bowmark.ai", "license": "Proprietary", @@ -4326,25 +4327,35 @@ export const apps: App[] = [ "homepage": "https://bowmark.ai", "methods": [ { - "name": "bowmark.ask", - "summary": "Give it a site and a plain-English task (e.g. 'find Apple's latest 10-K') and it returns a ready-to-run cheatsheet — a URL shortcut to fill and navigate and/or a short ui_procedure of steps. Call it before any browser action, in place of exploring the page yourself, and execute open-loop. status=ok returns an id; no_useful_data/site_not_supported → browse manually; ambiguous_scope → retry with scopeHint; rate_limited → back off. Pass variants:{auth_state:'logged_in'} for signed-in surfaces. Intent, not a URL.", + "name": "bowmark.get_library", + "summary": "START HERE. Read-only, and it touches no website. Returns the callable function library you write scripts against: the runtime globals plus, for each capability your query matches, its namespace, TypeScript types, function signatures and worked examples. Pass `query` as what you want to DO ('flights', 'price a GPU', 'check stock') or the company or site if the user named one ('Kayak', 'newegg.com'); a phrase in the user's own words is fine. You get what you asked about and nothing else. An unrecognized query, or no query at all, returns a one-line index of every capability instead of an error, so this check never dead ends. Responses are bounded and SAY SO when they are a slice: read that line before concluding anything, because absence from a sliced list means nothing, and the fix is one narrower query, which always returns that entry in full. Two tiers come back: capabilities like `bowmark.flights.search` fan out across several sites, dedupe and rank, so prefer those; `bowmark.providers.kayak.search` is one named site in its native shape.", "example": null, "gated": null }, { - "name": "bowmark.report_outcome", - "summary": "After running a cheatsheet from ask, report whether every step ran exactly as written (envelope_id + success, optional evidence). success=true only if it ran clean — no retries, no raw-JS fallback, no extra clicks; false on any deviation, even if you still got the answer. Honest results trigger a re-crawl that keeps the cheatsheet fresh.", - "example": null, - "gated": null - }, - { - "name": "bowmark.help", - "summary": "Discovery: every method with params, kind, and latency class.", + "name": "bowmark.run", + "summary": "Executes your script on the real websites and returns what came back. Call bowmark.get_library FIRST: it gives the exact function names, argument shapes and return types. The language is plain async JavaScript. `bowmark` is a ready global with no import, every call is awaited, and you get real control flow: if, loops, array methods, and Promise.all for fan-out. `return` a value to get it back, `log(...)` for progress lines. `bowmark` is the ONLY I/O: no fetch, no process, no filesystem, no import or require. Write a plain async body, not a wrapping function, and keep it small and deterministic, because it runs in a hard sandbox with CPU, memory and wall-clock ceilings. `script` is the only argument; there is no site argument. Returns {ok, status, result, logs, error, ms}. CHECK `status` BEFORE `ok`: ok | partial (the script ran and the result is real and usable but narrower than asked, ok stays true, and incomplete.summary names what never answered; this is not a failure. Check incomplete.failures[].fixable: true means YOUR argument was rejected, not the site, so fix it and run again; otherwise re-running returns the same thing) | error | needs_user (a site wants a signed-in session, so the run PAUSED: hand meta.handoff.url to the user, wait, then re-send the identical script). Bowmark drives the sites server side, so your agent never opens a browser.", "example": null, "gated": null } ], "changelog": [ + { + "version": "1.0.1", + "notes": [ + "Route the app's two methods through /v1/pilot-app/*, so calls arriving from the Pilot Protocol app store are attributable. No change to method names, parameters, or behaviour.", + "Rewrite the product demo to fit a small context window: three worked examples covering three distinct shapes rather than five with overlap, and tighter gotchas.", + "Document incomplete.failures[].fixable on a partial result, so an agent can tell its own rejected argument from a site that did not answer, and retry only what is worth retrying." + ] + }, + { + "version": "1.0.0", + "notes": [ + "Replaces bowmark.ask and bowmark.report_outcome with bowmark.get_library and bowmark.run. Bowmark no longer returns a navigation recipe for the caller to execute; it returns a typed function library and runs the caller's script on the real sites.", + "Execution moved server side. The calling agent no longer needs a browser of its own.", + "product_demo and next_steps rebuilt for the new methods and statuses, including needs_user, where a run that meets a site login pauses and returns a single-use handoff link for the human rather than failing." + ] + }, { "version": "0.1.0", "notes": [ @@ -4362,22 +4373,22 @@ export const apps: App[] = [ "bundles": [ { "platform": "darwin-arm64", - "bytes": 5374254 + "bytes": 5376801 }, { "platform": "darwin-amd64", - "bytes": 4968650 + "bytes": 4971005 }, { "platform": "linux-arm64", - "bytes": 5019350 + "bytes": 5021729 }, { "platform": "linux-amd64", - "bytes": 4613746 + "bytes": 4615933 } ], - "installedBytes": 9105408, + "installedBytes": 9113971, "depends": [], "protection": "shareable", "featured": false, @@ -4402,57 +4413,42 @@ export const apps: App[] = [ "productDemo": { "skill": "io.pilot.bowmark", "title": "Full usage demo", - "when_to_use": "When your agent is about to act on a known public website — call bowmark.ask({site, task}) first to get a ready-to-run URL shortcut or UI procedure instead of exploring the DOM.", - "metered": true, + "when_to_use": "When the answer only exists on a live website right now, after someone operates it: a price, stock, a fare, a quote, a form.", + "metered": false, "quickstart": { - "goal": "Get a navigation cheatsheet for a site + task", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"sec.gov\",\"task\":\"find Apple's latest 10-K filing\"}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"shortcut\":{\"template\":\"https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK={ticker}&type=10-K\"}}" + "goal": "See whether a function already covers the task", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.get_library '{\"query\":\"flights\"}'", + "expect": "markdown: the bowmark.flights capability, with its types, signatures and examples", + "note": "Always call this first. An unrecognized query returns a one-line index, not an error." }, "examples": [ { - "title": "Ask for a UI procedure on a product surface", - "goal": "Scope with a path to skip the ambiguous_scope round-trip", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"google.com/maps\",\"task\":\"get directions from SFO to downtown\"}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"ui_procedure\":{\"steps\":[...]}}" + "title": "Search flights across several aggregators", + "goal": "One capability fans out, dedupes the same flight, sorts by price", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const { flights, warnings } = await bowmark.flights.search({ from: \\\"YYZ\\\", to: \\\"LHR\\\", depart: \\\"2026-09-14\\\" }); return { best: flights[0], warnings };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"best\":{\"price\":412,\"site\":\"google_flights\"},\"warnings\":[]}}", + "note": "These prices exist only after each site's own live poll, so no static fetch reaches them. `warnings` names a site dropped." }, { - "title": "Request the signed-in surface", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.ask '{\"site\":\"github.com\",\"task\":\"create a new private repo\",\"variants\":{\"auth_state\":\"logged_in\",\"role\":\"owner\"}}'", - "expect": "{\"status\":\"ok\",\"id\":\"...\",\"ui_procedure\":{\"steps\":[...]},\"variants_assumed\":{...}}" + "title": "Read any page as markdown", + "goal": "Cheap GET first, a browser only if the page proves it needs one", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const p = await bowmark.read.page(\\\"https://example.com/pricing\\\"); return { title: p.title, servedBy: p.servedBy, content: p.content };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"title\":\"Pricing\",\"servedBy\":\"fetch\",\"content\":\"# Pricing\\n...\"}}", + "note": "`servedBy` names the cheapest rung that worked, and `wall` names the bot wall when there is one." }, { - "title": "Report the outcome to keep cheatsheets fresh", - "goal": "success=true only if every step ran clean — no retries or extra clicks", - "command": "pilotctl appstore call io.pilot.bowmark bowmark.report_outcome '{\"envelope_id\":\"\",\"success\":true}'", - "expect": "{\"id\":\"...\"}" + "title": "Drill into one site instead of the fan-out", + "goal": "One named site, two dependent calls, one script", + "command": "pilotctl appstore call io.pilot.bowmark bowmark.run '{\"script\":\"const k = bowmark.providers.kayak; const [best] = await k.search({ from: \\\"SFO\\\", to: \\\"JFK\\\", depart: \\\"2026-09-15\\\" }); const sellers = await k.getBookingOptions(best); return { price: best.price, sellers: sellers.length };\"}'", + "expect": "{\"ok\":true,\"status\":\"ok\",\"result\":{\"price\":198,\"sellers\":7}}", + "note": "A provider returns that site's native shape, so you give up the cross-site dedupe and the routing around failures." } ], - "cost": { - "unit": "requests (managed key, currently unmetered)", - "free_budget": "managed — no per-user charge today", - "hard_cap_usd": 0, - "operations": [ - { - "op": "bowmark.ask", - "price": "free", - "note": "the nav-recipe call (POST /v1/ask); managed key, no per-op meter currently" - }, - { - "op": "bowmark.report_outcome", - "price": "free", - "note": "feedback that triggers a re-crawl; no charge" - } - ], - "worked_total": "Managed key with quota 0 — there is no per-user dollar charge today; both methods are free to call." - }, "gotchas": [ - "Put intent in `task`, never a URL — 'find Apple's latest 10-K', not a link.", - "Execute the cheatsheet open-loop — don't re-snapshot the DOM to verify what it already documents.", - "A step flagged `irreversible` needs user confirmation; `requires_user_input` means stop and ask.", - "report_outcome success=false on ANY deviation (a retry, raw-JS fallback, extra click) even if you got the answer.", - "Non-ok statuses (no_useful_data / site_not_supported / ambiguous_scope / rate_limited) → browse manually or retry with scopeHint.", - "Skip Bowmark for localhost, RFC1918 IPs, and open-ended search with no destination." + "Call `get_library` before `run`, always. A script uses names only it supplies, and it touches no website.", + "Check `status` before `ok`. `partial` is a real result, narrower than asked; `incomplete.summary` names what never answered, and `failures[].fixable` true means YOUR argument was wrong, so fix and re-run.", + "`needs_user` is a login pause, not a failure: give the user `meta.handoff.url`, wait, then re-send the script.", + "`bowmark` is the only I/O in a script. No `fetch`, `import`, filesystem or `process`. Write a plain async body and `return`." ], "next": [ "io.pilot.bowmark bowmark.help '{}'" diff --git a/src/pages/app-store.astro b/src/pages/app-store.astro index c3af0ef..ac4ccaf 100644 --- a/src/pages/app-store.astro +++ b/src/pages/app-store.astro @@ -17,7 +17,7 @@ const canonicalUrl = 'https://pilotprotocol.network/app-store'; const featured = featuredApps(); const hero = featured[0]; const sideFeatured = featured.slice(1, 4); -const freshPinned = ['io.pilot.primitive', 'io.pilot.didit', 'io.pilot.agentphone', 'io.pilot.orthogonal', 'io.pilot.bowmark', 'io.pilot.miren', 'io.pilot.smol', 'io.pilot.wallet', 'io.pilot.slipstream']; +const freshPinned = ['io.pilot.bowmark', 'io.pilot.primitive', 'io.pilot.didit', 'io.pilot.agentphone', 'io.pilot.orthogonal', 'io.pilot.miren', 'io.pilot.smol', 'io.pilot.wallet', 'io.pilot.slipstream']; const freshExclude = new Set(['io.pilot.postgres', 'io.pilot.docker']); const fresh = [ ...freshPinned.map((id) => apps.find((a) => a.id === id)).filter(Boolean), diff --git a/src/pages/plain/app-store.astro b/src/pages/plain/app-store.astro index dc19127..a541370 100644 --- a/src/pages/plain/app-store.astro +++ b/src/pages/plain/app-store.astro @@ -1,7 +1,7 @@ --- // Auto-generated by scripts/regen-plain.mjs. Edit the marketing source and re-run. // plain-source: src/pages/app-store.astro -// plain-source-sha256: 5c3087953af2e13110f8ac610b49694b35cf295b7beea14178cb4d23286e3685 +// plain-source-sha256: 5cf4c4c205f936965e2b6484415eb9358359b902286c3176eaa0cdcbac5b5803 import PlainLayout from '../../layouts/PlainLayout.astro'; ---