openaso

Changelog

Unreleased

ADDED

  • Web: account dashboard at openaso.ai — sign in with email + password (Better Auth; sessions on the site, keys still header-auth on the API), see today's tool-call usage against the daily quota, and mint or revoke API keys self-serve (up to 5 active). Accounts link by email, so a key issued via the old POST /signup and a dashboard login with the same address are one account; lost-key recovery is now self-serve instead of email-support. New auth_* tables (migration 20260728000000_web_auth.sql).

REMOVED

  • Hosted: anonymous POST /signup (email in, key out) is retired — API keys now require an account, created at openaso.ai/login and minted in the dashboard. The endpoint answers 410 Gone with that pointer (CORS kept for cached clients). Account-creation abuse caps (3/IP/day + a fleet-wide daily cap) move to the dashboard path unchanged; OPENASO_SIGNUP_DAILY_CAP is no longer read, and the new OPENASO_SITE_URL (default https://openaso.ai) feeds the landing page and the 410 pointer.
  • revenue command + MCP tool: order-of-magnitude revenue estimates modeled from the app's public top-grossing chart rank — the one monetization signal Apple publishes for arbitrary apps. Daily/monthly gross-consumer-spend bands in USD (low/mid/high; developer proceeds ~70-85% of gross), from a power-law fit ($4.0M x rank^-0.95 on the US overall chart, calibrated 2026-07 against published rank-revenue datapoints: ChatGPT/ TikTok/MONOPOLY GO! press figures, Cal AI founder-public numbers, Distimo 2013 thresholds, the DazeEnd/Overcast 2015 curve), scaled per storefront (iOS spend share vs US) and per genre chart (spend share through the power law). Falls back from the overall chart to the app's genre grossing chart; apps on no consulted top 100 get upperBoundDaily only. Bands carry a 2.5x multiplier and a note stating the model's limits — estimates, never Apple data.
  • charts now reports topGrossing alongside top-free/top-paid, served by the legacy itunes.apple.com RSS feed (ITunesClient.topGrossing, per- genre capable) — the only public source of grossing ranks; the modern marketing-tools feed deliberately omits grossing. Additive JSON change: per-storefront results gain a topGrossing field.
  • Hosted: the daily managed run now archives the overall top-grossing top-100 per storefront into the new chart_snapshots table (migration 20260724000000_chart_snapshots.sql) for every tracked storefront plus US plus OPENASO_GROSSING_COUNTRIES — insurance against the legacy feed being retired, and the future calibration series for the revenue model. The run summary gains a charts section with its own failure list.

CHANGED

  • **Real Apple Search Ads popularity for volume (and audit).** The headline demand number is now popularity (0-100): Apple's real Search Ads Search Popularity when source is "apple", served server-side by the hosted service through a single Apple Ads account (Apple exposes this only via the Apple Ads console's internal endpoint, not the official API). Apple floors low-demand keywords at 5 and returns nothing below its threshold or in storefronts outside Apple Ads coverage; there — and in local mode, which carries no credentials — source is "proxy" and popularity falls back to the independent estimate (the former blended score), with floored and applePopularity exposed for transparency. Breaking JSON contract: the volume per-storefront volumeScore/popularityScore fields are replaced by popularity/source/applePopularity/floored/estimate; audit candidates gain popularity/popularitySource/applePopularity/floored/ estimate in place of volumeScore. The popularity fetch never fails a tool call — an expired session or uncovered storefront simply degrades to the estimate. Configured via OPENASO_APPLE_ADS_COOKIE + OPENASO_APPLE_ADS_ADAM_ID (see docs/HOSTED.md); cached per (storefront, keyword) for 24h in the shared L2 cache.

