Guide

From first install to the local API — the full documentation.

First run

the /init wizard

No terminal skills? Windows: double-click launch.bat — Linux/macOS: ./launch.sh (first time only: chmod +x launch.sh). It runs npm install on the very first launch, downloads the Chromium helper for Find my Account ID (~130 MB, one time), rebuilds the app whenever the sources changed, then starts it in production mode on http://127.0.0.1:8787 and opens your browser. The window shows compact [web] / [api] / [db] logs — press Q to stop it (the SQLite WAL is checkpointed on the way out).

  1. The /init wizard — with nothing configured yet, every app page redirects to a standalone first-run screen (no nav): paste your ScraperAPI key (optional), find your AccountId with Find my Account ID (temporary Chromium, manual login, nothing stored) — or press Try with the sample account to test the pipeline on [DE]Rebecca's public profile. Saving a key or an id leaves the wizard for good (skipping unlocks the app for the session and re-proposes it next session).
  2. Check — Find & run finds your AccountId and launches the check as soon as it is found; you can also Paste a known 24-hex id or reuse the saved one. The finder alone lives in Settings.
  3. Watch the live progress (SSE) and read the verdict; each run is kept in History.

What a check does

one request, five steps
  1. Fetch — exactly one HTTP request to https://api[-<platform>].warframe.com/cdn/getProfileViewingData.php?playerId=<id> (pc uses api.warframe.com; consoles use api-ps4|x-xb1|api-swi|api-mob|api-and). With a ScraperAPI key, a network-level failure triggers a single retry through the proxy (direct → scraperapi-fallback). HTTP error statuses are never retried.
  2. Parse — new ProfileParser(data, 'en', withItem=false).
  3. Validate — the 13 frozen checks (6 critical), run through a safe wrapper (an exception becomes a failed check) — full list and rules.
  4. Verdict — any critical fail → broken; only non-critical fails → warnings; else ok.
  5. Persist — summary + checks + request metadata in data/app.sqlite.

Scripts

npm run …
ScriptWhat it does
npm run dev API (node --watch + tsx) + web (Vite) together — GUI shows an offline banner if the API is down
npm run build typecheck all packages + build the web app into apps/web/dist
npm startserve API + built GUI on http://127.0.0.1:8787
npm test vitest — 58 tests (unit + API integration + web API client + offline live-fixture parity)
npm run typechecktsc on core, server and web
npm run lint / formatBiome check / auto-fix
npm run check:live one-shot terminal check against DE (1 request), refreshes the test fixture
npm run check:offline same pipeline on the saved fixture, no network
npm run db:checkpoint fold the SQLite WAL into data/app.sqlite so any viewer can read it (also run automatically on every launcher stop)

Configuration

.env is a bootstrap only

.env at the repo root is read once at startup; missing keys are seeded into the settings DB, and the GUI is the source of truth afterwards.

KeyMeaning
SCRAPERAPI_KEYoptional proxy key
WARFRAME_ACCOUNT_IDyour AccountId, used as the default in the GUI

Environment overrides: TENNOTOOLS_HOST (default 127.0.0.1), TENNOTOOLS_PORT (default 8787), TENNOTOOLS_DB_PATH (default data/app.sqlite).

Never upload the database. Run history lives in data/app.sqlite (SQLite, WAL mode). The WAL is checkpointed automatically on every clean stop (and on the next start after a crash), so the file is self-contained — npm run db:checkpoint remains available any time. The settings table holds your ScraperAPI key in clear text.

Local API

for integrations
MethodPathPurpose
GET/api/healthliveness
POST/api/runs start a check {platform, accountId, useProxy} → 202 {id}
GET/api/runs?limit&offsethistory list
GET/api/runs/:idfull run result
GET/api/runs/:id/events SSE: progress / done / error (replays history to late subscribers)
GET/PUT/api/settings settings view / update (key always returned masked)
POST/api/account-id/discover start the Playwright Get-ID job → 202 {jobId}
GET/api/account-id/discover/events?jobId= SSE status stream
POST/api/account-id/discover/cancel cancel the running job

All payloads are validated with zod schemas shared from @tennotools/profile-check-core.

Rules of engagement

hard-won
  • The only working endpoint is api[-<platform>].warframe.com/cdn/getProfileViewingData.php?playerId=; DE removed the old nickname endpoint — a nickname alone can no longer fetch a profile.
  • The profile must be viewable in game (Esc → Options → Social → profile viewing), otherwise DE answers 500 even for a valid id.
  • DE caches responses — the run page shows XpCacheExpiryDate; wait for expiry before retrying.
  • AccountIds are MongoDB ObjectIds: 24 hex chars whose first 4 bytes are a creation timestamp — that is what looksLikeAccountId checks (2012 → now + 1 year).
Do not spam the API. Excessive calls can temporarily ban your IP, which also blocks game login. One request per run is all this tool ever does (plus at most one ScraperAPI fallback).