Initial import: live state on api.qbirr.com (server v0.6.3)

This commit is contained in:
2026-05-26 16:06:29 +02:00
commit 7ba4cb4a31
38 changed files with 5242 additions and 0 deletions
+236
View File
@@ -0,0 +1,236 @@
# Changelog
All notable changes to wpide-server will be documented in this file. Format
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning
is [SemVer](https://semver.org/spec/v2.0.0.html).
## [0.6.3] — 2026-05-26
### Changed
- **Request logging back to `!isDev` (silent in prod).** Was temporarily
forced on to debug the live browser-direct tool round-trip with the
plugin on Hostinger (see plugin 56.95/56.96). Now that browser-direct
is verified end-to-end (744 ms tool round-trip from `mersaai.diretenders.com`),
prod logs go quiet again — every 30s `/v1/health` from Docker's
healthcheck no longer fills the log file.
### Kept
- The targeted diagnostic lines added in 0.6.2 stay in for future
support work:
- `browser-direct: awaiting tool result from browser` (orchestrator)
- `browser-direct: tool result TIMED OUT after 90s` (orchestrator)
- `tool_result POST received … matched:true|false` (runs route)
These are low-volume and useful for diagnosing future host quirks.
## [0.6.2] — 2026-05-26
### Fixed
- **`z.coerce.boolean()` read every boolean env var as `true`.** `Boolean("false")`
is `true` in JS, so `ALLOW_INSECURE_TLS=false`, `REQUIRE_LICENSE=false`, and
`FREE_TIER_ACTIVE=false` were all silently `true`. Replaced with a `boolish()`
parser that treats `0/false/no/off` as false and `1/true/yes/on` as true.
This bit on the first real VPS deploy: TLS verification stayed disabled and
license gating would have switched on unexpectedly.
### Added
- **Production deploy artifacts (`deploy/`).** `vps-bootstrap.sh` (Ubuntu prep:
update, ufw, base tools) and `docker-compose.coolify.yml` (runs the container
behind Coolify's existing Traefik proxy on the external `coolify` network, with
Let's Encrypt labels). First live deploy: Contabo VPS → Coolify/Traefik →
`https://api.qbirr.com`.
## [0.6.1] — 2026-05-26
### Fixed
- **SSE stream endpoint now sends CORS headers.** The `/v1/runs/:id/stream`
reply is hijacked (raw), so the cors plugin didn't run on it. Added
`access-control-allow-origin` (echoes request origin) so the plugin's
browser-direct `EventSource` can open the stream cross-origin (browser
on the WP site → server on another domain). Without this the browser
blocked the stream.
## [0.6.0] — 2026-05-25 — Memory, teams, browser-direct tools
### Added
- **Server-side conversation memory (`orchestrator/memory.ts`).** Persists
each run's user goal + final answer per `session_id`; recalls the last
~12 turns and merges (de-duplicated) into context on the next run. Runs
now have continuity even when the plugin sends little history. (DB
migration v3: `conversation_turns`.)
- **Multi-tenant teams (`accounts/teams.ts`, `routes/teams.ts`).** Orgs +
members + roles; `POST/GET /v1/teams`, member add/remove. A member
**inherits the org owner's subscription tier** (team owner pays once,
seats share the tier) — wired into `resolveAccess`. (Migration v3:
`orgs`, `org_members`, `subscriptions.org_id`.)
- **Browser-direct tool execution (cap-immune transport, server side).**
`browser_tools: true` on a run makes the loop emit `tool_call` SSE
events and await `POST /v1/runs/:id/tool_result` from the browser,
instead of calling back into the plugin. Removes the long-lived request
from the WP host entirely → works on any shared host. Relay mode
(plugin callback) remains the default; this is additive. Proven
end-to-end via test (tool_call emitted → result posted → run completed
using the posted data).
### Notes
- The browser-direct **plugin JS** (browser opens EventSource + runs the
tool via admin-ajax + POSTs the result) is the remaining client-side
step; the server fully supports it now.
## [0.5.0] — 2026-05-25 — SaaS platform foundation
### Added
- **Accounts & auth.** Email+password (scrypt, no native deps), Google +
GitHub OAuth (authorization-code flow), session JWT in an httpOnly
cookie. `/v1/auth/{register,login,logout,me,set-password}` and
`/v1/auth/oauth/:provider/{start,callback}`.
- **Licensing & subscriptions (no credits).** Each account gets a license
key + a subscription row (tier `basic|pro|max`, status). The run path
resolves the license → tier and gates on an active subscription
(`REQUIRE_LICENSE`, default off in dev so local testing needs no
account; `DEV_DEFAULT_TIER` applies when off).
- **Model routing by tier** (`routing/policy.ts`): tier sets the model
ceiling — basic→flash, pro→+thinking, max→+pro-max — combined with
mode/complexity. Explicit model picks are honored only within the tier,
else downgraded. DeepSeek `thinking` param wired through.
- **Stripe billing** (`fetch`, no SDK): `/v1/billing/{checkout,portal,webhook}`
with manual webhook signature verification; subscription state mirrored
into the DB. Disabled cleanly when keys absent.
- **Dashboard** at `/app`: signup/login (email + Google + GitHub),
license key with copy, plan + tier picker + manage-billing. Single
inline HTML page, no build step.
- **DB migration v2:** users, oauth_identities, licenses, subscriptions,
sites (site registry); runs gain user_id/tier/model.
- **Deploy:** `SETUP.md` operator checklist, `docker-compose.prod.yml` +
`Caddyfile.prod` for a plain VPS (Coolify uses the Dockerfile directly).
SQLite on a persistent volume for v1; Postgres is the documented
scale-up path.
### Notes
- Credits/metering intentionally deferred — access is subscription-gated,
tier gates models. Multi-tenant teams, server-side memory, and the
browser-direct SSE transport remain as later sub-projects.
## [0.4.1] — 2026-05-25
### Fixed
- **Reasoning rendered one token per line in the chat.** The agentic loop
was emitting a `thinking` event per reasoning token; the plugin relays
each as a `thought` event, and the browser appends one list item per
thought — so each reasoning token landed on its own line. Now the loop
accumulates `reasoning_content` per step and emits a single `thinking`
event when the step's LLM call finishes (matches the local
orchestrator's one-thought-per-step model). Content tokens still stream
individually into the live bubble.
## [0.4.0] — 2026-05-25
### Added
- **SSE token streaming.** `GET /v1/runs/:run_id/stream` streams a run's
live events (`token`, `thinking`, `tool_call`, `tool_result`, `status`,
`done`, `error`, `end`) with `?since=<seq>` resume. Backed by a per-run
event buffer in the registry (`addEvent`).
- `OpenAIClient.chatStream()` — streaming chat completions over fetch:
parses SSE token deltas, accumulates `content` / `reasoning_content` /
`tool_calls`, returns the same shape as `chat()`. Connect-only retry
(safe to retry before any token; never mid-stream).
- The agentic loop now uses `chatStream` and emits token/tool events into
the run's buffer as they happen.
### Fixed
- **DeepSeek v4 thinking mode HTTP 400** — the loop now echoes the
assistant message's `reasoning_content` back on the next turn after a
tool call, as DeepSeek v4 requires. (`chat()` and `chatStream()` both
capture the field; the loop re-sends it.)
## [0.3.0] — 2026-05-25
### Added
- **Async run model — removes the synchronous timeout ceiling.**
- `POST /v1/runs/start` registers a run, kicks off the orchestrator in
the background, and returns `{ run_id, session_id }` immediately.
- `GET /v1/runs/:run_id/status` reports live progress
(`status`, `steps_done`, `tools_used`, `elapsed_ms`, `partial_content`)
and the full PHP-shape `response` once finished.
- In-memory run registry (`src/orchestrator/registry.ts`) with a 30-min
TTL for finished runs. `process_request` shares its step/tool arrays
with the registry so status reflects progress as it happens.
- Old synchronous `POST /v1/runs` retained for non-browser callers.
- **Built-in server-side `wait` tool** — lets agents pace long-running
diagnostics without a DB `SLEEP()` (SQLite has none). Handled in-loop,
never calls back to the plugin. Max 30s per call.
- LLM chat retry hardened: 6 attempts, 20s per-attempt timeout (fails
fast on a hung VPN connection and retries), abort/timeout treated as
retryable. Step cap raised 25 → 40.
- `AGENT_STEP tool_call → <name>` logged at info level for live flow
visibility.
## [0.2.1] — 2026-05-25
### Added
- xAI (Grok) provider client — OpenAI-compatible base URL `https://api.x.ai/v1`.
- DeepSeek provider client — base URL `https://api.deepseek.com/v1`.
- Provider auto-routing in `pickProvider()`: explicit override → model-name
prefix (`deepseek*`, `grok*`, `gpt-*`) → first configured key
(deepseek → xai → openai). `defaultModelFor()` picks a sane model per
provider.
- `ALLOW_INSECURE_TLS` (default on in dev) — skips outbound TLS
verification for machines behind a VPN/MITM whose root CA Node doesn't
trust. Set off in prod.
- Tool-manifest sanitizer (`src/tools/manifest.ts`) — coerces PHP's empty
`[]` to `{}` where objects are required and strips null values, so
strict validators (DeepSeek) stop rejecting the plugin's 95 tool
schemas with HTTP 400.
- LLM chat calls now retry up to 3× with backoff on network errors
(connect timeout, DNS, TLS) and 5xx/429 — rides through flaky-VPN
blips. 4xx are treated as real errors and not retried.
- Detailed logging on tool-exec callback failures (URL, status, headers,
body length) to diagnose cross-machine / rewrite issues.
### Changed
- Server binds to `0.0.0.0` by default (was `127.0.0.1`) so other LAN
machines can reach it. Default port `3017`.
- Default model is now `deepseek-chat`.
## [0.2.0] — 2026-05-21
### Added
- `GET /` root route returns an API directory (name, version, endpoint
hints) — friendlier than the previous 404 when poking around in a
browser.
- `POST /v1/runs` — main orchestrator entrypoint. Accepts
`{ goal, context, options, tools_manifest, callback_url, callback_secret, license_key, site_url }`
and returns the exact response shape PHP's `wp_ide_process_agentic()`
uses: `{ success, content, tool_results, execution: { run_id,
session_id, mode, steps, status_messages, ... }, approval_payload }`.
- `GET /v1/runs/:run_id` — fetch a stored run record.
- Greeting + simple paths fully working. Greetings need no LLM call;
simple invokes OpenAI chat/completions (default `gpt-4o-mini`).
Agentic path is a placeholder that returns a "step 5" message.
- Orchestrator router (`src/orchestrator/router.ts`) — port of
`WP_IDE_AI_Router::classify`; cheap regex/keyword heuristics that pick
greeting / simple / agentic without an LLM call.
- OpenAI client over fetch (`src/providers/openai.ts`) — no SDK
dependency. Provider router (`src/providers/index.ts`) dispatches by
model name; Anthropic / xAI plug in here in later steps.
- Runs table CRUD (`src/db/runs.ts`) — persists every run with status
transitions so a future status endpoint can answer "what happened".
### Notes
- `OPENAI_API_KEY` is read from `.env` on boot. Without it the simple
path returns a clear actionable error in the standard response shape;
the greeting path still works because it doesn't call any LLM.
## [0.1.0] — 2026-05-21
### Added
- Initial Fastify scaffold (Node 20, TypeScript, ESM).
- `GET /v1/health` returns name, version, uptime, Node version, timestamp.
- SQLite via `better-sqlite3` as the dev database; WAL mode + foreign keys
enabled. Postgres URL recognised for later Coolify deploy but not yet
wired.
- Initial schema: `schema_version`, `runs` tables (more added per
milestone).
- Zod-validated env loader, pino logger, CORS, graceful shutdown.
- `start-dev.bat` for one-command Windows dev startup.
- `Dockerfile` for future Coolify deployment (not used locally).
- Optional `Caddyfile` for local TLS via `wpide.local`.