diff --git a/CONNECTION-MAP.md b/CONNECTION-MAP.md index 4bb434f..2d1f49d 100644 --- a/CONNECTION-MAP.md +++ b/CONNECTION-MAP.md @@ -2,7 +2,7 @@ **Upstream version:** v0.51.118 **Plugin version:** 0.5.0 -**Total dependencies:** 30 (24 public API · 0 forced internal · 6 frontend) +**Total dependencies:** 33 (24 public API · 0 forced internal · 9 frontend) > **Auto-generated by `scripts/ast-connection-map.py`. Do not hand-edit.** > To change a justification, edit the `# CONNECTION:` comment above the @@ -26,9 +26,9 @@ | `routes/adwright.py:68` | `api.logger` | `log = api.logger("svrnty.routes.adwright")` | | `routes/adwright.py:69` | `api.register_route` | `api.register_route(` | | `routes/adwright.py:71` | `api.register_route` | `api.register_route(` | -| `routes/bte_proxy.py:89` | `api.logger` | `log = api.logger("svrnty.routes.bte_proxy")` | -| `routes/bte_proxy.py:90` | `api.register_route` | `api.register_route("/api/bte/proxy", "GET", _handle_proxy)` | -| `routes/bte_proxy.py:91` | `api.register_route` | `api.register_route("/api/bte/proxy", "POST", _handle_proxy)` | +| `routes/bte_proxy.py:90` | `api.logger` | `log = api.logger("svrnty.routes.bte_proxy")` | +| `routes/bte_proxy.py:91` | `api.register_route` | `api.register_route("/api/bte/proxy", "GET", _handle_proxy)` | +| `routes/bte_proxy.py:92` | `api.register_route` | `api.register_route("/api/bte/proxy", "POST", _handle_proxy)` | | `routes/transcribe.py:37` | `api.logger` | `log = api.logger("svrnty.routes.transcribe")` | | `routes/transcribe.py:38` | `api.register_route` | `api.register_route("/api/transcribe", "POST", _handle_transcribe)` | | `routes/transcribe.py:39` | `api.register_audio_attachment_processor` | `api.register_audio_attachment_processor(_transcribe_audio_attachments)` | @@ -54,9 +54,12 @@ _None. Plugin uses only the public API._ ✓ | File | Line | URL | |---|---|---| | `static/bte.js` | 329 | `/api/command/requestPhotoshoot` | -| `static/bte.js` | 368 | `/api/query/assetGrid` | -| `static/bte.js` | 482 | `/api/command/rateAsset` | -| `static/adwright.js` | 484 | `/api/adwright/provision-creds` | +| `static/bte.js` | 360 | `/api/query/assetDtos` | +| `static/bte.js` | 372 | `/api/assets/` | +| `static/bte.js` | 481 | `/api/command/rateAsset` | +| `static/adwright.js` | 168 | `/api/profile/switch` | +| `static/adwright.js` | 189 | `/api/profile/active` | +| `static/adwright.js` | 539 | `/api/adwright/provision-creds` | | `static/umbrella.js` | 41 | `/api/umbrella` | | `static/app.js` | 165 | `/api/vault/status` | diff --git a/routes/bte_proxy.py b/routes/bte_proxy.py index ee1dc5d..c5f47ad 100644 --- a/routes/bte_proxy.py +++ b/routes/bte_proxy.py @@ -77,11 +77,12 @@ _BRAND_ID_CACHE: dict = {} _ALLOWED_EXACT = frozenset({ "/api/command/requestPhotoshoot", "/api/command/rateAsset", - "/api/query/assetGrid", + "/api/query/assetGrid", # PRD §5.4 (not yet built on BTE — 404 until ships) + "/api/query/assetDtos", # works today — pre-PRD grid "/api/query/recipeStats", }) -# Pattern: /api/assets//(thumb|status). id-segment may not contain '/' or '?'. -_ALLOWED_PATTERN = re.compile(r"^/api/assets/[A-Za-z0-9_\-]+/(thumb|status)$") +# Pattern: /api/assets//(thumb|status|image). image = full bytes via /api/assets/{id}/image. +_ALLOWED_PATTERN = re.compile(r"^/api/assets/[A-Za-z0-9_\-]+/(thumb|status|image)$") def register(api): diff --git a/static/bte.js b/static/bte.js index e446ebb..e8a553a 100644 --- a/static/bte.js +++ b/static/bte.js @@ -351,44 +351,43 @@ } // ── Grid refresh + polling ─────────────────────────────────────────────── + // Uses /api/query/assetDtos (live BTE endpoint) instead of /api/query/assetGrid + // (PRD §5.4 spec — not yet implemented). Returns all brand-scoped assets so + // the grid shows every render CMO has produced via bte_image_generate. function _refreshGrid() { - if (!state.family) return; - const filters = { - brandId: state.brand, - recipeSlug: state.family, - lifecycle: ["approved", "generating", "evaluating"], - }; - if (state.runId) filters.runId = state.runId; - const body = { - filters: filters, - page: 1, - pageSize: 24, - sort: "-created_at", - }; - _proxyPost("/api/query/assetGrid", body) + // assetDtos is brand-scoped, not recipe-scoped. Always fetch. + const body = { pageSize: 48, pageNumber: 1, sortBy: "createdAt", sortDescending: true }; + _proxyPost("/api/query/assetDtos", body) .then((resp) => { - if (resp.status === 404 || resp.status === 501) { - state.assets = []; - _renderGrid(); - _banner("BTE endpoint /api/query/assetGrid not yet implemented (PRD §5.4 status: implementing). Grid will populate once endpoint ships.", "warn"); - return; - } if (!resp.ok) { - _log(`assetGrid ← ${resp.status}`); + _log(`assetDtos ← ${resp.status}`); return; } - const json = _safeJson(resp.bodyText) || { items: [] }; - state.assets = json.items || []; + const json = _safeJson(resp.bodyText) || { data: [] }; + // Normalize AssetDto rows to the panel's `assets` shape so _renderGrid stays unchanged. + state.assets = (json.data || []) + .filter((a) => a.brandId === state.brand || !state.brand || state.brand === "planb") + .map((a) => ({ + id: a.id, + thumbUrl: a.hasImageData ? (PROXY_BASE + encodeURIComponent("/api/assets/" + a.id + "/image")) : null, + lifecycle: a.status, + ratingCount: 0, + meanScore: a.visualPolishScore || a.brandFitScore || null, + recipeSlug: state.family || "(any)", + prompt: a.prompt || "", + width: a.width, + height: a.height, + createdAt: a.createdAt, + })); _renderGrid(); - // Stop polling when no rendering asset remains. - const stillInFlight = state.assets.some((a) => a.lifecycle === "generating" || a.lifecycle === "evaluating"); + const stillInFlight = state.assets.some((a) => a.lifecycle === "requested" || a.lifecycle === "generating" || a.lifecycle === "evaluating"); if (!stillInFlight && state.pollTimer) { clearInterval(state.pollTimer); state.pollTimer = null; - _log("polling stopped — all renders complete"); + _log("polling stopped — no in-flight renders"); } }) - .catch((e) => _log(`assetGrid × ${e.message}`)); + .catch((e) => _log(`assetDtos × ${e.message}`)); } function _startPolling() {