The per-route loop in `rebuild_routes` only visits routes whose model_pattern
appears in the current `by_model` set. When the last active account for a
model goes away (account deleted, status flipped, model dropped from
model_availability), the model falls out of `by_model` entirely and its
existing auto-managed channels are never reconciled — they stay behind
pointing at a route nothing can serve.
Add a final orphan sweep over all auto-managed route_channels. Anything whose
route wasn't processed in the per-route loop gets hard-deleted. Manual-override
channels still get the same exemption as before.
Covered by two new in-memory sqlite tests:
- orphan_channel_is_cleaned_when_last_account_goes_inactive
- orphan_sweep_preserves_manual_channels
- spawn_route_rebuild after create / delete / model refresh / manual model
upsert; gate update_account on status change so cosmetic edits don't
burn a transaction
- list_accounts now accepts ?segment=apikey|session|oauth, filtered against
extra_config.$.credentialMode (legacy null rows treated as apikey)
Frontend reads main's drizzle-generated camelCase fields (siteId,
accessToken, oauthProvider, etc.) and a nested `site: {...}` on each
account row. Rust models were serializing snake_case with no nested
site, so PUT /api/sites/undefined/disabled-models 404s, account.site?.id
is undefined everywhere, and similar shape mismatches break Accounts,
Sites, Tokens, and Dashboard pages.
- Add #[serde(rename_all = "camelCase")] to every model struct in
src/db/models/* (Account, AccountToken, Site, User, Event,
TokenRoute, RouteChannel, BackgroundTask, DownstreamApiKey,
Organization/Member/Invitation, SiteAnnouncement)
- list_accounts now nests `site: {...}` per row, matching main's
`{...account, site: {...}}` shape from accountsOverviewService
- admin user-detail endpoint emits camelCase keys (totalRequests,
totalCost, last7DaysCost, recentLogs, promptTokens, completionTokens,
totalTokens, createdAt) to match the frontend
- routes refresh endpoint now emits both affected_routes and
affectedRoutes for forward compat
Co-Authored-By: Kiro <noreply@kiro.dev>
Previously every executor took `_state: AppState` (unused) and `check_auth`
discarded the matched downstream key id, so `usage_logs` was orphan code.
This change plumbs the key id from auth → handlers → executors → billing,
adds the OpenAI Responses API endpoint, and exposes shim aliases under
`/api/comfyui-agent/proxy/openai/*` so embedded ComfyUI's OpenAI nodes
flow through the same auth/conductor/billing pipeline.
- check_auth now returns Option<i64> (managed key id) instead of unit
- record_billing helper writes usage_logs after each successful upstream call
- extract_usage handles both chat-completions and responses usage shapes
- conductor wrappers record billing post-Success; stream variant scans
chunks in reverse for the final usage event
- POST /v1/responses (stream + non-stream) via ProxyConductor
- shim aliases mounted on the same Router so auth/failover/billing work for free
- ComfyUIProcess injects BOOSAPI_BUILTIN_KEY env + --comfy-api-base on spawn
- bootstrap built-in `comfyui-agent` downstream key at startup (idempotent)
- get_auth_header in vendored ComfyUI falls back to BOOSAPI_BUILTIN_KEY env
Co-Authored-By: Kiro <noreply@kiro.dev>
- COMFYUI_BASE configurable via env var (default 127.0.0.1:8188)
- All /api/comfyui/ routes protected by downstream API Key or admin token
- docker/Dockerfile.comfyui + docker-compose.comfyui.yml for dev
- npm scripts: comfyui:install, comfyui:start, comfyui:stop
- .gitignore / .env.example updated
- Fix auth: use upstream API key (sk-boos4721) instead of local proxy
- Simplify SSE: remove nested SSE-in-SSE parsing, stream as plain text
- Fix frontend: add Authorization header from localStorage
- Fix ComfyUI nodes: default to api.boos.lat with correct API key
- All verified: frontend streaming works end-to-end with gpt-5.5
ComfyUI is kept as a visual workflow editor (node-based). All actual
generation goes through BoosAPI's own API via boosapi_comfy_nodes.py
custom nodes (now defaulting to localhost:4000/v1).
Architecture:
BoosAPI (Node.js, port 4000)
├── /video-agent → iframe → ComfyUI (Python, port 8188, CPU)
│ └── custom_nodes/boosapi_comfy_nodes.py
│ └── calls BoosAPI /v1/... API
├── /comfyui-agent → chat interface (direct API calls)
└── /v1/... → API endpoints (LLM, image, TTS, video)
Signed-off-by: boos <boos@boos.lat>
CI / Detect Docs Changes (push) Has been cancelled
CI / Test Core (push) Has been cancelled
CI / Build Web (push) Has been cancelled
CI / Build Server (push) Has been cancelled
CI / Build Desktop (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Repo Drift Check (push) Has been cancelled
CI / Schema Check (SQLite) (push) Has been cancelled
CI / Schema Check (MySQL) (push) Has been cancelled
CI / Schema Check (Postgres) (push) Has been cancelled
CI / Build Docs (push) Has been cancelled
CI / Audit Production Dependencies (push) Has been cancelled
CI / Publish Docker Image (amd64) (push) Has been cancelled
CI / Publish Docker Image (arm64) (push) Has been cancelled
CI / Publish Docker Image (armv7) (push) Has been cancelled
CI / Publish Docker Manifest (push) Has been cancelled
CodeQL / Analyze (JavaScript/TypeScript) (javascript-typescript) (push) Has been cancelled
- Rename project from Metapi to BoosAPI across all UI and server strings
- Add ComfyUI conversational agent page (/comfyui-agent)
- Add user management system (register, login, API keys, admin)
- Update Dockerfile and database schema
- Add ComfyUI workflow nodes support
- Update shared contracts and platform configurations
Signed-off-by: Boos4721 <boos4721@icloud.com>
Refresh the PR onto current main and close the remaining probe-setting review gaps: the primary site save now persists the latency threshold, the site row type carries that field without an any-cast, active probe streams are aborted on unmount, and all-model one-shot probes get the longer timeout they need.
Constraint: PR #510 must preserve the per-site model probe feature while merging cleanly into current main.
Rejected: Leave threshold persistence behind the separate probe-settings button | the primary save path would still silently drop user changes.
Rejected: Keep the SiteRow any-cast | it hides API/schema drift for the new persisted field.
Confidence: high
Scope-risk: narrow
Directive: Keep probe setting fields represented in the typed site save payload and covered by focused web API/editor tests.
Tested: npm run typecheck; npm run repo:drift-check; npx vitest run --root . src/server/db/schemaContract.test.ts src/server/db/schemaArtifactGenerator.test.ts src/server/db/schemaIntrospection.test.ts src/server/db/schemaParity.test.ts src/web/api.test.ts src/web/pages/helpers/sitesEditor.test.ts src/web/pages/sites.disabled-models-save.test.tsx src/web/pages/sites.edit-scroll.test.tsx
Not-tested: Full npm test.
The release branch had fallen behind current main and only conflicted in the
Update Center recent-tag test. Keep the latest main-side deploy-state coverage
while preserving the PR's raw-tag filtering and digest visibility assertions.
Constraint: PR #495 must merge into the current main without widening the PR diff beyond the update-center candidate behavior.
Rejected: Drop the PR-specific digest/raw-tag assertions | would regress the review fixes that prompted the branch update.
Confidence: high
Scope-risk: narrow
Directive: Keep recent Docker tag cards deployable only from real tagName values and keep digest metadata visible before deployment.
Tested: npx vitest run --root . src/web/pages/settings/UpdateCenterSection.test.tsx src/server/services/updateCenterVersionService.test.ts src/server/routes/api/updateCenter.test.ts src/server/services/updateCenterStatusService.test.ts; npm run typecheck; npm run repo:drift-check; git diff --cached --check origin/main
Not-tested: Full npm test.
This merge keeps PR #533 aligned with dev after addressing CodeRabbit follow-up test comments. The changes are limited to UpdateCenterSection test assertions.
Constraint: Release branch remains the publication branch for the open main-targeting PR.
Confidence: high
Scope-risk: narrow
Tested: npx vitest run --root . src/web/pages/settings/UpdateCenterSection.test.tsx
Tested: npm run typecheck:web
Co-authored-by: OmX <omx@oh-my-codex.dev>
CodeRabbit follow-up review pointed out that one renderer assertion could not fail usefully and that the new recent-candidate deploy test did not prove log streaming after dispatch. The test now asserts the visible candidate text directly and covers streamUpdateCenterTaskLogs for the new deploy path.
Constraint: Keep review follow-up scoped to test coverage only.
Confidence: high
Scope-risk: narrow
Tested: npx vitest run --root . src/web/pages/settings/UpdateCenterSection.test.tsx
Tested: npm run typecheck:web
Co-authored-by: OmX <omx@oh-my-codex.dev>
The release branch is main-based and tracks the reviewed dev slice for PR publication. This merge carries the post-review compatibility fixes without rewriting the already-open PR history.
Constraint: PR #533 publishes dev changes through a clean release branch targeting main.
Rejected: Recreate the PR branch from scratch | unnecessary churn after green checks and review context already exist.
Confidence: high
Scope-risk: narrow
Tested: npx vitest run --root . src/server/proxy-core/responsesPreflight.test.ts src/server/proxy-core/serviceTierPolicy.test.ts src/server/routes/proxy/chat.stream.test.ts src/web/pages/settings/UpdateCenterSection.test.tsx
Tested: npm run typecheck:server
Tested: npm run typecheck:web
Tested: npm run repo:drift-check
Co-authored-by: OmX <omx@oh-my-codex.dev>
Review feedback found narrow compatibility regressions in the web-search shim, Responses tool detection, service tier accounting, and update-center Docker candidate actions. The fix keeps synthetic search routed through the dedicated search model, preserves Responses text compatibility, distinguishes absent service_tier from filtered service_tier, and evaluates recent Docker tags per candidate.
Constraint: Review fixes must stay on dev first, then flow into the main-based release PR branch.
Rejected: Leave Responses web_search simulation on the request model | it has the same search-route selection risk as Anthropic simulation.
Confidence: high
Scope-risk: narrow
Tested: npx vitest run --root . src/server/proxy-core/responsesPreflight.test.ts src/server/proxy-core/serviceTierPolicy.test.ts src/server/routes/proxy/chat.stream.test.ts src/web/pages/settings/UpdateCenterSection.test.tsx
Tested: npm run typecheck:server
Tested: npm run typecheck:web
Tested: npm run repo:drift-check
Co-authored-by: OmX <omx@oh-my-codex.dev>