ADDED

  • audit command + MCP tool: one-call ASO research bundle per storefront — the app's current listing (title + web-scraped subtitle), its visible word pool, and scored keyword candidates (supplied phrases plus autocomplete discoveries seeded from the pool). Each candidate is scored from a single shared search: demand (popularity, the volume tool's model — real Apple Search Ads popularity where available, else the estimate), competition, incumbent strength (medianTopRatings) and the app's current rank — where separate volume + rank calls would search twice. Emits progress notifications; supplied phrases never compete with discoveries for the limit slots.
  • optimize_metadata MCP prompt (the server now declares the prompts capability, served over stdio and hosted HTTP alike): a packaged metadata-optimization playbook the client's model executes with the tools — audit, deepen with reviews/competitors, choose targets, draft against coverage with {phrase, value: popularity} weights, regression-check that no currently-ranking phrase is silently dropped, then verify with rank and set up daily tracking. Prompt arguments: appId (required), countries, phrases (comma-separated).
  • coverage word-value aggregation: target phrases accept {phrase, value} weights (CLI: "photo editor=87"), and wordValues sums each word's search value across every phrase it can combine into ("photo" serves "photo editor", "photo maker" and "take photo" at once). A phrase's value is credited to each of its words — totals are not additive across words — so unlockValue (absent words whose addition alone covers new phrases) and atRiskValue (a true drop-counterfactual: covered phrases that would actually break, respecting surviving plural variants) are the marginal numbers to budget the 30/30/100 characters by.
  • coverage keyword-field waste accounting (keywordFieldWaste): commas count toward the 100-character limit; spaces around commas, duplicates of title/subtitle words, in-field repeats, stopwords and the separators their removal frees are reported as reclaimable — never more than the field contains. The comma/space convention is now also taught in the server instructions and tool description.
  • coverage command + MCP tool: metadata keyword coverage, pure local computation. Apple indexes the title, subtitle and hidden keyword field per locale as one combined word pool, so an app can rank for phrases assembled from words across fields — a model agents consuming the server did not reliably know. coverage operationalizes it: combined pool with strongest-field attribution, cross-field duplicates (reclaimable characters), per-field character budgets (30/30/100), and per-phrase coverage (exact-in-field / cross-field / missing words, with plural and stopword handling). The server instructions, the rank/app tool descriptions and a note in every coverage result now teach the combination model explicitly, so it survives hosts that drop server-level instructions.

0.3.0 (2026-07-22)

BREAKING

  • CLI --json output now always uses the canonical multi-storefront shape — the same shape the MCP tools return. Previously each command emitted a different, flatter JSON shape when a single --country was given, so adding a second country silently changed the schema. Single-storefront *text* output is unchanged.
  • competitors --json renames count to returned and adds total (the API's result count, capped at 200), matching the MCP tool. The search always requests the full 200 results and slices to limit, so total is meaningful regardless of the limit.
  • rank --json with --save emits flat historyFile/prev/change fields (matching the MCP tool) instead of a nested history object.
  • Exit codes are now meaningful: 0 success, 1 usage/validation error, 2 iTunes API/network failure, 3 app not found in any requested storefront (previously not-found exited 1, indistinguishable from usage errors).
  • Relative --history-file/--config paths resolve against $OPENASO_HOME when set (else the current directory, as before) and results echo resolved absolute paths.
  • The default politeness throttle between API requests is now 1000ms (was 250ms) to match the iTunes API's real rate limits.

ADDED

  • suggest command + MCP tool: App Store search-autocomplete suggestions (keyword discovery) via the public MZSearchHints endpoint.
  • reviews command + MCP tool: recent customer reviews per storefront from the public RSS feed — rating distribution plus review texts.
  • charts command + MCP tool: top-free / top-paid chart positions (top 100) from the public marketing-tools feed.
  • history command + MCP tool: read the JSONL history back — per (keyword, app, storefront) series with current/previous/best/worst rank, filterable by keyword/app/country/date. No network.
  • app --full (CLI) / full: true (MCP): return the complete iTunes lookup record, including the description — previously unreachable over MCP.
  • App summaries include languages (localization coverage signal).
  • --version; per-command --help; with --json, errors are emitted as JSON on stdout ({"error": {"message": ...}}).
  • MCP: tool annotations (readOnlyHint etc.) and titles, server instructions, progress notifications for track when the client sends a progressToken, cancellation support, full output schemas for competitors rows and app summaries, JSON-Schema constraints (min/max/default/pattern), unknown argument keys rejected, unknown tools rejected as protocol errors.
  • HTTP client: per-attempt timeout (10s default), authoritative Retry-After support (capped at 60s, shorter or longer than the backoff), retry on malformed 200 bodies, junk chart/review feed bodies rejected instead of read as empty, response-body snippets and cause chaining in errors, race-free shared throttle, and AbortSignal support end to end — cancellation reaches every MCP tool's outbound requests and interrupts retry backoff and throttle waits promptly.
  • track fetches each keyword x storefront search once for all configured apps (was once per app) and indexes history in one read per run.
  • History appends recover from partial trailing lines (crash/disk-full) instead of corrupting the next record.
  • Packaging: files allowlist, prepare/prepublishOnly build hooks, keywords/author/repository metadata, LICENSE file, CI workflow (Node 18/20/22), .openaso.example.json, type-checked tests (tsc -p tsconfig.test.json).

0.2.0

  • Initial public shape: rank, competitors, volume, app, track commands; MCP server over stdio; JSONL rank history with per-storefront change tracking; hermetic test suite.