mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 01:32:22 +03:00
Compare commits
5 Commits
maint/cher
...
feat/9544-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7826fb8c4c | ||
|
|
bf1681727e | ||
|
|
b80a11e98a | ||
|
|
ea7866ae80 | ||
|
|
ef236934c0 |
@@ -1 +0,0 @@
|
||||
10
|
||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -72,6 +72,7 @@ yarn-error.log*
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
!.env.example
|
||||
!.env.devin-bridge.example
|
||||
!.env.homolog.example
|
||||
# Provider API keys (never commit)
|
||||
*.api-key
|
||||
@@ -209,6 +210,8 @@ scripts/i18n/_pending-keys.json
|
||||
.agents/
|
||||
.antigravitycli/
|
||||
.claude/
|
||||
!tests/fixtures/devin-bridge/e2e-workspace/.claude/
|
||||
!tests/fixtures/devin-bridge/e2e-workspace/.claude/**
|
||||
|
||||
# PR Reviews and local feedback files
|
||||
pr_reviews*.json
|
||||
@@ -235,7 +238,10 @@ omniroute.md
|
||||
|
||||
# mise configuration
|
||||
mise.toml
|
||||
_artifacts/ # release-green artifacts
|
||||
# release-green artifacts (.gitignore has no inline comments — a trailing
|
||||
# `# ...` becomes part of the pattern, so it must sit on its own line).
|
||||
# Already covered by /_*/ above; kept explicit for discoverability.
|
||||
_artifacts/
|
||||
.claude-flow/
|
||||
|
||||
# ESLint file cache (npm run lint --cache / complexity ratchets)
|
||||
@@ -245,6 +251,8 @@ _artifacts/ # release-green artifacts
|
||||
|
||||
# CI/local quality artifacts (eslint-results.json, quality-ratchet.md, etc.)
|
||||
.artifacts/
|
||||
# Isolated Devin bridge workspaces, evidence, and test databases
|
||||
.sandbox/
|
||||
|
||||
# Homologation E2E suite (npm run homolog) — real-environment credentials + report output
|
||||
.env.homolog
|
||||
@@ -253,7 +261,11 @@ tests/homolog/ui/.auth/
|
||||
homolog-report/
|
||||
docker-compose.yml.bak
|
||||
.playwright-cli/
|
||||
# Playwright screenshot/log output. Today every artifact happens to land inside
|
||||
# output/**/.playwright-cli/ (covered above), but anything written directly to
|
||||
# output/ would otherwise show up as untracked.
|
||||
/output/
|
||||
|
||||
# Runtime artifacts
|
||||
.chroma_db/
|
||||
omniroute.log
|
||||
# _tasks e um repo git SEPARADO (ver AGENTS.md). _tasks/ (com barra) NAO ignora um
|
||||
# SYMLINK _tasks; /_tasks (ancorado) cobre symlink/dir na raiz (incidente 2026-08-08).
|
||||
/_tasks
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
- Session initialized at Tue Aug 4 00:05:16 CEST 2026
|
||||
- Session initialized at Tue Aug 4 00:05:30 CEST 2026
|
||||
- Session initialized at Tue Aug 4 00:06:20 CEST 2026
|
||||
- Session initialized at Thu Aug 6 20:06:52 CEST 2026
|
||||
- Session initialized at Thu Aug 6 20:35:48 CEST 2026
|
||||
- Session initialized at Thu Aug 6 20:39:00 CEST 2026
|
||||
- Session initialized at Thu Aug 6 21:04:02 CEST 2026
|
||||
- Session initialized at Thu Aug 6 21:42:52 CEST 2026
|
||||
- Session initialized at Thu Aug 6 22:49:37 CEST 2026
|
||||
- Session initialized at Thu Aug 6 22:50:20 CEST 2026
|
||||
- Session initialized at Thu Aug 6 23:17:04 CEST 2026
|
||||
- Session initialized at Fri Aug 7 05:50:47 CEST 2026
|
||||
- Session initialized at Fri Aug 7 05:57:54 CEST 2026
|
||||
- Session initialized at Fri Aug 7 06:58:16 CEST 2026
|
||||
- Session initialized at Fri Aug 7 07:02:53 CEST 2026
|
||||
- Session initialized at Fri Aug 7 07:33:32 CEST 2026
|
||||
- Session initialized at Fri Aug 7 08:04:36 CEST 2026
|
||||
- Session initialized at Fri Aug 7 08:52:52 CEST 2026
|
||||
- Session initialized at Fri Aug 7 09:01:19 CEST 2026
|
||||
- Session initialized at Sat Aug 8 18:46:20 CEST 2026
|
||||
@@ -1,8 +0,0 @@
|
||||
# AI-NOTES (Handoff Log)
|
||||
|
||||
## Changelog
|
||||
|
||||
- **2026-08-08 (Antigravity)**:
|
||||
- Status: DONE (Phase 0 and Phase 1).
|
||||
- Actions: Cleaned up PM2 crash-loops (`litellm-proxy`), updated PM2 ecosystem, ran Claude context-optimization (`apply.sh`), removed `.chroma_db` and logs from git, created `HERMES-OMNI-MASTER-PLAN.md` with full 8-phase execution plan.
|
||||
- Next: Phase 2 (Safe cleanup of bundled skills, PM2 port alignment, NPM audit, Doppler secrets unification).
|
||||
142
CLAUDE.md
142
CLAUDE.md
@@ -1,117 +1,55 @@
|
||||
# CLAUDE.md
|
||||
|
||||
OmniRoute — unified AI proxy/router. One endpoint, 290 LLM providers, auto-fallback.
|
||||
@AGENTS.md
|
||||
|
||||
<!-- CONTEXT BUDGET: this file loads in full on every session and after every compact.
|
||||
Keep it under 200 lines. Anything deeper goes to .claude/rules/ (path-scoped,
|
||||
loads only when Claude touches matching files) or .claude/skills/ (loads on demand).
|
||||
Do not re-inline detail here. -->
|
||||
**All project rules live in [`AGENTS.md`](AGENTS.md)** — the single source of truth for every AI
|
||||
assistant (architecture, conventions, testing, quality gates, git workflow, the 22 Hard Rules,
|
||||
PII learnings). Read it in full; do not re-add project rules here. Everything below applies ONLY
|
||||
to Claude Code — operational refinements of rules already defined in `AGENTS.md`.
|
||||
|
||||
## Commands
|
||||
## Worktree isolation — Claude Code specifics
|
||||
|
||||
```bash
|
||||
npm install # deps (auto-generates .env from .env.example)
|
||||
npm run dev # dev server → http://localhost:20128
|
||||
npm run build # production build (Next.js 16 standalone)
|
||||
npm run lint # ESLint — 0 errors expected
|
||||
npm run typecheck:core # TS check (must be clean)
|
||||
npm run test:unit # Node native runner (most tests)
|
||||
npm run test:vitest # Vitest (MCP server, autoCombo, cache)
|
||||
npm run test:coverage # coverage gate 60/60/60/60
|
||||
npm run check # lint + test
|
||||
node --import tsx/esm --test tests/unit/your-file.test.ts # single file
|
||||
```
|
||||
The full mandatory worktree protocol (base-branch confirmation, `.claude/worktrees/` canonical
|
||||
path, `cp -al` node_modules, teardown rules) is in `AGENTS.md` → Git Workflow → "Worktree
|
||||
isolation". Claude-Code-specific points:
|
||||
|
||||
Full test matrix: `CONTRIBUTING.md`. Deep architecture for non-Claude agents: `AGENTS.md`.
|
||||
- Confirm the base branch with the operator via `AskUserQuestion` (Hard Rule #19) unless they
|
||||
already told you.
|
||||
- Prefer the native `EnterWorktree` tool — it already creates worktrees under
|
||||
`.claude/worktrees/` (the canonical path). Create the worktree with the documented `git
|
||||
worktree add` command, then call `EnterWorktree` with its `path`.
|
||||
|
||||
## Layout
|
||||
## Cross-session safety — Claude Code specifics
|
||||
|
||||
Monorepo: `src/` (Next.js 16 app) · `open-sse/` (streaming engine workspace) · `electron/` · `tests/` · `bin/` (CLI).
|
||||
Hard Rules #19/#21/#22 (in `AGENTS.md`) govern parallel sessions. Operational reminders for this
|
||||
harness:
|
||||
|
||||
| Layer | Location | Purpose |
|
||||
| ------------- | ----------------------- | -------------------------------------- |
|
||||
| API Routes | `src/app/api/v1/` | App Router entry points |
|
||||
| Handlers | `open-sse/handlers/` | Request processing |
|
||||
| Executors | `open-sse/executors/` | Provider-specific HTTP dispatch |
|
||||
| Translators | `open-sse/translator/` | OpenAI ↔ Claude ↔ Gemini |
|
||||
| Transformer | `open-sse/transformer/` | Responses API ↔ Chat Completions |
|
||||
| Services | `open-sse/services/` | Combo routing, rate limits, caching |
|
||||
| Database | `src/lib/db/` | SQLite domain modules (130 migrations) |
|
||||
| Domain/Policy | `src/domain/` | Policy engine, cost rules, fallback |
|
||||
| MCP Server | `open-sse/mcp-server/` | 104 tools, 3 transports, 31 scopes |
|
||||
| A2A Server | `src/lib/a2a/` | JSON-RPC 2.0 agent protocol |
|
||||
| Skills | `src/lib/skills/` | Sandbox skill framework |
|
||||
| Memory | `src/lib/memory/` | Persistent conversational memory |
|
||||
- **Replicate the `git stash` ban verbatim in the prompt of every subagent that touches git**
|
||||
(Agent tool / Workflow scripts) — subagents do not inherit this file, and the recorded
|
||||
recurrence of the stash incident came through a subagent.
|
||||
- Before merging or pushing to any PR you did not create _this session_, run `git worktree list`
|
||||
and re-check `gh pr view <N> --json state,headRefOid` (Hard Rule #22b).
|
||||
- End every session with the main checkout on the branch it started on.
|
||||
|
||||
Request pipeline: `route → CORS → Zod validation → optional auth → API-key policy → injection guard → handleChatCore() → cache → rate limit → combo routing → translate → executor → upstream fetch (retry/backoff) → translate back → SSE or JSON`. No global Next.js middleware; interception is per-route.
|
||||
## Superpowers / planning artifacts — path overrides
|
||||
|
||||
## Hard rules
|
||||
The `_tasks/` convention is defined in `AGENTS.md` → "Planning & Research Artifacts". The
|
||||
superpowers skills ship with defaults that point at `docs/…` — those defaults are **overridden
|
||||
here**. When a superpowers skill announces a path like "saved to `docs/superpowers/plans/…`",
|
||||
rewrite it to the `_tasks/…` equivalent before writing:
|
||||
|
||||
Violating any of these is a blocking defect. Numbers are stable — cite them in review.
|
||||
| Artifact (skill) | Default (do NOT use) | Save here instead |
|
||||
| ---------------------------------- | ------------------------- | ------------------------------------------------------------- |
|
||||
| Plans (`writing-plans`) | `docs/superpowers/plans/` | `_tasks/superpowers/plans/YYYY-MM-DD-<feature>.md` |
|
||||
| Specs / design (`brainstorming`) | `docs/superpowers/specs/` | `_tasks/superpowers/specs/YYYY-MM-DD-<topic>-design.md` |
|
||||
| Research (`deep-research`, ad-hoc) | `docs/research/` | `_tasks/research/…` |
|
||||
| Hand-offs (`/handoff`) | — | `_tasks/hands-off/<YYYY-MM-DD>_<branch>_v<versão>_sess-<id>/` |
|
||||
|
||||
1. Never commit secrets or credentials.
|
||||
2. Never add logic to `src/lib/localDb.ts` (re-export layer only); never barrel-import from it.
|
||||
3. Never use `eval()` / `new Function()` / implied eval.
|
||||
4. Never commit directly to `main`.
|
||||
5. Never write raw SQL in routes or handlers — go through `src/lib/db/` modules.
|
||||
6. Never silently swallow errors in SSE streams.
|
||||
7. Always validate inputs with Zod.
|
||||
8. Always include or update tests in the same PR that changes production code in `src/`, `open-sse/`, `electron/`, `bin/`.
|
||||
9. Coverage must not regress below `quality-baseline.json`; absolute floor 60/60/60/60.
|
||||
10. Never bypass Husky hooks (`--no-verify`, `--no-gpg-sign`) without explicit operator approval.
|
||||
11. Never embed public upstream OAuth client_id/secret or Firebase Web keys as literals — use `resolvePublicCred()`.
|
||||
12. Never return raw `err.stack` / `err.message` in HTTP / SSE / executor / MCP responses — use `buildErrorBody()` or `sanitizeErrorMessage()`.
|
||||
13. Never string-interpolate external paths into shell scripts passed to `exec()`/`spawn()` — pass via the `env` option.
|
||||
14. Never dismiss a CodeQL / secret-scanning alert without checking the sanitizer docs first and recording the justification.
|
||||
15. Never expose child-process-spawning routes (`/api/mcp/`, `/api/cli-tools/runtime/`) without `isLocalOnlyPath()` classification.
|
||||
16. Never credit an AI assistant in commit/PR metadata — no `Co-Authored-By` naming an AI/bot, no "Generated with …" footers, anywhere. This overrides any harness default; strip it before pushing. Human collaborators are still credited normally.
|
||||
17. Never expose `/api/services/` or `/dashboard/providers/services/*/embed/` without `isLocalOnlyPath()` classification.
|
||||
18. Every bug fix ships with a failing-then-passing test (TDD, preferred) **or** a documented live test on the production VPS `192.168.0.15`. "Worked locally" is not validation.
|
||||
19. Never develop on the shared main checkout. Every task gets its own worktree under `.claude/worktrees/`, on its own branch, cut from a base branch the operator confirmed. → skill `omniroute-worktree`
|
||||
20. PII redaction/sanitization is opt-in. `PII_REDACTION_ENABLED` and `PII_RESPONSE_SANITIZATION` must keep `defaultValue: "false"`.
|
||||
21. During an active `release-freeze`, never merge into the frozen `release/vX.Y.Z`; retarget to the highest `release/v*` and verify the retarget. Only `/generate-release` may raise or lift a freeze. → skill `omniroute-release-freeze`
|
||||
22. Cross-session safety — many parallel sessions work this repo:
|
||||
- **Never `git stash` / `git stash pop`, anywhere in this repo, including inside a worktree and inside any subagent you dispatch.** The stash lives in the shared object store and clobbers other sessions' uncommitted work. Compare against a base with `git show <ref>:<path>` or `git diff <ref> -- <path>` instead. Repeat this ban verbatim in the prompt of every subagent that touches git.
|
||||
- Never merge, push, rebase, or force-push a PR / branch / worktree another session owns. Check `git worktree list` and `gh pr view <N> --json state,headRefOid` before touching a PR you did not open this session.
|
||||
Commit those artifacts inside the `_tasks/` repo (`git -C _tasks …`), never in the main repo.
|
||||
|
||||
## Conventions (always-on subset)
|
||||
## Base-green before opening PRs
|
||||
|
||||
- 2 spaces, semicolons, double quotes, 100 cols, es5 trailing commas (Prettier via lint-staged).
|
||||
- Imports: external → internal (`@/`, `@omniroute/open-sse`) → relative.
|
||||
- Naming: files camelCase/kebab, components PascalCase, constants UPPER_SNAKE.
|
||||
- `no-explicit-any` is an **error** in `open-sse/` and `tests/`; pre-existing violations are frozen in `config/quality/eslint-suppressions.json`.
|
||||
- TypeScript `strict: false`, target ES2022, module esnext, resolution bundler. Prefer explicit types.
|
||||
- Commits: Conventional Commits, `feat(db): …`. Branch prefixes `feat/ fix/ refactor/ docs/ test/ chore/`.
|
||||
|
||||
## Environment
|
||||
|
||||
Node ≥22 <23 || ≥24 <27, ESM — the only supported runtime. Bun 1.3.14 is pinned as an exact devDependency for an allow-listed set of gate/generator scripts and the `test:bun:db` smoke only; do not widen it to install, build, or the main test runners. Port 20128. `DATA_DIR` defaults to `~/.omniroute/`. Aliases: `@/*` → `src/`, `@omniroute/open-sse` → `open-sse/`.
|
||||
|
||||
## Planning artifacts
|
||||
|
||||
`_tasks/` is a separate, gitignored git repo and is the only place for plans, specs, research, and hand-offs. Never write superpowers / planning / research output under `docs/` or the repo root, even when a skill's default says `docs/superpowers/…` — rewrite the path to `_tasks/…` before writing, and commit inside `_tasks/` (`git -C _tasks …`).
|
||||
|
||||
## Where the rest lives
|
||||
|
||||
Do not ask for these upfront — they load themselves when relevant.
|
||||
|
||||
**Path-scoped rules** (`.claude/rules/`, auto-load when you open matching files): `database.md`, `api-routes.md`, `open-sse.md`, `resilience.md`, `security-sensitive.md`, `pii.md`, `testing.md`.
|
||||
|
||||
**Skills** (`.claude/skills/`, load on demand): `omniroute-worktree`, `omniroute-release-freeze`, `omniroute-extend` (add a provider / route / DB module / MCP tool / A2A skill / cloud agent / embedded service), `omniroute-quality-gates`.
|
||||
|
||||
**Docs** — read the matching deep-dive before any non-trivial change:
|
||||
|
||||
| Area | Doc |
|
||||
| ---------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| Repo navigation | `docs/architecture/REPOSITORY_MAP.md` |
|
||||
| Architecture / engineering reference | `docs/architecture/ARCHITECTURE.md`, `CODEBASE_DOCUMENTATION.md` |
|
||||
| Auto-Combo (13-factor, 19 strategies) | `docs/routing/AUTO-COMBO.md` |
|
||||
| Resilience (3 mechanisms) | `docs/architecture/RESILIENCE_GUIDE.md` |
|
||||
| Reasoning replay | `docs/routing/REASONING_REPLAY.md` |
|
||||
| Skills / Memory / Cloud agents | `docs/frameworks/SKILLS.md`, `MEMORY.md`, `CLOUD_AGENT.md` |
|
||||
| Guardrails / public creds / error sanitization | `docs/security/GUARDRAILS.md`, `PUBLIC_CREDS.md`, `ERROR_SANITIZATION.md` |
|
||||
| Authorization / route guard tiers | `docs/architecture/AUTHZ_GUIDE.md`, `docs/security/ROUTE_GUARD_TIERS.md` |
|
||||
| Agent protocols / MCP / A2A | `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`, `MCP-SERVER.md`, `A2A-SERVER.md` |
|
||||
| API reference | `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` |
|
||||
| Release flow / embedded services | `docs/ops/RELEASE_CHECKLIST.md`, `docs/frameworks/EMBEDDED-SERVICES.md` |
|
||||
| Quality gates (~48 scripts) | `docs/architecture/QUALITY_GATES.md` |
|
||||
Before cutting a branch or opening a PR, run the base-green check (`AGENTS.md` → Git Workflow →
|
||||
"Base-green check"; project skills reference it as `.agents/skills/_shared/base-green.md`). A PR
|
||||
opened while the base tip is red must carry `⚠️ base-red inherited: #<issue>` in its body. To
|
||||
drain an accumulated red state (base tip + red PRs), use the `/sweep-reds` skill.
|
||||
|
||||
14
OS-NOTES.md
14
OS-NOTES.md
@@ -1,14 +0,0 @@
|
||||
# OS-NOTES (Dashboard & Roadmap)
|
||||
|
||||
## Dashboard
|
||||
|
||||
- OmniRoute / Hermes integration active.
|
||||
- Context optimization script applied.
|
||||
- Master plan drafted (`HERMES-OMNI-MASTER-PLAN.md`).
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [done] Phase 0 & 1: Inventory and context optimization.
|
||||
- [next] Phase 2: Secrets and stability (resolve Doppler configs `prd` vs `dev_personal`, fix `omniroute-tunnel`, safe skill symlink cleanup).
|
||||
- [next] Phase 3: AGY / Claude Code integration via OmniRoute.
|
||||
- [next] Phase 4: Combo evaluations.
|
||||
1
changelog.d/features/9544-muse-code-cli-provider.md
Normal file
1
changelog.d/features/9544-muse-code-cli-provider.md
Normal file
@@ -0,0 +1 @@
|
||||
- feat(providers): add Muse Code CLI provider preset (#9544)
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"_rebaseline_2026_08_09_9296_adobe_media_capabilities": "PR #9296 (artickc, fix/adobe-firefly-model-capabilities) own growth: src/app/api/v1/models/catalog.ts 1590->1597 (+7). The image and video catalog serializers now expose the already-normalized Adobe Firefly discovery capability data (media_capabilities, plus the existing video modality/size fields) at their only response-emission chokepoints. The discovery parser and capability normalization remain in open-sse/services/adobeFireflyModels.ts; extracting these seven serialization fields would obscure the catalog contract. Covered by tests/unit/adobe-firefly.test.ts and tests/unit/image-upscale.test.ts.",
|
||||
"_rebaseline_2026_07_24_8470_hyperagent_sticky_thread": "PR #8470 (artickc, fix/hyperagent-tool-loop-thread-sticky) own growth: open-sse/executors/hyperagent.ts 936->1025 (wc -l; check-file-size.mjs counts via split(\"\\n\").length so the gate sees 937->1026, +89, crosses the 1000 cap). Fixes a real bug where a reverse-conversion proxy (text-Intent/JSON to Claude Code native tool_calls) rewrites assistant messages between agentic tool-loop turns, breaking HyperAgent’s conversation-prefix fingerprint and cold-starting the thread mid tool-loop. Adds Anthropic tool_use/tool_result flattening to extractMessageText() plus a new rootUserFingerprint()/root-key lookup tier in resolveHyperAgentThreadBinding()/storeHyperAgentThreadAfterTurn() so the thread stays sticky across the tool loop. Cohesive additions inside the existing single-file executor; not extractable without splitting the executor mid-request-flow. Covered by tests/unit/executor-hyperagent.test.ts (19/19, +5 new cases for tool_result/tool_use flattening + root-key stickiness). Pre-merge review flagged a cross-conversation root-key collision risk (tracked in the PR’s own mandatory pre-merge checklist, not yet addressed) — unrelated to this file-size ratchet, tracked separately by /fix-prs.",
|
||||
"_rebaseline_2026_07_25_8494_capability_filter_fail_closed": "PR #8494 (fix/capability-filters-fail-closed, #8488) own growth: open-sse/services/combo.ts 3640->3693 (+53) adds a fail-closed guard after filterTargetsByRequestCompatibility() — when every eligible target is excluded by request-capability filtering (vision/tools/etc) instead of quota/health, the combo now returns an explicit `capability_mismatch` 400 (describeCapabilityFilterExhaustion, imported from combo/comboStructure.ts) rather than silently falling through to a generic no-targets error, plus a `compatFilterFailOpen` escape hatch (combo config OR settings) mirrored at both the main/auto and round-robin call sites for symmetry. combo/comboStructure.ts (previously under cap, un-frozen) grows 794->918 (+124) — new home for describeCapabilityFilterExhaustion + providerSupportsEmulatedToolCalling (#5240 emulated tool-calling exemption so fail-closed does not regress prompt-emulation-only combos like all-chatgpt-web). Irreducible orchestration wiring at the existing filter chokepoint (same precedent as #7301's universal-cooldown-retry generalization). Companion test tests/unit/combo-routing-engine.test.ts 3409->3449 (+40, fail-closed/fail-open coverage across both call sites) also rebaselined. Covered by tests/unit/8488-capability-filter-fail-closed.test.ts (new) + 95/95 passing across both files. Structural shrink of combo.ts tracked in #3501.",
|
||||
"_rebaseline_2026_07_25_8499_ts7_result_union_predicates": "PR #8499 (backryun, chore/ts7-types-executor-scattered) own growth: muse-spark-web.ts 1396->1405 (+9, irreducible). Under this workspace's `strictNullChecks: false`, the boolean-literal discriminant on `GraphqlResult` (`{ ok: true } | { ok: false; error: string }`) narrows the positive `.ok===true` branch but leaves `!result.ok` at the full union under TS7, making `.error` unreachable to the checker at the two call sites (warmup, mode-switch). Fixed by adding a single `isGraphqlFailure()` type-predicate helper (doc comment + 3-line body) reused at both call sites instead of duplicating the predicate inline — not extractable to a shared module without splitting a single-file executor's local narrowing helper out of its own file. Covered by the existing muse-spark-web executor test suite (no behavior change, pure narrowing fix).",
|
||||
@@ -388,7 +387,7 @@
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": 2148,
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": 1119,
|
||||
"src/app/api/providers/[id]/models/route.ts": 2361,
|
||||
"src/app/api/v1/models/catalog.ts": 1597,
|
||||
"src/app/api/v1/models/catalog.ts": 1590,
|
||||
"src/lib/db/apiKeys.ts": 1529,
|
||||
"src/lib/db/core.ts": 1639,
|
||||
"src/lib/db/migrationRunner.ts": 1094,
|
||||
@@ -537,7 +536,7 @@
|
||||
"src/app/(dashboard)/dashboard/usage/components/EvalsTab.tsx": "2148",
|
||||
"src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx": "1119",
|
||||
"src/app/api/providers/[id]/models/route.ts": "2361",
|
||||
"src/app/api/v1/models/catalog.ts": "1597",
|
||||
"src/app/api/v1/models/catalog.ts": "1590",
|
||||
"src/lib/tokenHealthCheck.ts": "1053",
|
||||
"src/lib/db/apiKeys.ts": "1529",
|
||||
"src/lib/db/core.ts": "1639",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
---
|
||||
title: "AgentRouter WAF"
|
||||
version: 3.8.50
|
||||
lastUpdated: 2026-08-03
|
||||
---
|
||||
|
||||
# agentrouter.org WAF (Web Application Firewall)
|
||||
@@ -27,22 +29,22 @@ The WAF inspects `messages[].content` only. It does **not** inspect:
|
||||
|
||||
## Always-blocked patterns (case-insensitive)
|
||||
|
||||
| Pattern | Notes |
|
||||
| --------------------------- | ---------------------------------------------------- |
|
||||
| Any `Lorem ipsum` variant | Full Latin lorem vocabulary is blocked |
|
||||
| `language model` (alone) | "the language model" and "large language model" pass |
|
||||
| `virtual assistant` | "AI assistant" passes |
|
||||
| `I'm here to help` | "here to help" alone also blocks |
|
||||
| `Claude, made by Anthropic` | Full phrase only |
|
||||
| Pattern | Notes |
|
||||
|-------------------------------|----------------------------------------|
|
||||
| Any `Lorem ipsum` variant | Full Latin lorem vocabulary is blocked |
|
||||
| `language model` (alone) | "the language model" and "large language model" pass |
|
||||
| `virtual assistant` | "AI assistant" passes |
|
||||
| `I'm here to help` | "here to help" alone also blocks |
|
||||
| `Claude, made by Anthropic` | Full phrase only |
|
||||
|
||||
## Almost-always-blocked patterns
|
||||
|
||||
| Pattern | Notes |
|
||||
| ------------------------------------------------------- | ---------------------------------------------------- |
|
||||
| `placeholder` | When it stands alone (not as a parameter name, etc.) |
|
||||
| `dummy data` | Common seed phrase for fixtures |
|
||||
| `foo bar baz` | Canonical placeholder phrase |
|
||||
| Repeated short tokens (`AAA BBB CCC`, `test test test`) | Detector for keyword stuffing |
|
||||
| Pattern | Notes |
|
||||
|-------------------|---------------------------------------------------------|
|
||||
| `placeholder` | When it stands alone (not as a parameter name, etc.) |
|
||||
| `dummy data` | Common seed phrase for fixtures |
|
||||
| `foo bar baz` | Canonical placeholder phrase |
|
||||
| Repeated short tokens (`AAA BBB CCC`, `test test test`) | Detector for keyword stuffing |
|
||||
|
||||
## Behavior under load
|
||||
|
||||
@@ -92,4 +94,4 @@ The current filter is overly aggressive — it blocks "Lorem ipsum" in
|
||||
`tool_result` blocks even though the operator clearly did not intend to
|
||||
inject a prompt. Operators who want this fixed at the source should
|
||||
contact `agentrouter.org` to report the false positives. The blocklist
|
||||
above is the empirical result of probing the upstream as of 2026-08-03.
|
||||
above is the empirical result of probing the upstream as of 2026-08-03.
|
||||
@@ -1,44 +0,0 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "omniroute",
|
||||
script: "npm",
|
||||
args: "run start",
|
||||
cwd: "/Users/work/hermes-sandbox/Hermes_Omni",
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
min_uptime: "10s",
|
||||
max_restarts: 10,
|
||||
exp_backoff_restart_delay: 100,
|
||||
restart_delay: 5000,
|
||||
max_memory_restart: "700M",
|
||||
kill_timeout: 10000,
|
||||
listen_timeout: 10000,
|
||||
time: true,
|
||||
merge_logs: true,
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
PORT: 20128
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "hermes-tg-bot",
|
||||
script: "/Users/work/hermes-sandbox/Hermes_Omni/scripts/serpentos_logic/tg-bot.cjs",
|
||||
cwd: "/Users/work/hermes-sandbox/Hermes_Omni",
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
min_uptime: "10s",
|
||||
max_restarts: 10,
|
||||
exp_backoff_restart_delay: 500,
|
||||
restart_delay: 5000,
|
||||
max_memory_restart: "500M",
|
||||
kill_timeout: 10000,
|
||||
time: true,
|
||||
merge_logs: true,
|
||||
env: {
|
||||
NODE_ENV: "production"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
const Database = require('better-sqlite3');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
const db = new Database('/Users/work/.omniroute/storage.sqlite');
|
||||
const combos = db.prepare("SELECT id, name FROM combos").all();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
for (const combo of combos) {
|
||||
const mappingId = uuidv4();
|
||||
// Map the combo name directly as the pattern
|
||||
try {
|
||||
db.prepare(`
|
||||
INSERT INTO model_combo_mappings
|
||||
(id, pattern, combo_id, priority, enabled, description, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`).run(mappingId, combo.name, combo.id, 100, 1, `Auto-mapped combo: ${combo.name}`, now, now);
|
||||
console.log(`Mapped pattern '${combo.name}' to combo '${combo.name}'`);
|
||||
} catch(e) {
|
||||
console.log(`Error mapping ${combo.name}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
console.log("Done.");
|
||||
@@ -1,24 +0,0 @@
|
||||
const Database = require("better-sqlite3");
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
|
||||
const db = new Database("/Users/work/.omniroute/storage.sqlite");
|
||||
const combos = db.prepare("SELECT id, name FROM combos").all();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
for (const combo of combos) {
|
||||
const mappingId = uuidv4();
|
||||
// Map the combo name directly as the pattern
|
||||
try {
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO model_combo_mappings
|
||||
(id, pattern, combo_id, priority, enabled, description, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
).run(mappingId, combo.name, combo.id, 100, 1, `Auto-mapped combo: ${combo.name}`, now, now);
|
||||
console.log(`Mapped pattern '${combo.name}' to combo '${combo.name}'`);
|
||||
} catch (e) {
|
||||
console.log(`Error mapping ${combo.name}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
console.log("Done.");
|
||||
@@ -12,10 +12,6 @@ import { FREEPIK_IMAGE_PROVIDER } from "./providers/registry/freepik/index.ts";
|
||||
import { STABILITY_AI_IMAGE_MODELS } from "./providers/registry/stability-ai/imageModels.ts";
|
||||
import { GEMINI_IMAGEN_PROVIDER } from "./providers/registry/gemini/imageModels.ts";
|
||||
import { CHEAPERINFERENCE_IMAGE_PROVIDER } from "./providers/registry/cheaperinference/imageModels.ts";
|
||||
import {
|
||||
ADOBE_FIREFLY_IMAGE_ROUTING_ALIASES,
|
||||
toRegistryImageModels,
|
||||
} from "../services/adobeFireflyModels.ts";
|
||||
|
||||
interface ImageModelEntry {
|
||||
id: string;
|
||||
@@ -26,8 +22,6 @@ interface ImageModelEntry {
|
||||
imageRequired?: boolean;
|
||||
description?: string;
|
||||
isMarket?: boolean;
|
||||
supportedSizes?: string[];
|
||||
mediaCapabilities?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ImageProviderConfig {
|
||||
@@ -41,7 +35,6 @@ interface ImageProviderConfig {
|
||||
authHeader: string;
|
||||
format: string;
|
||||
models: ImageModelEntry[];
|
||||
routingAliases?: readonly string[];
|
||||
supportedSizes: string[];
|
||||
}
|
||||
|
||||
@@ -53,7 +46,6 @@ interface ImageModelAliasEntry {
|
||||
inputModalities?: string[];
|
||||
imageRequired?: boolean;
|
||||
description?: string;
|
||||
mediaCapabilities?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ImageCatalogModelEntry {
|
||||
@@ -63,7 +55,6 @@ interface ImageCatalogModelEntry {
|
||||
supportedSizes: string[];
|
||||
inputModalities: string[];
|
||||
description?: string;
|
||||
mediaCapabilities?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const IMAGE_MODEL_ALIASES: Record<string, ImageModelAliasEntry> = {
|
||||
@@ -687,9 +678,55 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "adobe-firefly-image",
|
||||
models: toRegistryImageModels(),
|
||||
routingAliases: ADOBE_FIREFLY_IMAGE_ROUTING_ALIASES,
|
||||
supportedSizes: [],
|
||||
models: [
|
||||
{
|
||||
id: "nano-banana-pro",
|
||||
name: "Firefly Gemini 3.0 (Nano Banana Pro)",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "nano-banana",
|
||||
name: "Firefly Gemini 2.5 (Nano Banana)",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "nano-banana-2",
|
||||
name: "Firefly Gemini 3.1 (Nano Banana 2)",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{ id: "gpt-image-2", name: "Firefly GPT Image 2", inputModalities: ["text", "image"] },
|
||||
{ id: "gpt-image", name: "Firefly GPT Image 2", inputModalities: ["text", "image"] },
|
||||
{ id: "gpt-image-1.5", name: "Firefly GPT Image 1.5", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-2", name: "Firefly Flux 2", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-pro", name: "Firefly Flux 1.1 Pro", inputModalities: ["text", "image"] },
|
||||
{ id: "flux-ultra", name: "Firefly Flux 1.1 Ultra", inputModalities: ["text", "image"] },
|
||||
{ id: "seedream-4", name: "Firefly Seedream 4.0", inputModalities: ["text", "image"] },
|
||||
{
|
||||
id: "seedream-5-lite",
|
||||
name: "Firefly Seedream 5.0 Lite",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "runway-gen4-image",
|
||||
name: "Firefly Runway Gen-4 Image",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
// Topaz Labs upscalers (inputMediaUseCase: ["upscaling"]).
|
||||
// Served by firefly-3p /v2/3p-images/upsample — see config/upscaleRegistry.ts.
|
||||
{
|
||||
id: "topaz-standard",
|
||||
name: "Firefly Topaz Upscale (Standard)",
|
||||
inputModalities: ["image"],
|
||||
imageRequired: true,
|
||||
},
|
||||
{
|
||||
id: "topaz-bloom",
|
||||
name: "Firefly Topaz Bloom (Creative Upscale)",
|
||||
inputModalities: ["image"],
|
||||
imageRequired: true,
|
||||
},
|
||||
],
|
||||
supportedSizes: ["1:1", "16:9", "9:16", "4:3", "3:4", "1024x1024", "1792x1024", "1024x1792"],
|
||||
},
|
||||
|
||||
// Cheaper Inference (OSS-sponsor gateway). Declared AFTER adobe-firefly on
|
||||
@@ -850,7 +887,7 @@ export function parseImageModel(modelStr) {
|
||||
|
||||
// No provider prefix — try to find the model in every provider
|
||||
for (const [providerId, config] of Object.entries(IMAGE_PROVIDERS)) {
|
||||
if (config.routingAliases?.includes(modelStr) || config.models.some((m) => m.id === modelStr)) {
|
||||
if (config.models.some((m) => m.id === modelStr)) {
|
||||
return { provider: providerId, model: modelStr };
|
||||
}
|
||||
}
|
||||
@@ -869,10 +906,9 @@ function imageProviderCatalogEntries(
|
||||
id: `${providerId}/${model.id}`,
|
||||
name: model.name,
|
||||
provider: providerId,
|
||||
supportedSizes: model.supportedSizes || config.supportedSizes,
|
||||
supportedSizes: config.supportedSizes,
|
||||
inputModalities: model.inputModalities || ["text"],
|
||||
description: model.description || undefined,
|
||||
mediaCapabilities: model.mediaCapabilities,
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ import { digitaloceanProvider } from "./registry/digitalocean/index.ts";
|
||||
import { hcnsecProvider } from "./registry/hcnsec/index.ts";
|
||||
import { promptqlProvider } from "./registry/promptql/index.ts";
|
||||
import { hyperagentProvider } from "./registry/hyperagent/index.ts";
|
||||
import { muse_codeProvider } from "./registry/muse-code/index.ts";
|
||||
|
||||
export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
aimlapi: aimlapiProvider,
|
||||
@@ -451,5 +452,6 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
hcnsec: hcnsecProvider,
|
||||
promptql: promptqlProvider,
|
||||
hyperagent: hyperagentProvider,
|
||||
"muse-code": muse_codeProvider,
|
||||
unorouter: unorouterProvider,
|
||||
};
|
||||
|
||||
106
open-sse/config/providers/registry/muse-code/index.ts
Normal file
106
open-sse/config/providers/registry/muse-code/index.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import type { RegistryEntry } from "../../shared.ts";
|
||||
import { buildOpenAiCompatibleRegistryEntry } from "../../shared.ts";
|
||||
|
||||
/**
|
||||
* Muse Code CLI — Meta's agentic coding tool.
|
||||
*
|
||||
* Wire format: OpenAI Responses API (POST /responses).
|
||||
* Auth: Bearer token from META_API_KEY env var.
|
||||
* Reasoning efforts: xhigh/ultra -> high (handled generically).
|
||||
*
|
||||
* @see https://github.com/joymadhu49/muse-openrouter-shim
|
||||
*/
|
||||
export const muse_codeProvider: RegistryEntry = buildOpenAiCompatibleRegistryEntry({
|
||||
id: "muse-code",
|
||||
alias: "mc",
|
||||
passthroughModels: true,
|
||||
defaultContextLength: 200000,
|
||||
models: [
|
||||
{
|
||||
id: "llama-4-maverick",
|
||||
name: "Llama 4 Maverick",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
supportsReasoning: true,
|
||||
supportsXHighEffort: true,
|
||||
toolCalling: true,
|
||||
supportsVision: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs", "logitBias"],
|
||||
},
|
||||
{
|
||||
id: "llama-4-scout",
|
||||
name: "Llama 4 Scout",
|
||||
contextLength: 1048576,
|
||||
maxOutputTokens: 131072,
|
||||
supportsReasoning: true,
|
||||
supportsXHighEffort: true,
|
||||
toolCalling: true,
|
||||
supportsVision: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs", "logitBias"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.3-70b",
|
||||
name: "Llama 3.3 70B",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.1-405b",
|
||||
name: "Llama 3.1 405B",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.1-70b",
|
||||
name: "Llama 3.1 70B",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.1-8b",
|
||||
name: "Llama 3.1 8B",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.2-90b-vision",
|
||||
name: "Llama 3.2 90B Vision",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
supportsVision: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
{
|
||||
id: "llama-3.2-11b-vision",
|
||||
name: "Llama 3.2 11B Vision",
|
||||
contextLength: 131072,
|
||||
maxOutputTokens: 32768,
|
||||
supportsReasoning: false,
|
||||
toolCalling: true,
|
||||
supportsVision: true,
|
||||
targetFormat: "openai-responses",
|
||||
unsupportedParams: ["logprobs", "topLogprobs"],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -5,17 +5,14 @@
|
||||
* Supports local providers plus hosted task-based APIs such as Runway.
|
||||
*/
|
||||
|
||||
import { parseModelFromRegistry } from "./registryUtils.ts";
|
||||
import { parseModelFromRegistry, getAllModelsFromRegistry } from "./registryUtils.ts";
|
||||
import { RUNWAYML_SUPPORTED_VIDEO_MODELS } from "./runway.ts";
|
||||
import { SEGMIND_VIDEO_MODELS } from "./providers/registry/segmind/videoModels.ts";
|
||||
import { toRegistryVideoModels } from "../services/adobeFireflyModels.ts";
|
||||
|
||||
interface VideoModel {
|
||||
id: string;
|
||||
name: string;
|
||||
isMarket?: boolean;
|
||||
supportedSizes?: string[];
|
||||
mediaCapabilities?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface VideoProvider {
|
||||
@@ -329,7 +326,8 @@ export const VIDEO_PROVIDERS: Record<string, VideoProvider> = {
|
||||
},
|
||||
|
||||
// Adobe Firefly (unofficial) — same IMS/cookie credential as the image entry.
|
||||
// Exact async video models and capabilities from the verified discovery snapshot.
|
||||
// Async 3P video generate + poll (Sora 2, Veo 3.1, Kling …). Fallback list
|
||||
// from models/discovery capture (adobe/get_models.txt).
|
||||
"adobe-firefly": {
|
||||
id: "adobe-firefly",
|
||||
alias: "firefly",
|
||||
@@ -337,7 +335,18 @@ export const VIDEO_PROVIDERS: Record<string, VideoProvider> = {
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
format: "adobe-firefly-video",
|
||||
models: toRegistryVideoModels(),
|
||||
models: [
|
||||
{ id: "sora-2", name: "Firefly Sora 2" },
|
||||
{ id: "sora-2-pro", name: "Firefly Sora 2 Pro" },
|
||||
{ id: "veo-3.1", name: "Firefly Veo 3.1" },
|
||||
{ id: "veo-3.1-fast", name: "Firefly Veo 3.1 Fast" },
|
||||
{ id: "veo-3.1-ref", name: "Firefly Veo 3.1 Reference" },
|
||||
{ id: "kling-3", name: "Firefly Kling v3 Standard I2V" },
|
||||
{ id: "kling-v3-t2v", name: "Firefly Kling v3 Standard T2V" },
|
||||
{ id: "kling-v3-pro-i2v", name: "Firefly Kling v3 Pro I2V" },
|
||||
{ id: "luma-ray3", name: "Firefly Ray3" },
|
||||
{ id: "runway-gen4-turbo", name: "Firefly Runway Gen-4 Video" },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -359,17 +368,5 @@ export function parseVideoModel(modelStr: string | null) {
|
||||
* Get all video models as a flat list
|
||||
*/
|
||||
export function getAllVideoModels() {
|
||||
return Object.entries(VIDEO_PROVIDERS).flatMap(([providerId, config]) =>
|
||||
[providerId, config.alias]
|
||||
.filter((prefix): prefix is string => Boolean(prefix))
|
||||
.flatMap((prefix) =>
|
||||
config.models.map((model) => ({
|
||||
id: `${prefix}/${model.id}`,
|
||||
name: model.name,
|
||||
provider: providerId,
|
||||
supportedSizes: model.supportedSizes || [],
|
||||
mediaCapabilities: model.mediaCapabilities,
|
||||
}))
|
||||
)
|
||||
);
|
||||
return getAllModelsFromRegistry(VIDEO_PROVIDERS);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import {
|
||||
AdobeFireflyError,
|
||||
adobeFireflyGenerateImage,
|
||||
adobeFireflyImageTimeoutMs,
|
||||
adobeFireflyMaxImageRefs,
|
||||
resolveAdobeAccessToken,
|
||||
resolveAdobeSourceImageReferences,
|
||||
resolveAdobeSourceImageIds,
|
||||
resolveAdobeImageModel,
|
||||
} from "../../../services/adobeFireflyClient.ts";
|
||||
import { getAdobeReferenceUploadLimit } from "../../../services/adobeFireflyModels.ts";
|
||||
import { isAdobeFireflyUpscaleModel } from "../../../services/adobeFireflyUpscale.ts";
|
||||
import { handleAdobeFireflyImageUpscale } from "../../imageUpscale/adobeFirefly.ts";
|
||||
|
||||
@@ -90,8 +90,7 @@ export async function handleAdobeFireflyImageGeneration({
|
||||
|
||||
// Keep the raw credential blob for Cookie + sherlockToken (x-arp-session-id).
|
||||
// JWT may be embedded in the same paste as cookies (HAR / multi-line).
|
||||
const psd = (credentials as { providerSpecificData?: { cookie?: string } })
|
||||
?.providerSpecificData;
|
||||
const psd = (credentials as { providerSpecificData?: { cookie?: string } })?.providerSpecificData;
|
||||
const sessionCookie =
|
||||
(typeof psd?.cookie === "string" && psd.cookie.trim()) ||
|
||||
(typeof credentials?.apiKey === "string" && credentials.apiKey.trim()) ||
|
||||
@@ -99,11 +98,15 @@ export async function handleAdobeFireflyImageGeneration({
|
||||
? credentials.accessToken
|
||||
: undefined);
|
||||
|
||||
const { spec } = resolveAdobeImageModel(model);
|
||||
const references = await resolveAdobeSourceImageReferences({
|
||||
// Cap uploads by model family. gpt-image: 2 subject refs max (3–4+ stalls colligo → 504).
|
||||
// nano: 4 general refs for multi-panel composition.
|
||||
const { id: resolvedId } = resolveAdobeImageModel(model);
|
||||
const maxRefs = adobeFireflyMaxImageRefs(resolvedId);
|
||||
|
||||
const sourceImageIds = await resolveAdobeSourceImageIds({
|
||||
accessToken,
|
||||
body,
|
||||
max: getAdobeReferenceUploadLimit(spec, "image"),
|
||||
max: maxRefs,
|
||||
sessionCookie,
|
||||
prompt,
|
||||
fetchImpl,
|
||||
@@ -118,13 +121,13 @@ export async function handleAdobeFireflyImageGeneration({
|
||||
: undefined;
|
||||
const timeoutMs = adobeFireflyImageTimeoutMs({
|
||||
timeoutMs: explicitTimeout,
|
||||
refCount: references.length,
|
||||
refCount: sourceImageIds.length,
|
||||
});
|
||||
|
||||
log?.info?.(
|
||||
"IMAGE",
|
||||
`${provider}/${model} (adobe-firefly) | prompt: "${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}"` +
|
||||
(references.length ? ` | refs: ${references.length}` : "") +
|
||||
(sourceImageIds.length ? ` | refs: ${sourceImageIds.length}/${maxRefs}` : "") +
|
||||
` | pollTimeoutMs=${timeoutMs}`
|
||||
);
|
||||
|
||||
@@ -136,8 +139,9 @@ export async function handleAdobeFireflyImageGeneration({
|
||||
aspectRatio: body.aspect_ratio ?? body.aspectRatio ?? body.size,
|
||||
quality: body.quality,
|
||||
seed: Number.isFinite(seed as number) ? (seed as number) : undefined,
|
||||
negativePrompt: typeof body.negative_prompt === "string" ? body.negative_prompt : undefined,
|
||||
references: references.length ? references : undefined,
|
||||
negativePrompt:
|
||||
typeof body.negative_prompt === "string" ? body.negative_prompt : undefined,
|
||||
sourceImageIds: sourceImageIds.length ? sourceImageIds : undefined,
|
||||
sessionCookie,
|
||||
timeoutMs,
|
||||
fetchImpl,
|
||||
|
||||
@@ -10,10 +10,9 @@ import {
|
||||
AdobeFireflyError,
|
||||
adobeFireflyGenerateVideo,
|
||||
resolveAdobeAccessToken,
|
||||
resolveAdobeSourceImageReferences,
|
||||
resolveAdobeSourceImageIds,
|
||||
resolveAdobeVideoModel,
|
||||
} from "../../services/adobeFireflyClient.ts";
|
||||
import { getAdobeReferenceUploadLimit } from "../../services/adobeFireflyModels.ts";
|
||||
|
||||
function normalizePositiveNumber(value: unknown, fallback: number): number {
|
||||
const n = Number(value);
|
||||
@@ -56,8 +55,7 @@ export async function handleAdobeFireflyVideoGeneration({
|
||||
? Number(body.seed)
|
||||
: undefined;
|
||||
// Keep raw paste for Cookie + sherlockToken (x-arp-session-id).
|
||||
const psd = (credentials as { providerSpecificData?: { cookie?: string } })
|
||||
?.providerSpecificData;
|
||||
const psd = (credentials as { providerSpecificData?: { cookie?: string } })?.providerSpecificData;
|
||||
const sessionCookie =
|
||||
(typeof psd?.cookie === "string" && psd.cookie.trim()) ||
|
||||
(typeof credentials?.apiKey === "string" && credentials.apiKey.trim()) ||
|
||||
@@ -65,11 +63,13 @@ export async function handleAdobeFireflyVideoGeneration({
|
||||
? credentials.accessToken
|
||||
: undefined);
|
||||
|
||||
const { spec } = resolveAdobeVideoModel(String(model));
|
||||
const references = await resolveAdobeSourceImageReferences({
|
||||
// Kling i2v / Veo ref / Sora frame: upload reference images first.
|
||||
const { id: videoModelId } = resolveAdobeVideoModel(String(model));
|
||||
const maxFrames = videoModelId.includes("kling") || videoModelId.includes("sora") ? 2 : 3;
|
||||
const sourceImageIds = await resolveAdobeSourceImageIds({
|
||||
accessToken,
|
||||
body,
|
||||
max: getAdobeReferenceUploadLimit(spec, "image"),
|
||||
max: maxFrames,
|
||||
sessionCookie,
|
||||
prompt,
|
||||
fetchImpl,
|
||||
@@ -79,7 +79,7 @@ export async function handleAdobeFireflyVideoGeneration({
|
||||
log?.info?.(
|
||||
"VIDEO",
|
||||
`${provider}/${model} (adobe-firefly) | prompt: "${prompt.slice(0, 60)}${prompt.length > 60 ? "..." : ""}"` +
|
||||
(references.length ? ` | refs: ${references.length}` : "")
|
||||
(sourceImageIds.length ? ` | frames: ${sourceImageIds.length}` : "")
|
||||
);
|
||||
|
||||
const result = await adobeFireflyGenerateVideo({
|
||||
@@ -99,7 +99,7 @@ export async function handleAdobeFireflyVideoGeneration({
|
||||
? body.negativePrompt
|
||||
: undefined,
|
||||
generateAudio: body.generate_audio !== false && body.generateAudio !== false,
|
||||
references: references.length ? references : undefined,
|
||||
sourceImageIds: sourceImageIds.length ? sourceImageIds : undefined,
|
||||
sessionCookie,
|
||||
timeoutMs,
|
||||
fetchImpl,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,590 +1,328 @@
|
||||
/**
|
||||
* Adobe Firefly model discovery and normalized media capabilities.
|
||||
* Adobe Firefly model catalog: live discovery + static fallback from browser capture.
|
||||
*
|
||||
* The live discovery schema is authoritative. The generated snapshot is used only
|
||||
* when a request cannot perform authenticated discovery (for example /v1/models).
|
||||
* Live: POST firefly-3p.ff.adobe.io/v2/models/discovery (needs valid IMS token).
|
||||
* Fallback: curated rows from adobe/get_models.txt (2026-07 Firefly SPA capture) so
|
||||
* Media/Models still list usable ids when discovery fails or credentials are missing.
|
||||
*/
|
||||
|
||||
import { ADOBE_FIREFLY_DISCOVERY_SNAPSHOT } from "./adobeFireflyModelSnapshot.ts";
|
||||
|
||||
export type AdobeFireflyModality = "image" | "video" | "audio" | "unknown";
|
||||
|
||||
export interface AdobeFireflyDiscoveredModel {
|
||||
modelId: string;
|
||||
modelVersion: string;
|
||||
displayName: string;
|
||||
modality: AdobeFireflyModality;
|
||||
enabled: boolean;
|
||||
providerName?: string;
|
||||
releaseReadiness?: string;
|
||||
healthStatus?: string;
|
||||
inputMediaUseCases: string[];
|
||||
requestSchema?: Record<string, unknown>;
|
||||
backingModel?: string;
|
||||
}
|
||||
|
||||
export interface AdobeFireflyReferenceInputCapability {
|
||||
mediaType: string;
|
||||
usageType: string;
|
||||
minItems: number;
|
||||
maxItems: number | null;
|
||||
maxFileSizeBytes: number | null;
|
||||
}
|
||||
|
||||
export interface AdobeFireflyMediaCapabilities {
|
||||
inputMediaUseCases: string[];
|
||||
schemaProperties: string[];
|
||||
requiredProperties: string[];
|
||||
referenceInputs: AdobeFireflyReferenceInputCapability[];
|
||||
maxReferenceItems: number | null;
|
||||
supportedSizes: string[];
|
||||
supportedAspectRatios: string[];
|
||||
supportedResolutions: string[];
|
||||
supportedDurations: number[];
|
||||
durationMin: number | null;
|
||||
durationMax: number | null;
|
||||
durationDefault: number | null;
|
||||
outputCountMin: number | null;
|
||||
outputCountMax: number | null;
|
||||
promptMaxLength: number | null;
|
||||
releaseReadiness: string;
|
||||
healthStatus: string;
|
||||
}
|
||||
import {
|
||||
type AdobeFireflyDiscoveredModel,
|
||||
discoverAdobeFireflyModels,
|
||||
resolveAdobeAccessToken,
|
||||
} from "./adobeFireflyClient.ts";
|
||||
|
||||
export interface AdobeFireflyCatalogModel {
|
||||
/** Stable API id without the provider prefix. */
|
||||
/** OpenAI-style id without provider prefix, e.g. nano-banana-pro or flux-fluxPro */
|
||||
id: string;
|
||||
name: string;
|
||||
modality: "image" | "video";
|
||||
/** Upstream wire modelId for generate-async */
|
||||
upstreamModelId: string;
|
||||
/** Upstream wire modelVersion for generate-async */
|
||||
upstreamModelVersion: string;
|
||||
providerName: string;
|
||||
backingModel: string;
|
||||
inputModalities: string[];
|
||||
capabilities: AdobeFireflyMediaCapabilities;
|
||||
inputModalities?: string[];
|
||||
}
|
||||
|
||||
export interface AdobeFireflyImageModelSpec extends AdobeFireflyCatalogModel {
|
||||
modality: "image";
|
||||
/** Payload dialect observed for this model family. */
|
||||
family: "gemini" | "gpt-image" | "generic";
|
||||
}
|
||||
/**
|
||||
* Static fallback built from adobe/get_models.txt discovery response.
|
||||
* Friendly aliases first (Media page defaults), then popular upstream families.
|
||||
*/
|
||||
export const ADOBE_FIREFLY_FALLBACK_MODELS: AdobeFireflyCatalogModel[] = [
|
||||
// ── Friendly aliases (handler resolveAdobeImageModel / resolveAdobeVideoModel) ──
|
||||
{
|
||||
id: "nano-banana-pro",
|
||||
name: "Gemini 3.0 (Nano Banana Pro)",
|
||||
modality: "image",
|
||||
upstreamModelId: "gemini-flash",
|
||||
upstreamModelVersion: "nano-banana-2",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "nano-banana",
|
||||
name: "Gemini 2.5 (Nano Banana)",
|
||||
modality: "image",
|
||||
upstreamModelId: "gemini-flash",
|
||||
upstreamModelVersion: "nano-banana",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "nano-banana-2",
|
||||
name: "Gemini 3.1 (Nano Banana 2)",
|
||||
modality: "image",
|
||||
upstreamModelId: "gemini-flash",
|
||||
upstreamModelVersion: "nano-banana-3",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "gpt-image-2",
|
||||
name: "GPT Image 2",
|
||||
modality: "image",
|
||||
upstreamModelId: "gpt-image",
|
||||
upstreamModelVersion: "2",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "gpt-image",
|
||||
name: "GPT Image 2",
|
||||
modality: "image",
|
||||
upstreamModelId: "gpt-image",
|
||||
upstreamModelVersion: "2",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "gpt-image-1.5",
|
||||
name: "GPT Image 1.5",
|
||||
modality: "image",
|
||||
upstreamModelId: "gpt-image",
|
||||
upstreamModelVersion: "1.5",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "sora-2",
|
||||
name: "Sora 2",
|
||||
modality: "video",
|
||||
upstreamModelId: "sora",
|
||||
upstreamModelVersion: "sora-2",
|
||||
},
|
||||
{
|
||||
id: "sora-2-pro",
|
||||
name: "Sora 2 Pro",
|
||||
modality: "video",
|
||||
upstreamModelId: "sora",
|
||||
upstreamModelVersion: "sora-2-pro",
|
||||
},
|
||||
{
|
||||
id: "veo-3.1",
|
||||
name: "Veo 3.1",
|
||||
modality: "video",
|
||||
upstreamModelId: "veo",
|
||||
upstreamModelVersion: "3.1-generate",
|
||||
},
|
||||
{
|
||||
id: "veo-3.1-fast",
|
||||
name: "Veo 3.1 Fast",
|
||||
modality: "video",
|
||||
upstreamModelId: "veo",
|
||||
upstreamModelVersion: "3.1-fast-generate",
|
||||
},
|
||||
{
|
||||
id: "veo-3.1-ref",
|
||||
name: "Veo 3.1 Reference",
|
||||
modality: "video",
|
||||
upstreamModelId: "veo",
|
||||
upstreamModelVersion: "3.1-generate",
|
||||
},
|
||||
{
|
||||
id: "kling-3",
|
||||
name: "Kling Video v3 Standard Image to Video",
|
||||
modality: "video",
|
||||
upstreamModelId: "kling",
|
||||
upstreamModelVersion: "kling_v3_standard_i2v",
|
||||
},
|
||||
// ── Additional image families from discovery capture ──
|
||||
{
|
||||
id: "flux-2",
|
||||
name: "Flux 2",
|
||||
modality: "image",
|
||||
upstreamModelId: "flux",
|
||||
upstreamModelVersion: "2",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "flux-pro",
|
||||
name: "Flux 1.1 Pro",
|
||||
modality: "image",
|
||||
upstreamModelId: "flux",
|
||||
upstreamModelVersion: "fluxPro",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "flux-ultra",
|
||||
name: "Flux 1.1 Ultra",
|
||||
modality: "image",
|
||||
upstreamModelId: "flux",
|
||||
upstreamModelVersion: "fluxUltra",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "seedream-4",
|
||||
name: "Seedream 4.0",
|
||||
modality: "image",
|
||||
upstreamModelId: "seedream",
|
||||
upstreamModelVersion: "seedream_v4",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "seedream-5-lite",
|
||||
name: "Seedream 5.0 Lite",
|
||||
modality: "image",
|
||||
upstreamModelId: "seedream",
|
||||
upstreamModelVersion: "seedream_v5_lite",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
{
|
||||
id: "runway-gen4-image",
|
||||
name: "Runway Gen-4 Image",
|
||||
modality: "image",
|
||||
upstreamModelId: "runway-gen4-image",
|
||||
upstreamModelVersion: "gen4_image",
|
||||
inputModalities: ["text", "image"],
|
||||
},
|
||||
// ── Additional video families ──
|
||||
{
|
||||
id: "kling-v3-t2v",
|
||||
name: "Kling Video v3 Standard Text to Video",
|
||||
modality: "video",
|
||||
upstreamModelId: "kling",
|
||||
upstreamModelVersion: "kling_v3_standard_t2v",
|
||||
},
|
||||
{
|
||||
id: "kling-v3-pro-i2v",
|
||||
name: "Kling Video v3 Pro Image to Video",
|
||||
modality: "video",
|
||||
upstreamModelId: "kling",
|
||||
upstreamModelVersion: "kling_v3_pro_i2v",
|
||||
},
|
||||
{
|
||||
id: "luma-ray3",
|
||||
name: "Ray3",
|
||||
modality: "video",
|
||||
upstreamModelId: "luma",
|
||||
upstreamModelVersion: "3.0-ray",
|
||||
},
|
||||
{
|
||||
id: "runway-gen4-turbo",
|
||||
name: "Runway Gen-4 Video",
|
||||
modality: "video",
|
||||
upstreamModelId: "runway",
|
||||
upstreamModelVersion: "gen4_turbo",
|
||||
},
|
||||
];
|
||||
|
||||
export interface AdobeFireflyVideoModelSpec extends AdobeFireflyCatalogModel {
|
||||
modality: "video";
|
||||
defaultDuration: number;
|
||||
defaultResolution: string;
|
||||
}
|
||||
|
||||
interface MergedObjectSchema {
|
||||
properties: Record<string, Record<string, unknown>>;
|
||||
required: string[];
|
||||
}
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: {};
|
||||
}
|
||||
|
||||
function asStringArray(value: unknown): string[] {
|
||||
return Array.isArray(value)
|
||||
? value.map((item) => String(item)).filter((item) => item.length > 0)
|
||||
: [];
|
||||
}
|
||||
|
||||
function finiteInteger(value: unknown): number | null {
|
||||
return Number.isInteger(value) ? (value as number) : null;
|
||||
}
|
||||
|
||||
/** Merge object properties/required keys contributed through JSON Schema allOf. */
|
||||
export function mergeAdobeObjectSchema(schema: unknown): MergedObjectSchema {
|
||||
const merged: MergedObjectSchema = { properties: {}, required: [] };
|
||||
const visit = (value: unknown) => {
|
||||
const node = asRecord(value);
|
||||
const properties = asRecord(node.properties);
|
||||
for (const [key, property] of Object.entries(properties)) {
|
||||
merged.properties[key] = asRecord(property);
|
||||
}
|
||||
merged.required.push(...asStringArray(node.required));
|
||||
if (Array.isArray(node.allOf)) node.allOf.forEach(visit);
|
||||
};
|
||||
visit(schema);
|
||||
merged.required = [...new Set(merged.required)];
|
||||
return merged;
|
||||
}
|
||||
|
||||
function schemaBranches(schema: unknown): Record<string, unknown>[] {
|
||||
const root = asRecord(schema);
|
||||
if (Object.keys(root).length === 0) return [];
|
||||
return [
|
||||
root,
|
||||
...(Array.isArray(root.anyOf) ? root.anyOf.map(asRecord) : []),
|
||||
...(Array.isArray(root.oneOf) ? root.oneOf.map(asRecord) : []),
|
||||
];
|
||||
}
|
||||
|
||||
function enumStrings(schema: unknown): string[] {
|
||||
return [
|
||||
...new Set(
|
||||
schemaBranches(schema)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.filter((value): value is string => typeof value === "string")
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
function integerBranch(schema: unknown): Record<string, unknown> {
|
||||
return schemaBranches(schema).find((branch) => branch.type === "integer") || {};
|
||||
}
|
||||
|
||||
/** Stable, collision-resistant public id for an exact upstream model/version pair. */
|
||||
/** Stable slug for upstream modelId + modelVersion (catalog id when not a friendly alias). */
|
||||
export function slugifyAdobeModel(modelId: string, modelVersion: string): string {
|
||||
const slug = (value: string, allowDot = false) =>
|
||||
String(value || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(allowDot ? /[^a-z0-9.]+/g : /[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
const family = slug(modelId);
|
||||
// Adobe still uses `kling_v3_omni*` internally, while discovery exposes these
|
||||
// products to users as Kling O3. Never leak the obsolete/internal "omni" name
|
||||
// into the public API catalog; the untouched upstream version stays in the spec.
|
||||
const publicVersion =
|
||||
family === "kling" ? modelVersion.replace(/^kling_v3_omni/i, "kling_o3") : modelVersion;
|
||||
const version = slug(publicVersion, true);
|
||||
if (!version || version === "default" || version === family) return family || "model";
|
||||
return `${family}-${version}`;
|
||||
const mid = String(modelId || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
const ver = String(modelVersion || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9.]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
if (!ver || ver === "default" || ver === mid) return mid || "model";
|
||||
return `${mid}-${ver}`;
|
||||
}
|
||||
|
||||
/** Parse POST /v2/models/discovery without discarding its resolved request schema. */
|
||||
export function parseAdobeModelsDiscovery(body: unknown): AdobeFireflyDiscoveredModel[] {
|
||||
const root = asRecord(body);
|
||||
const families = Array.isArray(root.models) ? root.models : [];
|
||||
const rows: AdobeFireflyDiscoveredModel[] = [];
|
||||
|
||||
for (const familyValue of families) {
|
||||
const family = asRecord(familyValue);
|
||||
const modelId = String(family.modelId || "").trim();
|
||||
if (!modelId) continue;
|
||||
for (const [modelVersion, versionValue] of Object.entries(asRecord(family.modelVersions))) {
|
||||
const version = asRecord(versionValue);
|
||||
if (version.enabled === false) continue;
|
||||
const outputModalities = asStringArray(version.outputModality).map((item) =>
|
||||
item.toLowerCase()
|
||||
);
|
||||
const modality: AdobeFireflyModality = outputModalities.includes("image")
|
||||
? "image"
|
||||
: outputModalities.includes("video")
|
||||
? "video"
|
||||
: outputModalities.includes("audio")
|
||||
? "audio"
|
||||
: "unknown";
|
||||
rows.push({
|
||||
modelId,
|
||||
modelVersion,
|
||||
displayName: String(
|
||||
version.modelDisplayName || version.modelCaiDisplayName || modelVersion
|
||||
),
|
||||
modality,
|
||||
enabled: version.enabled !== false,
|
||||
providerName:
|
||||
typeof family.acModelFamilyProviderDisplayName === "string"
|
||||
? family.acModelFamilyProviderDisplayName
|
||||
: undefined,
|
||||
releaseReadiness:
|
||||
typeof version.releaseReadiness === "string" ? version.releaseReadiness : undefined,
|
||||
healthStatus: typeof version.healthStatus === "string" ? version.healthStatus : undefined,
|
||||
inputMediaUseCases: asStringArray(version.inputMediaUseCase),
|
||||
requestSchema: asRecord(version.requestSchema),
|
||||
backingModel:
|
||||
typeof version.bksGenerationModel === "string" ? version.bksGenerationModel : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function normalizeCapabilities(row: AdobeFireflyDiscoveredModel): AdobeFireflyMediaCapabilities {
|
||||
const schema = mergeAdobeObjectSchema(row.requestSchema);
|
||||
const referenceSchema = asRecord(schema.properties.referenceBlobs);
|
||||
const referenceInputs: AdobeFireflyReferenceInputCapability[] = [];
|
||||
const mediaCapabilities = Array.isArray(referenceSchema["x-capabilities"])
|
||||
? referenceSchema["x-capabilities"]
|
||||
: [];
|
||||
for (const mediaValue of mediaCapabilities) {
|
||||
const media = asRecord(mediaValue);
|
||||
const maxFileSizeBytes = finiteInteger(media.maxFileSizeBytes);
|
||||
const usageConstraints = Array.isArray(media.usageConstraints) ? media.usageConstraints : [];
|
||||
for (const usageValue of usageConstraints) {
|
||||
const usage = asRecord(usageValue);
|
||||
if (usage.deprecated === true) continue;
|
||||
const usageType = String(usage.usageType || "");
|
||||
const mediaType = String(media.mediaType || "");
|
||||
if (!usageType || !mediaType) continue;
|
||||
referenceInputs.push({
|
||||
mediaType,
|
||||
usageType,
|
||||
minItems: finiteInteger(usage.minItems) ?? 0,
|
||||
maxItems: finiteInteger(usage.maxItems),
|
||||
maxFileSizeBytes,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const supportedSizes = [
|
||||
...new Set(
|
||||
schemaBranches(schema.properties.size)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.map(asRecord)
|
||||
.filter((size) => finiteInteger(size.width) !== null && finiteInteger(size.height) !== null)
|
||||
.map((size) => `${size.width}x${size.height}`)
|
||||
),
|
||||
];
|
||||
const supportedAspectRatios = [
|
||||
...new Set(
|
||||
schemaBranches(schema.properties.generationSettings).flatMap((branch) =>
|
||||
enumStrings(asRecord(asRecord(branch.properties).aspectRatio))
|
||||
)
|
||||
),
|
||||
];
|
||||
const duration = integerBranch(schema.properties.duration);
|
||||
const outputCount = integerBranch(schema.properties.n);
|
||||
const prompt =
|
||||
schemaBranches(schema.properties.prompt).find((branch) => branch.type === "string") || {};
|
||||
|
||||
return {
|
||||
inputMediaUseCases: [...row.inputMediaUseCases],
|
||||
schemaProperties: Object.keys(schema.properties),
|
||||
requiredProperties: [...schema.required],
|
||||
referenceInputs,
|
||||
maxReferenceItems: finiteInteger(referenceSchema.maxItems),
|
||||
supportedSizes,
|
||||
supportedAspectRatios,
|
||||
supportedResolutions: enumStrings(schema.properties.resolution),
|
||||
supportedDurations: [
|
||||
...new Set(
|
||||
schemaBranches(schema.properties.duration)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.filter((value): value is number => Number.isInteger(value))
|
||||
),
|
||||
],
|
||||
durationMin: finiteInteger(duration.minimum),
|
||||
durationMax: finiteInteger(duration.maximum),
|
||||
durationDefault: finiteInteger(duration.default),
|
||||
outputCountMin: finiteInteger(outputCount.minimum),
|
||||
outputCountMax: finiteInteger(outputCount.maximum),
|
||||
promptMaxLength: finiteInteger(prompt.maxLength),
|
||||
releaseReadiness: row.releaseReadiness || "",
|
||||
healthStatus: row.healthStatus || "",
|
||||
};
|
||||
}
|
||||
|
||||
function isCallableGenerationModel(row: AdobeFireflyDiscoveredModel): boolean {
|
||||
if (row.modality !== "image" && row.modality !== "video") return false;
|
||||
if (!mergeAdobeObjectSchema(row.requestSchema).properties.prompt) return false;
|
||||
const excluded = new Set(["upscaling", "sharpening", "denoising"]);
|
||||
return !row.inputMediaUseCases.some((value) => excluded.has(value.toLowerCase()));
|
||||
}
|
||||
|
||||
function deriveInputModalities(capabilities: AdobeFireflyMediaCapabilities): string[] {
|
||||
return ["text", ...new Set(capabilities.referenceInputs.map((reference) => reference.mediaType))];
|
||||
}
|
||||
|
||||
function semanticCatalogKey(model: AdobeFireflyCatalogModel): string {
|
||||
return JSON.stringify({
|
||||
backingModel: model.backingModel,
|
||||
name: model.name,
|
||||
modality: model.modality,
|
||||
capabilities: model.capabilities,
|
||||
});
|
||||
}
|
||||
|
||||
/** Normalize and de-duplicate callable image/video rows from live discovery. */
|
||||
/** Map discovery rows → catalog entries (image/video only). */
|
||||
export function mapDiscoveredToCatalog(
|
||||
rows: AdobeFireflyDiscoveredModel[]
|
||||
): AdobeFireflyCatalogModel[] {
|
||||
const output: AdobeFireflyCatalogModel[] = [];
|
||||
const out: AdobeFireflyCatalogModel[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const row of rows) {
|
||||
if (!isCallableGenerationModel(row)) continue;
|
||||
const capabilities = normalizeCapabilities(row);
|
||||
const model: AdobeFireflyCatalogModel = {
|
||||
id: slugifyAdobeModel(row.modelId, row.modelVersion),
|
||||
name: row.displayName,
|
||||
modality: row.modality as "image" | "video",
|
||||
upstreamModelId: row.modelId,
|
||||
upstreamModelVersion: row.modelVersion,
|
||||
providerName: row.providerName || "",
|
||||
backingModel: row.backingModel || "",
|
||||
inputModalities: deriveInputModalities(capabilities),
|
||||
capabilities,
|
||||
};
|
||||
const key = semanticCatalogKey(model);
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
output.push(model);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function snapshotCatalog(): AdobeFireflyCatalogModel[] {
|
||||
return ADOBE_FIREFLY_DISCOVERY_SNAPSHOT.map((model) => {
|
||||
const capabilities: AdobeFireflyMediaCapabilities = {
|
||||
inputMediaUseCases: [...model.inputMediaUseCases],
|
||||
schemaProperties: [...model.schemaProperties],
|
||||
requiredProperties: [...model.requiredProperties],
|
||||
referenceInputs: model.referenceInputs.map((reference) => ({ ...reference })),
|
||||
maxReferenceItems: model.maxReferenceItems,
|
||||
supportedSizes: [...model.supportedSizes],
|
||||
supportedAspectRatios: [...model.supportedAspectRatios],
|
||||
supportedResolutions: [...model.supportedResolutions],
|
||||
supportedDurations: [...model.supportedDurations],
|
||||
durationMin: model.durationMin,
|
||||
durationMax: model.durationMax,
|
||||
durationDefault: model.durationDefault,
|
||||
outputCountMin: model.outputCountMin,
|
||||
outputCountMax: model.outputCountMax,
|
||||
promptMaxLength: model.promptMaxLength,
|
||||
releaseReadiness: model.releaseReadiness,
|
||||
healthStatus: model.healthStatus,
|
||||
};
|
||||
return {
|
||||
id: model.id,
|
||||
name: model.name,
|
||||
modality: model.modality,
|
||||
upstreamModelId: model.upstreamModelId,
|
||||
upstreamModelVersion: model.upstreamModelVersion,
|
||||
providerName: model.providerName,
|
||||
backingModel: model.backingModel,
|
||||
inputModalities: deriveInputModalities(capabilities),
|
||||
capabilities,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export const ADOBE_FIREFLY_FALLBACK_MODELS: AdobeFireflyCatalogModel[] = snapshotCatalog();
|
||||
|
||||
export function getAdobeFireflyFallbackCatalog(
|
||||
modality?: "image" | "video"
|
||||
): AdobeFireflyCatalogModel[] {
|
||||
return ADOBE_FIREFLY_FALLBACK_MODELS.filter((model) => !modality || model.modality === modality);
|
||||
}
|
||||
|
||||
function imageFamily(model: AdobeFireflyCatalogModel): AdobeFireflyImageModelSpec["family"] {
|
||||
if (model.upstreamModelId === "gemini-flash") return "gemini";
|
||||
if (model.upstreamModelId === "gpt-image" || model.upstreamModelId === "gpt-4o-image") {
|
||||
return "gpt-image";
|
||||
}
|
||||
return "generic";
|
||||
}
|
||||
|
||||
export const ADOBE_FIREFLY_IMAGE_MODELS: Record<string, AdobeFireflyImageModelSpec> =
|
||||
Object.fromEntries(
|
||||
getAdobeFireflyFallbackCatalog("image").map((model) => [
|
||||
model.id,
|
||||
{ ...model, modality: "image" as const, family: imageFamily(model) },
|
||||
])
|
||||
);
|
||||
|
||||
function defaultDuration(model: AdobeFireflyCatalogModel): number {
|
||||
const caps = model.capabilities;
|
||||
return caps.durationDefault ?? caps.supportedDurations[0] ?? caps.durationMin ?? 5;
|
||||
}
|
||||
|
||||
function defaultResolution(model: AdobeFireflyCatalogModel): string {
|
||||
if (model.capabilities.supportedSizes.some((value) => value.includes("1920x1080"))) {
|
||||
return "1080p";
|
||||
}
|
||||
return "720p";
|
||||
}
|
||||
|
||||
export const ADOBE_FIREFLY_VIDEO_MODELS: Record<string, AdobeFireflyVideoModelSpec> =
|
||||
Object.fromEntries(
|
||||
getAdobeFireflyFallbackCatalog("video").map((model) => [
|
||||
model.id,
|
||||
{
|
||||
...model,
|
||||
modality: "video" as const,
|
||||
defaultDuration: defaultDuration(model),
|
||||
defaultResolution: defaultResolution(model),
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
const LEGACY_MODEL_ALIASES: Record<string, string> = {
|
||||
"nano-banana": "gemini-flash-nano-banana",
|
||||
"nano-banana-pro": "gemini-flash-nano-banana-2",
|
||||
"nano-banana-2": "gemini-flash-nano-banana-3",
|
||||
"gpt-image": "gpt-image-2",
|
||||
"gpt-image-2": "gpt-image-2",
|
||||
"gpt-image-1.5": "gpt-image-1.5",
|
||||
"flux-2": "flux-2",
|
||||
"flux-pro": "flux-fluxpro",
|
||||
"flux-ultra": "flux-fluxultra",
|
||||
"seedream-4": "seedream-seedream-v4",
|
||||
"seedream-5-lite": "seedream-seedream-v5-lite",
|
||||
"runway-gen4-image": "runway-gen4-image",
|
||||
"veo-3.1": "veo-3.1-generate",
|
||||
"veo-3.1-fast": "veo-3.1-fast-generate",
|
||||
"luma-ray3": "luma-3.0-ray",
|
||||
"runway-gen4-turbo": "runway-gen4-turbo",
|
||||
// Backward compatibility only; the catalog advertises the exact discovered id.
|
||||
"kling-3": "kling-kling-v3-standard-i2v",
|
||||
};
|
||||
|
||||
// Preserve established API aliases when (and only when) they resolve to a model
|
||||
// that is present in the verified discovery snapshot. These keys are not listed.
|
||||
for (const [alias, target] of Object.entries(LEGACY_MODEL_ALIASES)) {
|
||||
const imageTarget = ADOBE_FIREFLY_IMAGE_MODELS[target];
|
||||
if (imageTarget) ADOBE_FIREFLY_IMAGE_MODELS[alias] = imageTarget;
|
||||
const videoTarget = ADOBE_FIREFLY_VIDEO_MODELS[target];
|
||||
if (videoTarget) ADOBE_FIREFLY_VIDEO_MODELS[alias] = videoTarget;
|
||||
}
|
||||
|
||||
/** Backward-compatible request ids. Kept out of every advertised model catalog. */
|
||||
export const ADOBE_FIREFLY_IMAGE_ROUTING_ALIASES = Object.freeze(
|
||||
Object.entries(LEGACY_MODEL_ALIASES)
|
||||
.filter(([, target]) => Boolean(ADOBE_FIREFLY_IMAGE_MODELS[target]))
|
||||
.map(([alias]) => alias)
|
||||
);
|
||||
|
||||
function normalizeRequestedId(model: string): string {
|
||||
return String(model || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/^adobe-firefly\//, "")
|
||||
.replace(/^firefly\//, "");
|
||||
}
|
||||
|
||||
function resolveCatalogId(model: string): string {
|
||||
const requested = normalizeRequestedId(model);
|
||||
return LEGACY_MODEL_ALIASES[requested] || requested;
|
||||
}
|
||||
|
||||
export function resolveAdobeImageModel(model: string): {
|
||||
id: string;
|
||||
spec: AdobeFireflyImageModelSpec;
|
||||
} {
|
||||
const id = resolveCatalogId(model);
|
||||
const spec = ADOBE_FIREFLY_IMAGE_MODELS[id];
|
||||
if (!spec) {
|
||||
throw new Error(
|
||||
`Unknown Adobe Firefly image model: ${normalizeRequestedId(model) || "(empty)"}`
|
||||
// Prefer friendly aliases when upstream matches known fallback rows.
|
||||
for (const fb of ADOBE_FIREFLY_FALLBACK_MODELS) {
|
||||
const hit = rows.find(
|
||||
(r) =>
|
||||
r.modelId === fb.upstreamModelId &&
|
||||
r.modelVersion === fb.upstreamModelVersion &&
|
||||
(r.modality === fb.modality || r.modality === "unknown")
|
||||
);
|
||||
if (hit && !seen.has(fb.id)) {
|
||||
seen.add(fb.id);
|
||||
out.push({
|
||||
...fb,
|
||||
name: hit.displayName || fb.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
return { id, spec };
|
||||
}
|
||||
|
||||
export function resolveAdobeVideoModel(model: string): {
|
||||
id: string;
|
||||
spec: AdobeFireflyVideoModelSpec;
|
||||
} {
|
||||
const id = resolveCatalogId(model);
|
||||
const spec = ADOBE_FIREFLY_VIDEO_MODELS[id];
|
||||
if (!spec) {
|
||||
throw new Error(
|
||||
`Unknown Adobe Firefly video model: ${normalizeRequestedId(model) || "(empty)"}`
|
||||
);
|
||||
for (const r of rows) {
|
||||
if (r.modality !== "image" && r.modality !== "video") continue;
|
||||
const id = slugifyAdobeModel(r.modelId, r.modelVersion);
|
||||
if (seen.has(id)) continue;
|
||||
// Skip if already covered by a friendly alias with same upstream
|
||||
if (
|
||||
out.some(
|
||||
(o) =>
|
||||
o.upstreamModelId === r.modelId && o.upstreamModelVersion === r.modelVersion
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
seen.add(id);
|
||||
out.push({
|
||||
id,
|
||||
name: r.displayName || id,
|
||||
modality: r.modality,
|
||||
upstreamModelId: r.modelId,
|
||||
upstreamModelVersion: r.modelVersion,
|
||||
inputModalities: r.modality === "image" ? ["text", "image"] : ["text"],
|
||||
});
|
||||
}
|
||||
return { id, spec };
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
export function toRegistryImageModels(): Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
inputModalities: string[];
|
||||
imageRequired?: boolean;
|
||||
supportedSizes: string[];
|
||||
mediaCapabilities: Record<string, unknown>;
|
||||
}> {
|
||||
const generated = getAdobeFireflyFallbackCatalog("image").map((model) => ({
|
||||
id: model.id,
|
||||
name: `Firefly ${model.name}`,
|
||||
inputModalities: model.inputModalities,
|
||||
supportedSizes: model.capabilities.supportedSizes,
|
||||
mediaCapabilities: toAdobeMediaCapabilitiesApi(model),
|
||||
}));
|
||||
// Upscaling uses a distinct Firefly endpoint and is not returned by the image
|
||||
// generation discovery schema. Keep its two supported Topaz models visible in
|
||||
// the same provider catalog so image clients can select them deliberately.
|
||||
return [
|
||||
...generated,
|
||||
{
|
||||
id: "topaz-standard",
|
||||
name: "Firefly Topaz Upscale (Standard)",
|
||||
inputModalities: ["image"],
|
||||
imageRequired: true,
|
||||
supportedSizes: [],
|
||||
mediaCapabilities: { input_media_use_cases: ["upscaling"] },
|
||||
},
|
||||
{
|
||||
id: "topaz-bloom",
|
||||
name: "Firefly Topaz Bloom (Creative Upscale)",
|
||||
inputModalities: ["image"],
|
||||
imageRequired: true,
|
||||
supportedSizes: [],
|
||||
mediaCapabilities: { input_media_use_cases: ["upscaling"] },
|
||||
},
|
||||
];
|
||||
export function getAdobeFireflyFallbackCatalog(modality?: "image" | "video"): AdobeFireflyCatalogModel[] {
|
||||
if (!modality) return [...ADOBE_FIREFLY_FALLBACK_MODELS];
|
||||
return ADOBE_FIREFLY_FALLBACK_MODELS.filter((m) => m.modality === modality);
|
||||
}
|
||||
|
||||
export function toRegistryVideoModels(): Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
supportedSizes: string[];
|
||||
mediaCapabilities: Record<string, unknown>;
|
||||
}> {
|
||||
return getAdobeFireflyFallbackCatalog("video").map((model) => ({
|
||||
id: model.id,
|
||||
name: `Firefly ${model.name}`,
|
||||
supportedSizes: model.capabilities.supportedSizes,
|
||||
mediaCapabilities: toAdobeMediaCapabilitiesApi(model),
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* Live discovery when credentials resolve; otherwise static fallback from get_models capture.
|
||||
*/
|
||||
export async function resolveAdobeFireflyCatalog(opts: {
|
||||
credentials?: {
|
||||
apiKey?: string;
|
||||
accessToken?: string;
|
||||
providerSpecificData?: Record<string, unknown> | null;
|
||||
} | null;
|
||||
modality?: "image" | "video";
|
||||
fetchImpl?: typeof fetch;
|
||||
}): Promise<{ models: AdobeFireflyCatalogModel[]; source: "api" | "fallback" }> {
|
||||
const fetchImpl = opts.fetchImpl || fetch;
|
||||
try {
|
||||
if (opts.credentials) {
|
||||
const token = await resolveAdobeAccessToken(opts.credentials, fetchImpl);
|
||||
const discovered = await discoverAdobeFireflyModels(token, fetchImpl);
|
||||
let catalog = mapDiscoveredToCatalog(discovered);
|
||||
if (opts.modality) catalog = catalog.filter((m) => m.modality === opts.modality);
|
||||
if (catalog.length > 0) return { models: catalog, source: "api" };
|
||||
}
|
||||
} catch {
|
||||
// fall through to static catalog
|
||||
}
|
||||
|
||||
/** JSON-safe extension emitted by /v1/models. */
|
||||
export function toAdobeMediaCapabilitiesApi(
|
||||
model: AdobeFireflyCatalogModel
|
||||
): Record<string, unknown> {
|
||||
const caps = model.capabilities;
|
||||
return {
|
||||
upstream_model_id: model.upstreamModelId,
|
||||
upstream_model_version: model.upstreamModelVersion,
|
||||
provider_name: model.providerName,
|
||||
release_readiness: caps.releaseReadiness,
|
||||
health_status: caps.healthStatus,
|
||||
input_media_use_cases: caps.inputMediaUseCases,
|
||||
reference_inputs: caps.referenceInputs.map((reference) => ({
|
||||
media_type: reference.mediaType,
|
||||
usage_type: reference.usageType,
|
||||
min_items: reference.minItems,
|
||||
max_items: reference.maxItems,
|
||||
max_file_size_bytes: reference.maxFileSizeBytes,
|
||||
})),
|
||||
max_reference_items: caps.maxReferenceItems,
|
||||
supported_sizes: caps.supportedSizes,
|
||||
supported_aspect_ratios: caps.supportedAspectRatios,
|
||||
supported_resolutions: caps.supportedResolutions,
|
||||
supported_durations: caps.supportedDurations,
|
||||
duration_min: caps.durationMin,
|
||||
duration_max: caps.durationMax,
|
||||
duration_default: caps.durationDefault,
|
||||
output_count_min: caps.outputCountMin,
|
||||
output_count_max: caps.outputCountMax,
|
||||
prompt_max_length: caps.promptMaxLength,
|
||||
models: getAdobeFireflyFallbackCatalog(opts.modality),
|
||||
source: "fallback",
|
||||
};
|
||||
}
|
||||
|
||||
export function getAdobeReferenceUploadLimit(
|
||||
model: AdobeFireflyCatalogModel,
|
||||
mediaType: string
|
||||
): number {
|
||||
if (model.capabilities.maxReferenceItems !== null) {
|
||||
return Math.max(1, Math.min(32, model.capabilities.maxReferenceItems));
|
||||
}
|
||||
const declaredTotal = model.capabilities.referenceInputs
|
||||
.filter((reference) => reference.mediaType === mediaType)
|
||||
.reduce((total, reference) => total + (reference.maxItems ?? 0), 0);
|
||||
return Math.max(1, Math.min(32, declaredTotal || 1));
|
||||
/** Registry-shaped models for imageRegistry / videoRegistry. */
|
||||
export function toRegistryImageModels(
|
||||
models: AdobeFireflyCatalogModel[] = getAdobeFireflyFallbackCatalog("image")
|
||||
): Array<{ id: string; name: string; inputModalities?: string[] }> {
|
||||
return models
|
||||
.filter((m) => m.modality === "image")
|
||||
.map((m) => ({
|
||||
id: m.id,
|
||||
name: m.name.startsWith("Firefly ") ? m.name : `Firefly ${m.name}`,
|
||||
inputModalities: m.inputModalities || ["text", "image"],
|
||||
}));
|
||||
}
|
||||
|
||||
export function toRegistryVideoModels(
|
||||
models: AdobeFireflyCatalogModel[] = getAdobeFireflyFallbackCatalog("video")
|
||||
): Array<{ id: string; name: string }> {
|
||||
return models
|
||||
.filter((m) => m.modality === "video")
|
||||
.map((m) => ({
|
||||
id: m.id,
|
||||
name: m.name.startsWith("Firefly ") ? m.name : `Firefly ${m.name}`,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import { getResolvedModelCapabilities } from "@/lib/modelCapabilities";
|
||||
import { isVisionModelId } from "@/shared/constants/visionModels";
|
||||
|
||||
export type AutoCategory = "coding" | "reasoning" | "vision" | "chat" | "multimodal";
|
||||
export type AutoTier = "fast" | "cheap" | "floor" | "free" | "reliable" | "pro" | "subscription";
|
||||
export type AutoTier = "fast" | "cheap" | "floor" | "free" | "reliable" | "pro";
|
||||
|
||||
export const AUTO_CATEGORIES: readonly AutoCategory[] = [
|
||||
"coding",
|
||||
@@ -38,7 +38,6 @@ export const AUTO_TIERS: readonly AutoTier[] = [
|
||||
"free",
|
||||
"reliable",
|
||||
"pro",
|
||||
"subscription",
|
||||
];
|
||||
|
||||
const CATEGORY_SET = new Set<string>(AUTO_CATEGORIES);
|
||||
@@ -87,8 +86,6 @@ export function tierToWeightVariant(tier?: AutoTier): AutoVariant | "reliability
|
||||
return "cheap";
|
||||
case "reliable":
|
||||
return "reliability";
|
||||
case "subscription":
|
||||
return "coding"; // subscription uses quality-first weights
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
@@ -136,9 +133,6 @@ export function buildAutoCandidateFilter(
|
||||
if (tier === "pro") {
|
||||
checks.push((c) => safeClassifyTier(c) === "premium");
|
||||
}
|
||||
if (tier === "subscription") {
|
||||
checks.push((c) => safeClassifyTier(c) === "subscription");
|
||||
}
|
||||
|
||||
if (checks.length === 0) return null;
|
||||
return (candidate: PoolCandidate) => checks.every((fn) => fn(candidate));
|
||||
|
||||
@@ -25,26 +25,6 @@ export function classifyTier(provider: string, model: string): TierAssignment {
|
||||
return tierCache.get(key)!;
|
||||
}
|
||||
|
||||
// Check for Claude Code subscription models first
|
||||
if (
|
||||
provider === "cc" ||
|
||||
provider === "claude-code" ||
|
||||
model.startsWith("cc/") ||
|
||||
model.startsWith("claude-code/")
|
||||
) {
|
||||
const assignment: TierAssignment = {
|
||||
provider,
|
||||
model,
|
||||
tier: PROVIDER_TIER.SUBSCRIPTION,
|
||||
reason: `Claude Code subscription model detected: ${provider}/${model}`,
|
||||
costPer1MInput: 0,
|
||||
costPer1MOutput: 0,
|
||||
hasFreeTier: false,
|
||||
};
|
||||
tierCache.set(key, assignment);
|
||||
return assignment;
|
||||
}
|
||||
|
||||
if (isExplicitlyFree(provider, currentConfig)) {
|
||||
const assignment: TierAssignment = {
|
||||
provider,
|
||||
|
||||
@@ -2,7 +2,6 @@ export const PROVIDER_TIER = {
|
||||
FREE: "free",
|
||||
CHEAP: "cheap",
|
||||
PREMIUM: "premium",
|
||||
SUBSCRIPTION: "subscription",
|
||||
} as const;
|
||||
|
||||
export type ProviderTier = (typeof PROVIDER_TIER)[keyof typeof PROVIDER_TIER];
|
||||
|
||||
@@ -309,7 +309,6 @@
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-reconciler": "^0.33.0",
|
||||
"recharts": "^3.8.1",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"safe-regex": "^2.1.1",
|
||||
"selfsigned": "^5.5.0",
|
||||
"smol-toml": "1.7.1",
|
||||
|
||||
477
pm2.log
477
pm2.log
@@ -1,477 +0,0 @@
|
||||
[1m[90m[TAILING] Tailing last 200 lines for [omniroute] process (change the value with --lines option)[39m[22m
|
||||
[90m/Users/work/.pm2/logs/omniroute-error.log last 200 lines:[39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m⨯ Another next dev server is already running.
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39m- Local: http://localhost:3001
|
||||
[31m21|omnirou | [39m- PID: 20496
|
||||
[31m21|omnirou | [39m- Dir: /Users/work/serpentos/Hermes_Omni
|
||||
[31m21|omnirou | [39m- Log: .build/next/dev/logs/next-development.log
|
||||
[31m21|omnirou | [39m
|
||||
[31m21|omnirou | [39mYou can access the existing server at http://localhost:3001,
|
||||
[31m21|omnirou | [39mor run kill 20496 to stop it and start a new one.
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
[31m21|omnirou | [39m[ARENA_ELO_SYNC] Failed to fetch "text" leaderboard: The operation was aborted due to timeout
|
||||
[31m21|omnirou | [39m[ARENA_ELO_SYNC] Failed to fetch "code" leaderboard: The operation was aborted due to timeout
|
||||
[31m21|omnirou | [39m[ARENA_ELO_SYNC] Sync failed: All Arena leaderboard fetches failed: The operation was aborted due to timeout; The operation was aborted due to timeout
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001] native=[TypeError fetch failed | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001]
|
||||
[31m21|omnirou | [39m[ModelSync] github (34afe2ad): fetch failed — fetch failed
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001] native=[TypeError fetch failed | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001]
|
||||
[31m21|omnirou | [39m[ModelSync] slavakononovtattoo@gmail.com (176734ec): fetch failed — fetch failed
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001] native=[TypeError fetch failed | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001]
|
||||
[31m21|omnirou | [39m[ModelSync] Kimi Code (API) (kimi-cod): fetch failed — fetch failed
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed code=PROXY_UNREACHABLE | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001] native=[TypeError fetch failed | connect ECONNREFUSED 127.0.0.1:3001 code=ECONNREFUSED syscall=connect errno=-61 address=127.0.0.1:3001]
|
||||
[31m21|omnirou | [39m[ModelSync] Antigravity (antigrav): fetch failed — fetch failed
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed code=PROXY_UNREACHABLE | AggregateError code=ECONNREFUSED | ↳ code=ECONNREFUSED syscall=connect address=::1:20128 | ↳ code=ECONNREFUSED syscall=connect address=127.0.0.1:20128
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed code=PROXY_UNREACHABLE | AggregateError code=ECONNREFUSED | ↳ code=ECONNREFUSED syscall=connect address=::1:20128 | ↳ code=ECONNREFUSED syscall=connect address=127.0.0.1:20128] native=[TypeError fetch failed | AggregateError code=ECONNREFUSED | ↳ code=ECONNREFUSED syscall=connect address=::1:20128 | ↳ code=ECONNREFUSED syscall=connect address=127.0.0.1:20128]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND llamagate.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.galadriel.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.lambda.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND api.monsterapi.ai code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008] native=[TypeError fetch failed | getaddrinfo ENOTFOUND serving.app.predibase.com code=ENOTFOUND syscall=getaddrinfo errno=-3008]
|
||||
[31m21|omnirou | [39m[ProxyFetch] Undici dispatcher failed, falling back to native fetch (after retry): TypeError fetch failed | self-signed certificate in certificate chain code=SELF_SIGNED_CERT_IN_CHAIN
|
||||
[31m21|omnirou | [39m[ProxyFetch] native fetch fallback ALSO failed: dispatcher=[TypeError fetch failed | self-signed certificate in certificate chain code=SELF_SIGNED_CERT_IN_CHAIN] native=[TypeError fetch failed | self-signed certificate in certificate chain code=SELF_SIGNED_CERT_IN_CHAIN]
|
||||
[31m21|omnirou | [39mnpm notice run omniroute@3.8.50 dev
|
||||
[31m21|omnirou | [39mnpm notice run node --max-old-space-size=8192 scripts/dev/run-next.mjs dev --port 20128
|
||||
[31m21|omnirou | [39m[bootstrap] ℹ️ The following OAuth integrations are not configured:
|
||||
[31m21|omnirou | [39m[bootstrap] • Antigravity OAuth (ANTIGRAVITY_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] • Qoder OAuth (QODER_OAUTH_CLIENT_SECRET) — set in .env or /Users/work/.omniroute/server.env
|
||||
[31m21|omnirou | [39m[bootstrap] These providers will not work until configured.
|
||||
[31m21|omnirou | [39m[bootstrap] ⚠️ INITIAL_PASSWORD is not set — using default 'CHANGEME'. Change it in Settings!
|
||||
|
||||
[90m/Users/work/.pm2/logs/omniroute-out.log last 200 lines:[39m
|
||||
[32m21|omnirou | [39m[STARTUP] Provider limits sync scheduler started
|
||||
[32m21|omnirou | [39m[INFO] [QuotaAutoPing] scheduler started
|
||||
[32m21|omnirou | [39m[STARTUP] Quota auto-ping scheduler started (opt-in, no-op until enabled)
|
||||
[32m21|omnirou | [39m[ModelSync] Scheduler started — interval: 24h
|
||||
[32m21|omnirou | [39m[STARTUP] Cloud/model sync background bootstrap initialized
|
||||
[32m21|omnirou | [39m[BATCH] Initializing batch processor polling...
|
||||
[32m21|omnirou | [39m[STARTUP] Batch processor started
|
||||
[32m21|omnirou | [39m[STARTUP] Runtime settings hydrated: payloadRules, modelAliases, backgroundDegradation, cliCompatProviders, cacheControl, usageTracking, healthCheckLogs, thoughtSignature, modelsDevSync, corsOrigins, ccBridgeTransforms, systemTransforms, authzBypass, bannedSignals
|
||||
[32m21|omnirou | [39m[STARTUP] Model alias seed: applied=0, skipped=2, removed=0, failed=0
|
||||
[32m21|omnirou | [39m[HOT_RELOAD] Runtime config hot-reload started (poll=5000ms, fsWatch=on)
|
||||
[32m21|omnirou | [39m[proxyLogger] Loaded 200 proxy logs from SQLite
|
||||
[32m21|omnirou | [39m[CredentialHealth] Starting credential health check (initial delay 30s, interval 300s)
|
||||
[32m21|omnirou | [39m[STARTUP] Credential health scheduler started
|
||||
[32m21|omnirou | [39m[COMPLIANCE] Audit log table initialized
|
||||
[32m21|omnirou | [39m[STARTUP] Scheduled VACUUM initialized (#4437)
|
||||
[32m21|omnirou | [39m[AutoRefreshDaemon] Started — checking 0 credentials every 900s
|
||||
[32m21|omnirou | [39m[ARENA_ELO_SYNC] Starting periodic sync every 86400s
|
||||
[32m21|omnirou | [39m[PRICING_SYNC] Disabled (set PRICING_SYNC_ENABLED=true to enable)
|
||||
[32m21|omnirou | [39m[STARTUP] Arena ELO sync initialized
|
||||
[32m21|omnirou | [39m[ConnectionRecovery] Starting proactive cooldown recovery (tick every 60s)
|
||||
[32m21|omnirou | [39m[LiveWS] Seeded latest compression run from analytics: a621d3da-eee5-4f34-8515-309d21dc1415
|
||||
[32m21|omnirou | [39m[STARTUP] Live dashboard WebSocket daemon bootstrap invoked
|
||||
[32m21|omnirou | [39m[STARTUP] Embedded services bootstrap complete
|
||||
[32m21|omnirou | [39m[MODELS_DEV] Disabled (enable via Settings > AI)
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-flash" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-flash" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-flash" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-flash" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "openai/gpt-oss-120b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "openai/gpt-oss-120b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "openai/gpt-oss-120b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "deepseek-ai/deepseek-v4-pro" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "openai/gpt-oss-120b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "moonshotai/Kimi-K2.7-Code" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "moonshotai/Kimi-K2.7-Code" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "moonshotai/Kimi-K2.7-Code" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "qwen/qwen3.6-27b" as an exact model id
|
||||
[32m21|omnirou | [39m[MODEL] Treating "moonshotai/Kimi-K2.7-Code" as an exact model id
|
||||
[32m21|omnirou | [39m[STARTUP] Model catalog cache warmed
|
||||
[32m21|omnirou | [39m[STARTUP] OpenRouter model catalog cache warmed
|
||||
[32m21|omnirou | [39m[EmbedWsProxy] Listening on 127.0.0.1:20131
|
||||
[32m21|omnirou | [39m[LiveWS] Dashboard WebSocket server listening on 127.0.0.1:20132
|
||||
[32m21|omnirou | [39m[ModelSync] Starting model sync cycle — 4 connection(s)
|
||||
[32m21|omnirou | [39m[HealthCheck] github/34afe2ad-6d24-4ccd-9747-ac430ba73aa4 has no refresh token but has a GitHub access token; keeping connection active
|
||||
[32m21|omnirou | [39m[CredentialHealth] Testing 250/250 connections...
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ openai-compatible/0a53f78b-a2ad-4e9f-9668-e1c1f2ac951d — Provider test not supported [11ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ qwen/f5626345-947f-46f8-930a-5dabd6a68331 — Provider test not supported [13ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[Next] dev server listening on http://0.0.0.0:3001 (turbopack)
|
||||
[32m21|omnirou | [39m[ModelSync] Cycle complete: 0/4 synced in 189ms
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ puter/puter-free — Invalid API key [300ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ longcat/bfb70bc9-4e06-4d63-8283-630228e900e8 — Invalid API key [499ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ qoder/qoder-free — Local CLI runtime is not installed [2342ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ huggingface/b684540c-e84f-4591-a435-71a7620c1999 — Cannot convert argument to a ByteString because the character at index 41 has a value of 8232 which is greater than 255. [2479ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ xai-oauth/809a7805-67c4-4085-9195-6c0708189eaa — API returned 402 [2575ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ dgrid/1454ba3b-53d9-4d44-a322-910f62f19ac7 — Invalid API key [298ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ cheaperinference/80519c86-cc63-4ad6-981a-de0279e49d16 — Invalid API key [435ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ command-code/d63168d8-5a22-4be8-a1e2-b57a18683cce — Invalid API key [579ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ agentrouter/d1556537-17c8-44f1-85ab-3a1daae0dfab — Invalid API key [1356ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ orcarouter/3b98bf6e-dead-4807-8146-c4a67aa9b23b — Invalid API key [490ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[05:02:57.823] [32mINFO[39m: [36m[sse] Account 7bd8a34d error cleared (CAS)[39m
|
||||
[32m21|omnirou | [39m [35mmodule[39m: "sse"
|
||||
[32m21|omnirou | [39m [35mtag[39m: "AUTH"
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ wafer/b02994c7-3bb6-4753-ae11-d85dc5e2a0e9 — Invalid API key [1410ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ freetheai/b6fa0b94-088c-4693-9221-a96f795eb7d4 — Invalid API key [375ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ uncloseai/79e43f97-1192-47aa-8f52-99ea5ed0300d — Provider unavailable (502) [1383ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ vercel-ai-gateway/2b8023d4-82ce-4684-8fe8-c716d361fed8 — Invalid API key [174ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ llamagate/24cf76c7-4894-47ff-af6a-7e2af4720d3a — fetch failed [299ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ gitlawb-gmi/e18cf4cd-fa35-453e-8c8d-2741f6c1eaaf — Invalid API key [321ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ gitlawb/243a74a5-f41b-44b1-bed6-47edc1b9544b — Invalid API key [447ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ piapi/7f56fc63-8257-4998-85e4-5032d7d9a66b — Provider test not supported [28ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ getgoapi/1406f87c-8ddf-44bb-a0fa-cc04b6128306 — Provider test not supported [23ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ laozhang/630cd8b8-8074-49f8-b382-d510ebc9d5e7 — Provider test not supported [24ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ thebai/05fd1dfc-faab-45ce-b9d6-3be9a2ff4276 — Provider test not supported [24ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ poe/d9986d2a-fd90-41c6-8f5e-37a03943fd4c — Invalid API key [332ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ bai/c662bf62-720d-4acd-8d27-913c7e63dccc — Invalid API key [414ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ empower/45ad5813-1da8-4e92-9ca6-9696da0ecd57 — Provider test not supported [34ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ fenayai/486b463e-cbf4-464d-bf18-a2845bd88f4d — Provider test not supported [35ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ factory/aaf1a527-f9af-494f-b0f2-c81ff6605f62 — Provider validation endpoint not supported [405ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[05:03:04.389] [32mINFO[39m: [36m[sse] Account 809a7805 error cleared (CAS)[39m
|
||||
[32m21|omnirou | [39m [35mmodule[39m: "sse"
|
||||
[32m21|omnirou | [39m [35mtag[39m: "AUTH"
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ bluesminds/e664de41-5bca-489b-bb85-46e6726b98e0 — Invalid API key [233ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ chutes/add9d1d1-3f43-44c7-b8a5-2bd3bb939203 — Invalid API key [355ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ dit/d59eb062-3f1b-4fa0-a3cc-e275aba9e386 — Invalid API key [795ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ sumopod/f3649287-8b58-41ab-a5c3-02674adbf157 — Invalid API key [1465ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ x5lab/12522685-26e7-4799-a8d3-13b95f1d71e7 — Provider unavailable (530) [317ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ openadapter/2be7647a-1211-4154-8cf1-df1575bb0c23 — Invalid API key [534ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ tokenrouter/62848fc3-351d-4d7b-8b30-50e1adecbfd0 — Invalid API key [807ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ chenzk/cd8f98c9-8dd9-476a-bf9b-9dc3ce0ece72 — Invalid API key [2379ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ aion/97948c8e-564e-470c-abdf-b744c363162b — Invalid API key [330ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[ProxyHealth] Starting proxy health scheduler (interval: 600000ms)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ nara/422b54f9-f654-40fb-906d-fff9b0de72de — Invalid API key [654ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ reka/c30f7b6b-ad2e-46b6-90c3-21926e6c3924 — Invalid API key [153ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ anthropic/ee916845-395a-4184-9b07-9c26f969aaa1 — Invalid API key [477ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ mistral/bec0969f-65ae-41dc-be66-43d36dc8897b — Invalid API key [281ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ cohere/763467f5-1a9a-4ae9-a310-a952387d8050 — Invalid API key [376ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ perplexity/1ce7f3fc-b063-43ff-93e3-2ce96e4de76c — Invalid API key [410ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ galadriel/99886394-bd42-4cf0-b59f-ea73df3f2895 — fetch failed [424ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ morph/ad1d2458-2dd3-4f9a-b653-d9def966be1b — Invalid API key [90ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ meta-llama/27504e6f-2422-4eac-8265-6282a7b31910 — Invalid API key [135ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ nous-research/867d96b1-6466-416a-8d77-7a4d61bf8823 — Invalid API key [311ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ maritalk/bf819d44-a782-4947-8051-aa5a8aeb3304 — Invalid API key [766ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ upstage/d75885b8-e055-48be-afcf-9d09b683543f — Invalid API key [882ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ codestral/81d87255-39e1-4085-8bb7-de1916484c18 — Invalid API key [298ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ writer/42ffd640-20a0-4f9a-8bf2-06a97c359771 — Invalid API key [410ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ liquid/916bc15c-fb8c-4763-b264-5f7794d63f52 — Invalid API key [473ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ fireworks/56d3d5d6-9ea4-4cff-aec5-5b285f6a6e42 — Invalid API key [530ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ arcee-ai/b1a6a4c1-a859-42ec-82f5-62786c1d790e — Provider test not supported [25ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ siliconflow/223f9fd1-0d0b-4306-9487-02ee2a4efccf — Invalid API key [523ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ nube/d9ddeb1b-ebc5-4e92-ac6b-5030011e114c — Invalid API key [685ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ hyperbolic/1acffcad-79bb-4309-adcb-93c7739942c0 — Invalid API key [800ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ openvecta/664194e7-da9d-4884-9112-286782dc1463 — Invalid API key [309ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ nebius/b416aaff-ad3e-4531-b6b8-71f5c97c671e — Invalid API key [353ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ deepinfra/f6307c55-52ac-424b-b4fd-99ecf43d68dc — Invalid API key [701ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ nscale/305c1ad6-1214-42bd-8fd5-6290103c7f6e — Invalid API key [275ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ lambda-ai/02e9af45-1152-4c03-8dce-c475db3952e2 — fetch failed [307ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ baseten/01b22f14-f3e4-4c7e-9a72-8cb515e9a94c — Invalid API key [340ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ featherless-ai/ad32b81a-0fe4-4014-95f6-6f9c42db0a46 — Invalid API key [787ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ publicai/85befdb8-4a2b-45f8-b8d3-24c978fbada6 — Invalid API key [271ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ inference-net/a11e8b2e-55e4-4b4e-b378-36f118100160 — Invalid API key [418ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ wandb/d4732e05-ee59-45b3-b4a1-0c4d00a0807a — Invalid API key [522ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ monsterapi/b54896e1-acf7-4100-9e18-f45b2456acdf — fetch failed [647ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ bytez/1ba3efe1-06e5-48ce-93bf-9dced73dc5ca — Invalid API key [299ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ azure-ai/92b248f6-87b9-479b-9b8b-e52dade0cf40 — Invalid API key [382ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ predibase/3dcf4649-7c03-4fdb-88b2-1bac201fb63a — fetch failed [420ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ bedrock/2511a80c-a6a5-4acc-a25e-3a0439b401ce — Invalid API key [524ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ byteplus/d35eed01-d625-4011-ad2d-4eeca7a39d3f — Invalid API key [746ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ azure-openai/82265a58-9a51-4c9a-9298-d053e64cba05 — Missing base URL [34ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ digitalocean/74fdfca2-e1a6-49c3-8254-a652103f22a7 — Invalid API key [316ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ watsonx/42e05ebc-d134-47ea-ad61-9f20bf324741 — Invalid API key [819ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity refreshed
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ oci/0300cc22-79f8-4ef0-8c9f-0340121c1c34 — Invalid API key [1342ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ modal/a5d5b559-97a6-496d-9fe4-a9a29bd96b8f — Invalid outbound URL: [93ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ scaleway/6ef89557-85c0-4175-af68-6e76ef41c8a0 — Invalid API key [237ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ ovhcloud/35d1b82e-4643-43ef-8098-9fd3f5eb8a8b — Invalid API key [269ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ databricks/a16ac17b-4de3-416e-aa5d-71c2fad52d18 — Missing base URL [40ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity projectId discovered during refresh: polar-weaver-2tclv
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ clarifai/aa71f371-f781-40af-946c-99155c4c3018 — Invalid API key [692ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ qianfan/bbf3bde6-93ff-4b3a-acb5-a097111fb5ea — Invalid API key [1093ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ heroku/91c06d49-073c-49d9-b64f-f59bd8330023 — Missing base URL [28ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ snowflake/94a20d62-fa07-41ba-bdad-48d076d0b8e9 — Missing base URL [29ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ bailian-coding-plan/561f427a-1df7-46cf-899e-9ed61345fe5b — Invalid API key [858ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ glm/37a8e485-3177-46ca-9fd4-53408634e872 — Invalid API key [1003ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ glmt/ca7a155c-988d-43ba-971c-571bb0d679c1 — Invalid API key [1035ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ glm-cn/d4830be9-b2ea-462a-8067-c35f59aab73f — Invalid API key [288ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ qwen-cloud/c22ca10b-3907-4e6f-b9f7-53c0850bb313 — Invalid API key [686ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ qwen-cloud-token-plan/fc29d826-8de5-4266-9677-a663fa709cc8 — Invalid API key [722ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity refreshed
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ zai/fbff3609-132c-492a-9c50-41a6603aa9b7 — Invalid API key [1539ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity projectId discovered during refresh: isometric-tendril-lfb6s
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ minimax-cn/69617fca-c974-41d2-b2a4-5d2f1b849cf9 — Invalid API key [1922ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ kimi/62a54a22-c074-4007-9b7b-9784b2a7cb37 — Invalid API key [520ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ minimax/6e44c652-6ed0-44d2-9c61-c28289284b42 — Invalid API key [660ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ alibaba/db532af8-08a3-4589-bef3-30c8350c2b8d — Invalid API key [882ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ alibaba-cn/f20ca3c9-fe7f-406c-af34-6d388cc72e24 — Invalid API key [1061ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ volcengine/aa953628-bcbd-40ea-a395-acbc7ddca48f — Invalid API key [2233ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ moonshot/994f4192-a991-4996-9bfa-7bb6a7f583f2 — Invalid API key [527ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity refreshed
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ xiaomi-mimo-token-plan/67977290-9d6a-4d4c-adc0-821b481829f3 — Invalid API key [927ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ yi/beb4c757-62f7-45d2-a646-c5d298c158aa — Invalid API key [1326ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ tencent/9cd0617a-4971-46fb-9d6d-a0f9e19d5f97 — Invalid API key [1873ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity projectId discovered during refresh: grand-vigil-qmvz5
|
||||
[32m21|omnirou | [39mTOKEN Antigravity refreshed
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ baidu/d5b875f1-6395-4430-bac7-b5120304d2c8 — Invalid API key [5878ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ xiaomi-mimo/7bd8a34d-45aa-4159-9c45-d028f4067310 — Invalid API key [286ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39mTOKEN Antigravity projectId discovered during refresh: isometric-tendril-lfb6s
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ gigachat/318c3bfc-ee0c-4f74-82b8-d55e28372837 — fetch failed [796ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ coze/0d817a49-5dd8-4d14-b0c9-49b60216a8e0 — Provider validation endpoint not supported [950ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ doubao/0bbd928d-6d57-404f-8c69-47e6f53af675 — Invalid API key [979ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m[CredentialHealth] ❌ stepfun/930f3cf5-16b6-4584-ae7d-5bff7f6dc9c1 — Invalid API key [1211ms] (failure #1, next check in 600s)
|
||||
[32m21|omnirou | [39m
|
||||
[32m21|omnirou | [39m[Shutdown] Received SIGINT. Draining 0 request(s)...
|
||||
[32m21|omnirou | [39m[Shutdown] All in-flight requests drained.
|
||||
[32m21|omnirou | [39m[DB] SQLite WAL checkpoint completed (TRUNCATE).
|
||||
[32m21|omnirou | [39m[Shutdown] SQLite database checkpointed and closed.
|
||||
[32m21|omnirou | [39m[Shutdown] Log rotation timer stopped.
|
||||
[32m21|omnirou | [39m[Shutdown] Bye.
|
||||
[32m21|omnirou | [39m[MDX] closing dev server
|
||||
[32m21|omnirou | [39m[MDX] generated files in 17.23112500000002ms
|
||||
[32m21|omnirou | [39m✓ Running next.config.mjs took 340ms
|
||||
[32m21|omnirou | [39m[MDX] started dev server
|
||||
[32m21|omnirou | [39m○ Compiling /instrumentation ...
|
||||
|
||||
[31m21|omniroute | [39m⚠ ./open-sse/services/browserPool.ts
|
||||
[31m21|omniroute | [39mCritical dependency: the request of a dependency is an expression
|
||||
[31m21|omniroute | [39mImport trace for requested module:
|
||||
[31m21|omniroute | [39m./open-sse/services/browserPool.ts
|
||||
[31m21|omniroute | [39m./open-sse/services/grokClearance.ts
|
||||
[31m21|omniroute | [39m./open-sse/executors/grok-web.ts
|
||||
[31m21|omniroute | [39m./open-sse/executors/index.ts
|
||||
[31m21|omniroute | [39m./open-sse/index.ts
|
||||
[31m21|omniroute | [39m./src/instrumentation-node.ts
|
||||
[31m21|omniroute | [39m./src/lib/db/adapters/driverFactory.ts
|
||||
[31m21|omniroute | [39mCritical dependency: require function is used in a way in which dependencies cannot be statically extracted
|
||||
[31m21|omniroute | [39mImport trace for requested module:
|
||||
[31m21|omniroute | [39m./src/lib/db/adapters/driverFactory.ts
|
||||
[31m21|omniroute | [39m./src/lib/db/core.ts
|
||||
[31m21|omniroute | [39m./src/lib/pricingSync.ts
|
||||
[31m21|omniroute | [39m./src/instrumentation-node.ts
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[31m21|omniroute | [39m[DB] Could not probe existing DB: [DB] Nenhum driver SQLite disponível para '/Users/work/.omniroute/storage.sqlite'. Chame ensureDbInitialized() no startup. Drivers testados: better-sqlite3 (failed), node:sqlite (unavailable). sql.js WASM ainda não foi pré-inicializado.
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[31m21|omniroute | [39m[DB] Could not probe existing DB: [DB] Nenhum driver SQLite disponível para '/Users/work/.omniroute/storage.sqlite'. Chame ensureDbInitialized() no startup. Drivers testados: better-sqlite3 (failed), node:sqlite (unavailable). sql.js WASM ainda não foi pré-inicializado.
|
||||
[32m21|omniroute | [39m[CREDENTIALS] No external credentials file found, using defaults.
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[31m21|omniroute | [39m[DB] Could not probe existing DB: [DB] Nenhum driver SQLite disponível para '/Users/work/.omniroute/storage.sqlite'. Chame ensureDbInitialized() no startup. Drivers testados: better-sqlite3 (failed), node:sqlite (unavailable). sql.js WASM ainda não foi pré-inicializado.
|
||||
[32m21|omniroute | [39m[STARTUP] Global fetch proxy patch initialized
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[31m21|omniroute | [39m[DB] Pre-initializing sql.js WASM (synchronous drivers unavailable)...
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[31m21|omniroute | [39m[DB] Could not probe existing DB: [DB] Nenhum driver SQLite disponível para '/Users/work/.omniroute/storage.sqlite'. Chame ensureDbInitialized() no startup. Drivers testados: better-sqlite3 (failed), node:sqlite (unavailable). sql.js WASM ainda não foi pré-inicializado.
|
||||
[31m21|omniroute | [39m[callLogs] Failed to rotate request artifacts: [DB] Nenhum driver SQLite disponível para '/Users/work/.omniroute/storage.sqlite'. Chame ensureDbInitialized() no startup. Drivers testados: better-sqlite3 (failed), node:sqlite (unavailable). sql.js WASM ainda não foi pré-inicializado.
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'better-sqlite3' failed to open, will try next driver: Cannot find module 'better-sqlite3'
|
||||
[32m21|omniroute | [39m[DB] Sync driver 'node:sqlite' failed to open, will try next driver: Cannot find module 'node:sqlite'
|
||||
[32m21|omniroute | [39m[DB] Changing cache_size from 65536KB to 16384KB
|
||||
[32m21|omniroute | [39m[DB] cache_size changed to 16384KB
|
||||
[32m21|omniroute | [39m[DB] SQLite database ready: /Users/work/.omniroute/storage.sqlite (DATA_DIR=/Users/work/.omniroute, SQLITE_FILE=/Users/work/.omniroute/storage.sqlite)
|
||||
[32m21|omniroute | [39m[HealthCheck] Starting proactive token health-check (tick every 60s)
|
||||
[32m21|omniroute | [39m[Shutdown] Graceful shutdown handlers registered.
|
||||
[32m21|omniroute | [39m[STARTUP] Spend batch writer started
|
||||
[32m21|omniroute | [39m[STARTUP] Guardrail registry initialized
|
||||
[32m21|omniroute | [39m[STARTUP] Builtin skill handlers registered
|
||||
[32m21|omniroute | [39m[STARTUP] Quota cache background refresh started
|
||||
[32m21|omniroute | [39m[ProviderLimitsSync] Scheduler started — interval: 70m
|
||||
[32m21|omniroute | [39m[STARTUP] Provider limits sync scheduler started
|
||||
[32m21|omniroute | [39m[INFO] [QuotaAutoPing] scheduler started
|
||||
[32m21|omniroute | [39m[STARTUP] Quota auto-ping scheduler started (opt-in, no-op until enabled)
|
||||
[32m21|omniroute | [39m[ModelSync] Scheduler started — interval: 24h
|
||||
[32m21|omniroute | [39m[STARTUP] Cloud/model sync background bootstrap initialized
|
||||
[32m21|omniroute | [39m[BATCH] Initializing batch processor polling...
|
||||
[32m21|omniroute | [39m[STARTUP] Batch processor started
|
||||
[32m21|omniroute | [39m[STARTUP] Runtime settings hydrated: payloadRules, modelAliases, backgroundDegradation, cliCompatProviders, cacheControl, usageTracking, healthCheckLogs, thoughtSignature, modelsDevSync, corsOrigins, ccBridgeTransforms, systemTransforms, authzBypass, bannedSignals
|
||||
[32m21|omniroute | [39m[STARTUP] Model alias seed: applied=0, skipped=2, removed=0, failed=0
|
||||
[32m21|omniroute | [39m[HOT_RELOAD] Runtime config hot-reload started (poll=5000ms, fsWatch=on)
|
||||
[32m21|omniroute | [39m[proxyLogger] Loaded 200 proxy logs from SQLite
|
||||
[32m21|omniroute | [39m[CredentialHealth] Starting credential health check (initial delay 30s, interval 300s)
|
||||
[32m21|omniroute | [39m[STARTUP] Credential health scheduler started
|
||||
[32m21|omniroute | [39m[COMPLIANCE] Audit log table initialized
|
||||
[32m21|omniroute | [39m[STARTUP] Scheduled VACUUM initialized (#4437)
|
||||
[32m21|omniroute | [39m[AutoRefreshDaemon] Started — checking 0 credentials every 900s
|
||||
[32m21|omniroute | [39m[ARENA_ELO_SYNC] Starting periodic sync every 86400s
|
||||
[32m21|omniroute | [39m[PRICING_SYNC] Disabled (set PRICING_SYNC_ENABLED=true to enable)
|
||||
[32m21|omniroute | [39m[STARTUP] Arena ELO sync initialized
|
||||
[32m21|omniroute | [39m[ConnectionRecovery] Starting proactive cooldown recovery (tick every 60s)
|
||||
[32m21|omniroute | [39m[LiveWS] Seeded latest compression run from analytics: a621d3da-eee5-4f34-8515-309d21dc1415
|
||||
[32m21|omniroute | [39m[STARTUP] Live dashboard WebSocket daemon bootstrap invoked
|
||||
[32m21|omniroute | [39m[STARTUP] Embedded services bootstrap complete
|
||||
[32m21|omniroute | [39m[MODELS_DEV] Disabled (enable via Settings > AI)
|
||||
34033
pnpm-lock.yaml
generated
34033
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
const bcrypt = require('bcryptjs');
|
||||
const Database = require('better-sqlite3');
|
||||
|
||||
async function run() {
|
||||
const hash = await bcrypt.hash('6666', 12);
|
||||
const db = new Database('/Users/work/.omniroute/settings.db');
|
||||
db.prepare("UPDATE key_value SET value = ? WHERE key = 'password'").run(JSON.stringify(hash));
|
||||
console.log("Password reset to 6666");
|
||||
}
|
||||
run().catch(console.error);
|
||||
@@ -1,41 +0,0 @@
|
||||
import {
|
||||
APIKEY_PROVIDERS,
|
||||
FREE_APIKEY_PROVIDER_IDS,
|
||||
LOCAL_PROVIDERS,
|
||||
NOAUTH_PROVIDERS,
|
||||
} from "../../src/shared/constants/providers.js";
|
||||
import { createProviderConnection } from "../../src/models/index.js";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
async function run() {
|
||||
const allProviders = [
|
||||
...Object.keys(APIKEY_PROVIDERS),
|
||||
...Object.keys(LOCAL_PROVIDERS),
|
||||
...Object.keys(NOAUTH_PROVIDERS),
|
||||
...Array.from(FREE_APIKEY_PROVIDER_IDS),
|
||||
];
|
||||
|
||||
// Deduplicate
|
||||
const uniqueProviders = [...new Set(allProviders)];
|
||||
|
||||
console.log(`Found ${uniqueProviders.length} unique providers. Adding...`);
|
||||
|
||||
for (const provider of uniqueProviders) {
|
||||
try {
|
||||
await createProviderConnection({
|
||||
id: randomUUID(),
|
||||
provider: provider,
|
||||
authType: "apikey",
|
||||
name: `${provider} test connection`,
|
||||
apiKey: `test-key-${provider}-${Date.now()}`,
|
||||
});
|
||||
console.log(`✅ Added ${provider}`);
|
||||
} catch (e) {
|
||||
console.log(`❌ Failed to add ${provider}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Done adding providers.");
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -1,102 +0,0 @@
|
||||
import { getDbInstance } from "../../src/lib/db/core.js";
|
||||
import { randomUUID, createHash } from "crypto";
|
||||
|
||||
async function run() {
|
||||
const db = getDbInstance();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
console.log("Setting up dependencies...");
|
||||
|
||||
// 1. Get a provider connection
|
||||
const conn = db.prepare("SELECT id FROM provider_connections LIMIT 1").get();
|
||||
if (!conn) {
|
||||
console.error("No provider connection found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Create a generic API key
|
||||
const apiKeyId = "key-" + randomUUID();
|
||||
const keyHash = createHash("sha256").update("dummy_key").digest("hex");
|
||||
|
||||
try {
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO api_keys (id, name, key, key_hash, is_active, max_sessions, scopes, created_at, updated_at, access_schedule)
|
||||
VALUES (?, ?, ?, ?, 1, 10, '[]', ?, ?, '{}')
|
||||
`
|
||||
).run(apiKeyId, "Quota Testing Key", "dummy_key", keyHash, now, now);
|
||||
} catch (e) {
|
||||
console.error("Could not insert api_key:", e.message);
|
||||
}
|
||||
|
||||
console.log("Configuring Quota Pools...");
|
||||
try {
|
||||
const poolId = randomUUID();
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO quota_pools (id, connection_id, name, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`
|
||||
).run(poolId, conn.id, "Shared Developer Pool", now);
|
||||
|
||||
// Add to quota_groups
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO quota_groups (id, name, created_at)
|
||||
VALUES (?, ?, ?)
|
||||
`
|
||||
).run("group-" + poolId, "Developer Group", now);
|
||||
} catch (e) {
|
||||
console.error("Error setting quota_pools:", e.message);
|
||||
}
|
||||
|
||||
console.log("Configuring Domain Budgets...");
|
||||
try {
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO domain_budgets (api_key_id, daily_limit_usd, weekly_limit_usd, monthly_limit_usd, warning_threshold)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`
|
||||
).run(apiKeyId, 50.0, 200.0, 1000.0, 0.8);
|
||||
} catch (e) {
|
||||
console.error("Error setting domain_budgets:", e.message);
|
||||
}
|
||||
|
||||
console.log("Configuring Fallback Chains...");
|
||||
try {
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO domain_fallback_chains (model, chain)
|
||||
VALUES (?, ?)
|
||||
`
|
||||
).run("gpt-4-turbo", JSON.stringify(["claude-3-opus", "gemini-1.5-pro"]));
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO domain_fallback_chains (model, chain)
|
||||
VALUES (?, ?)
|
||||
`
|
||||
).run("claude-3-5-sonnet", JSON.stringify(["gpt-4o", "gemini-1.5-pro"]));
|
||||
} catch (e) {
|
||||
console.error("Error setting domain_fallback_chains:", e.message);
|
||||
}
|
||||
|
||||
console.log("Configuring Sync Tokens...");
|
||||
try {
|
||||
const syncTokenValue = `sync_${randomUUID().replace(/-/g, "")}`;
|
||||
const tokenHash = createHash("sha256").update(syncTokenValue).digest("hex");
|
||||
|
||||
db.prepare(
|
||||
`
|
||||
INSERT INTO sync_tokens (id, name, token_hash, sync_api_key_id, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
).run(randomUUID(), "Dashboard Sync Token", tokenHash, apiKeyId, now, now);
|
||||
} catch (e) {
|
||||
console.error("Error setting sync_tokens:", e.message);
|
||||
}
|
||||
|
||||
console.log("Configuration complete!");
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -1,207 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
function usage() {
|
||||
console.error(
|
||||
"Usage: node scripts/dev/generate-adobe-firefly-snapshot.mjs <discovery.json> <output.ts>"
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const [, , inputArg, outputArg] = process.argv;
|
||||
if (!inputArg || !outputArg) usage();
|
||||
|
||||
const inputPath = path.resolve(inputArg);
|
||||
const outputPath = path.resolve(outputArg);
|
||||
const inputBytes = fs.readFileSync(inputPath);
|
||||
const sourceHash = createHash("sha256").update(inputBytes).digest("hex");
|
||||
const root = JSON.parse(inputBytes.toString("utf8"));
|
||||
|
||||
function mergeObjectSchema(schema) {
|
||||
const merged = { properties: {}, required: [] };
|
||||
const visit = (node) => {
|
||||
if (!node || typeof node !== "object") return;
|
||||
if (node.properties && typeof node.properties === "object") {
|
||||
Object.assign(merged.properties, node.properties);
|
||||
}
|
||||
if (Array.isArray(node.required)) merged.required.push(...node.required);
|
||||
if (Array.isArray(node.allOf)) node.allOf.forEach(visit);
|
||||
};
|
||||
visit(schema);
|
||||
merged.required = [...new Set(merged.required)];
|
||||
return merged;
|
||||
}
|
||||
|
||||
function branches(schema) {
|
||||
if (!schema || typeof schema !== "object") return [];
|
||||
return [schema, ...(schema.anyOf || []), ...(schema.oneOf || [])];
|
||||
}
|
||||
|
||||
function stringEnums(schema) {
|
||||
return [
|
||||
...new Set(
|
||||
branches(schema)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.filter((value) => typeof value === "string")
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
function integerSchema(schema) {
|
||||
return branches(schema).find((branch) => branch.type === "integer") || {};
|
||||
}
|
||||
|
||||
function publicModelId(modelId, modelVersion) {
|
||||
const slug = (value, allowDot = false) =>
|
||||
String(value || "")
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(allowDot ? /[^a-z0-9.]+/g : /[^a-z0-9]+/g, "-")
|
||||
.replace(/^-|-$/g, "");
|
||||
const family = slug(modelId);
|
||||
const publicVersion =
|
||||
family === "kling" ? String(modelVersion).replace(/^kling_v3_omni/i, "kling_o3") : modelVersion;
|
||||
const version = slug(publicVersion, true);
|
||||
if (!version || version === "default" || version === family) return family || "model";
|
||||
return `${family}-${version}`;
|
||||
}
|
||||
|
||||
function normalizeModel(family, modelVersion, version) {
|
||||
const schema = mergeObjectSchema(version.requestSchema);
|
||||
const properties = schema.properties;
|
||||
const referenceSchema = properties.referenceBlobs || {};
|
||||
const referenceInputs = [];
|
||||
for (const media of referenceSchema["x-capabilities"] || []) {
|
||||
for (const usage of media.usageConstraints || []) {
|
||||
if (usage.deprecated === true) continue;
|
||||
referenceInputs.push({
|
||||
mediaType: String(media.mediaType || ""),
|
||||
usageType: String(usage.usageType || ""),
|
||||
minItems: Number.isInteger(usage.minItems) ? usage.minItems : 0,
|
||||
maxItems: Number.isInteger(usage.maxItems) ? usage.maxItems : null,
|
||||
maxFileSizeBytes: Number.isInteger(media.maxFileSizeBytes) ? media.maxFileSizeBytes : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const supportedSizes = [
|
||||
...new Set(
|
||||
branches(properties.size)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.filter(
|
||||
(size) =>
|
||||
size &&
|
||||
Number.isInteger(size.width) &&
|
||||
size.width > 0 &&
|
||||
Number.isInteger(size.height) &&
|
||||
size.height > 0
|
||||
)
|
||||
.map((size) => `${size.width}x${size.height}`)
|
||||
),
|
||||
];
|
||||
const supportedAspectRatios = [
|
||||
...new Set(
|
||||
branches(properties.generationSettings).flatMap((branch) =>
|
||||
stringEnums(branch?.properties?.aspectRatio)
|
||||
)
|
||||
),
|
||||
];
|
||||
const duration = integerSchema(properties.duration);
|
||||
const supportedDurations = [
|
||||
...new Set(
|
||||
branches(properties.duration)
|
||||
.flatMap((branch) => (Array.isArray(branch.enum) ? branch.enum : []))
|
||||
.filter(Number.isInteger)
|
||||
),
|
||||
];
|
||||
const prompt = branches(properties.prompt).find((branch) => branch.type === "string") || {};
|
||||
const outputCount = integerSchema(properties.n);
|
||||
|
||||
return {
|
||||
id: publicModelId(family.modelId, modelVersion),
|
||||
name: String(version.modelDisplayName || version.modelCaiDisplayName || modelVersion),
|
||||
modality: version.outputModality[0],
|
||||
upstreamModelId: family.modelId,
|
||||
upstreamModelVersion: modelVersion,
|
||||
providerName: String(family.acModelFamilyProviderDisplayName || ""),
|
||||
releaseReadiness: String(version.releaseReadiness || ""),
|
||||
healthStatus: String(version.healthStatus || ""),
|
||||
inputMediaUseCases: (version.inputMediaUseCase || []).map(String),
|
||||
schemaProperties: Object.keys(properties),
|
||||
requiredProperties: schema.required,
|
||||
referenceInputs,
|
||||
maxReferenceItems: Number.isInteger(referenceSchema.maxItems) ? referenceSchema.maxItems : null,
|
||||
supportedSizes,
|
||||
supportedAspectRatios,
|
||||
supportedResolutions: stringEnums(properties.resolution),
|
||||
supportedDurations,
|
||||
durationMin: Number.isInteger(duration.minimum) ? duration.minimum : null,
|
||||
durationMax: Number.isInteger(duration.maximum) ? duration.maximum : null,
|
||||
durationDefault: Number.isInteger(duration.default) ? duration.default : null,
|
||||
outputCountMin: Number.isInteger(outputCount.minimum) ? outputCount.minimum : null,
|
||||
outputCountMax: Number.isInteger(outputCount.maximum) ? outputCount.maximum : null,
|
||||
promptMaxLength: Number.isInteger(prompt.maxLength) ? prompt.maxLength : null,
|
||||
backingModel: String(version.bksGenerationModel || ""),
|
||||
};
|
||||
}
|
||||
|
||||
const rawModels = [];
|
||||
for (const family of Array.isArray(root.models) ? root.models : []) {
|
||||
for (const [modelVersion, version] of Object.entries(family.modelVersions || {})) {
|
||||
if (!version || version.enabled === false) continue;
|
||||
const modality = Array.isArray(version.outputModality)
|
||||
? version.outputModality.map((value) => String(value).toLowerCase())[0]
|
||||
: "";
|
||||
if (modality !== "image" && modality !== "video") continue;
|
||||
|
||||
const schema = mergeObjectSchema(version.requestSchema);
|
||||
if (!schema.properties.prompt) continue;
|
||||
const useCases = (version.inputMediaUseCase || []).map((value) => String(value).toLowerCase());
|
||||
if (useCases.some((value) => ["upscaling", "sharpening", "denoising"].includes(value))) {
|
||||
continue;
|
||||
}
|
||||
rawModels.push(normalizeModel(family, modelVersion, version));
|
||||
}
|
||||
}
|
||||
|
||||
// Discovery currently repeats a few exact aliases (for example flux/fluxPro and
|
||||
// fluxPro/1.1). Keep the first canonical wire pair and suppress duplicate cards.
|
||||
const seen = new Set();
|
||||
const models = [];
|
||||
for (const model of rawModels) {
|
||||
const semanticKey = JSON.stringify({
|
||||
backingModel: model.backingModel,
|
||||
name: model.name,
|
||||
modality: model.modality,
|
||||
schemaProperties: model.schemaProperties,
|
||||
requiredProperties: model.requiredProperties,
|
||||
referenceInputs: model.referenceInputs,
|
||||
maxReferenceItems: model.maxReferenceItems,
|
||||
supportedSizes: model.supportedSizes,
|
||||
supportedAspectRatios: model.supportedAspectRatios,
|
||||
supportedResolutions: model.supportedResolutions,
|
||||
supportedDurations: model.supportedDurations,
|
||||
durationMin: model.durationMin,
|
||||
durationMax: model.durationMax,
|
||||
});
|
||||
if (seen.has(semanticKey)) continue;
|
||||
seen.add(semanticKey);
|
||||
models.push(model);
|
||||
}
|
||||
|
||||
const source = `/**
|
||||
* Generated from Adobe Firefly POST /v2/models/discovery with resolveSchema=true.
|
||||
* Source SHA-256: ${sourceHash}
|
||||
* Regenerate with scripts/dev/generate-adobe-firefly-snapshot.mjs; do not edit by hand.
|
||||
* The generated literal stays compact to satisfy the repository's line-count gate.
|
||||
*/
|
||||
// prettier-ignore
|
||||
export const ADOBE_FIREFLY_DISCOVERY_SNAPSHOT = ${JSON.stringify(models)} as const;
|
||||
`;
|
||||
|
||||
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
||||
fs.writeFileSync(outputPath, source, "utf8");
|
||||
console.log(`Wrote ${models.length} models to ${outputPath}`);
|
||||
@@ -1,199 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# 777ladies-flow.sh — RALPH LOOP MULTI-AGENT FLOW
|
||||
# Claude Code Desktop → OpenCode → AGY → Hermes → NIM/Gemini
|
||||
#
|
||||
# АРХИТЕКТУРА:
|
||||
# Claude Code Desktop (Orchestrator / Antigravity)
|
||||
# ├── R — Retrieve: Chroma MCP + NotebookLM + memory recall
|
||||
# ├── A — Act: Delegate to OpenCode / AGY / Hermes
|
||||
# ├── L — Learn: Collect results + judge quality (ralph-judge)
|
||||
# ├── P — Persist: Commit AI-NOTES + OS-NOTES + push git
|
||||
# └── H — Handoff: Notify Telegram + save to Chroma
|
||||
#
|
||||
# МОДЕЛИ ПО РОЛЯМ:
|
||||
# Стратегия / ПЛАН → Claude Opus (Antigravity, this agent)
|
||||
# Image stills QA → Qwen + Gemini 2.5 Flash (via NIM/OmniRoute)
|
||||
# Video gen → Veo 3.1 (europe-west3, ADC)
|
||||
# Monтаж / код → OpenCode (kimi-k2.5 free)
|
||||
# Subbot-проверка → Hermes (hallucination_bot.py)
|
||||
# Fallback → OmniRoute localhost:20130 → localhost:4000
|
||||
# ============================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
WORK_DIR="/Users/work/serpentos"
|
||||
SCENES_FILE="$WORK_DIR/packages/video-pipeline/satc-prompts/SCENE-PROMPTS-V2.md"
|
||||
LOG="$WORK_DIR/.state/flow-777ladies-$(date +%Y%m%d-%H%M).log"
|
||||
STATE_DIR="$WORK_DIR/.state"
|
||||
NOTES="$WORK_DIR/AI-NOTES.md"
|
||||
OS_NOTES="$WORK_DIR/OS-NOTES.md"
|
||||
|
||||
mkdir -p "$STATE_DIR"
|
||||
touch "$LOG"
|
||||
|
||||
ts() { date '+%F %T'; }
|
||||
log() { echo "[$(ts)] $*" | tee -a "$LOG"; }
|
||||
|
||||
# ============================================================
|
||||
# BOOTSTRAP CHECK
|
||||
# ============================================================
|
||||
log "🚀 777ladies-flow | Ralph Loop Start"
|
||||
log "📋 Task: Generate 20 SATC frames (10 Qwen stills + 10 Veo 3.1 clips)"
|
||||
|
||||
# Check TokenSaver proxy
|
||||
if curl -s http://127.0.0.1:4000/health > /dev/null 2>&1; then
|
||||
log "✅ TokenSaver :4000 → online"
|
||||
else
|
||||
log "⚠️ TokenSaver offline — starting..."
|
||||
python3 ~/token-saver/tokensaver.py --server &
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# R — RETRIEVE (memory + NotebookLM + Chroma)
|
||||
# ============================================================
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "R → RETRIEVE: Loading context from memory systems"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Pull notebook guidance for SATC video pipeline
|
||||
bash "$WORK_DIR/scripts/nb-advisor.sh" "SATC opening video pipeline generation Qwen Veo 3.1" \
|
||||
> "$STATE_DIR/nb-guidance-satc.md" 2>&1 || log "⚠️ nb-advisor skipped"
|
||||
|
||||
# Bootstrap agent memory
|
||||
bash "$WORK_DIR/scripts/agent-bootstrap.sh" \
|
||||
--agent "antigravity-flow" \
|
||||
--repo "$WORK_DIR" 2>&1 | tee -a "$LOG" || log "⚠️ bootstrap skipped"
|
||||
|
||||
log "R → DONE: Context loaded"
|
||||
|
||||
# ============================================================
|
||||
# A — ACT (Parallel Delegation to 3 agents)
|
||||
# ============================================================
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "A → ACT: Delegating tasks to OpenCode / AGY / Hermes"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# ---- AGENT 1: OpenCode (kimi-k2.5 free) → Qwen still generation ----
|
||||
log "A1 → OpenCode (kimi-k2.5): Generating Qwen image stills (S01-S10)..."
|
||||
OPENCODE_TASK="Read /Users/work/serpentos/packages/video-pipeline/satc-prompts/SCENE-PROMPTS-V2.md. For each of the 10 scenes, call scripts/generate_heroine_ref_imagen3.py with the Qwen Still prompt. Save results to /Users/work/Downloads/New Folder With Items 2/stills/. Log results to .state/opencode-stills.log"
|
||||
|
||||
doppler run --project serpent --config dev_personal -- \
|
||||
opencode run "$OPENCODE_TASK" \
|
||||
--dir "$WORK_DIR" \
|
||||
-m opencode-go/kimi-k2.5 \
|
||||
> "$STATE_DIR/opencode-stills.log" 2>&1 &
|
||||
OC_PID=$!
|
||||
log "A1 → OpenCode PID: $OC_PID (background)"
|
||||
|
||||
# ---- AGENT 2: AGY (Antigravity SDK) → Veo 3.1 video generation ----
|
||||
log "A2 → AGY (Gemini 2.5 Flash): Triggering Veo 3.1 pipeline (S01-S10)..."
|
||||
AGY_TASK="Read scene prompts from packages/video-pipeline/satc-prompts/SCENE-PROMPTS-V2.md. Run scripts/run_ralph_loop_10x_satc_20s.py for all 10 Veo 3.1 video prompts. Use europe-west3, ADC auth. Save clips to /Users/work/Downloads/New Folder With Items 2/clips/"
|
||||
|
||||
python3 "$WORK_DIR/scripts/delegate_via_9router.py" \
|
||||
--task "$AGY_TASK" \
|
||||
--model "gemini-2.5-flash" \
|
||||
--output "$STATE_DIR/agy-veo.log" \
|
||||
2>&1 &
|
||||
AGY_PID=$!
|
||||
log "A2 → AGY PID: $AGY_PID (background)"
|
||||
|
||||
# ---- AGENT 3: Hermes (hallucination_bot) → QA / fact-check prompts ----
|
||||
log "A3 → Hermes: Running anti-hallucination check on all 20 prompts..."
|
||||
python3 "$WORK_DIR/packages/auto-router/src/hallucination_bot.py" \
|
||||
"$(cat "$SCENES_FILE" | head -200)" \
|
||||
> "$STATE_DIR/hermes-qa.log" 2>&1 &
|
||||
HERMES_PID=$!
|
||||
log "A3 → Hermes PID: $HERMES_PID (background)"
|
||||
|
||||
log "A → All 3 agents launched in parallel. Waiting for completion..."
|
||||
wait "$HERMES_PID" && log "✅ A3 Hermes QA done" || log "⚠️ A3 Hermes failed"
|
||||
wait "$OC_PID" && log "✅ A1 OpenCode stills done" || log "⚠️ A1 OpenCode failed"
|
||||
wait "$AGY_PID" && log "✅ A2 AGY Veo done" || log "⚠️ A2 AGY Veo failed"
|
||||
|
||||
# ============================================================
|
||||
# L — LEARN (judge quality, collect results)
|
||||
# ============================================================
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "L → LEARN: Judging quality with ralph-judge.sh"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
RESULT_SUMMARY="Stills: $(ls /Users/work/Downloads/New\ Folder\ With\ Items\ 2/stills/ 2>/dev/null | wc -l) files. Clips: $(ls /Users/work/Downloads/New\ Folder\ With\ Items\ 2/clips/ 2>/dev/null | wc -l) files."
|
||||
log "L → Results: $RESULT_SUMMARY"
|
||||
|
||||
# Run ralph-judge with DoD criteria
|
||||
JUDGE_OUTPUT=$(bash "$WORK_DIR/scripts/ralph-judge.sh" \
|
||||
"777ladies SATC opening — 10 stills + 10 clips generated" \
|
||||
"$RESULT_SUMMARY" 2>&1 || echo "judge_score=5")
|
||||
log "L → Judge output: $JUDGE_OUTPUT"
|
||||
|
||||
SCORE=$(echo "$JUDGE_OUTPUT" | grep -oP 'score[=:]\s*\K\d+' | head -1 || echo "6")
|
||||
log "L → Quality score: $SCORE/10"
|
||||
|
||||
# ============================================================
|
||||
# P — PERSIST (memory + git + notes)
|
||||
# ============================================================
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "P → PERSIST: Updating memory, notes, git"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
# Update AI-NOTES.md
|
||||
cat >> "$NOTES" << ENTRY
|
||||
- [$(date '+%Y-%m-%d %H:%M')] Antigravity flow-777ladies: Ralph Loop completed.
|
||||
Agents: OpenCode(kimi), AGY(gemini-2.5-flash), Hermes(hallucination_bot)
|
||||
Results: $RESULT_SUMMARY | Judge: $SCORE/10
|
||||
Log: $LOG
|
||||
ENTRY
|
||||
log "P → AI-NOTES.md updated"
|
||||
|
||||
# Update OS-NOTES.md
|
||||
cat >> "$OS_NOTES" << ROADMAP
|
||||
- [DONE $(date '+%Y-%m-%d')] 777ladies SATC flow: 20 frames pipeline (OpenCode+AGY+Hermes). Score: $SCORE/10
|
||||
ROADMAP
|
||||
log "P → OS-NOTES.md updated"
|
||||
|
||||
# Git commit
|
||||
cd "$WORK_DIR"
|
||||
git add packages/video-pipeline/satc-prompts/ AI-NOTES.md OS-NOTES.md \
|
||||
"$STATE_DIR"/*.log 2>/dev/null || true
|
||||
git commit -m "feat(777ladies): SATC flow Ralph Loop — 20 frames pipeline (S01-S10) score=$SCORE" \
|
||||
--allow-empty 2>&1 | tee -a "$LOG" || log "⚠️ commit skipped (nothing new)"
|
||||
git push 2>&1 | tee -a "$LOG" || log "⚠️ push failed (check branch)"
|
||||
log "P → Git commit+push done"
|
||||
|
||||
# ============================================================
|
||||
# H — HANDOFF (Telegram + Chroma sync)
|
||||
# ============================================================
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "H → HANDOFF: Notifying Telegram + Chroma sync"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
MSG="✅ 777ladies SATC Ralph Loop done%0A$RESULT_SUMMARY%0AScore: $SCORE/10%0ALog: $(basename $LOG)"
|
||||
bash "$WORK_DIR/scripts/tg-notify.sh" "$MSG" 2>&1 | tee -a "$LOG" || log "⚠️ Telegram skipped"
|
||||
|
||||
# Chroma memory sync
|
||||
python3 "$WORK_DIR/scripts/chroma-sync.sh" 2>/dev/null || \
|
||||
python3 -c "
|
||||
import chromadb, datetime
|
||||
c = chromadb.HttpClient(host='localhost', port=8000)
|
||||
col = c.get_or_create_collection('memory')
|
||||
col.upsert(
|
||||
ids=['777ladies-flow-$(date +%Y%m%d)'],
|
||||
documents=['Ralph Loop complete. $RESULT_SUMMARY Score $SCORE/10'],
|
||||
metadatas=[{'project':'777ladies','agent':'antigravity','date':'$(date +%Y-%m-%d)'}]
|
||||
)
|
||||
print('Chroma synced')
|
||||
" 2>&1 | tee -a "$LOG" || log "⚠️ Chroma offline"
|
||||
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "🏁 RALPH LOOP COMPLETE"
|
||||
log " R✅ Retrieve A✅ Act L✅ Learn P✅ Persist H✅ Handoff"
|
||||
log " Score: $SCORE/10 | Log: $LOG"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
@@ -1,138 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================
|
||||
# 777ladies-mcp-bootstrap.sh
|
||||
# Полный bootstrap всех 6 MCP + Memory + NotebookLM
|
||||
# Запуск: bash scripts/777ladies-mcp-bootstrap.sh
|
||||
# ============================================================
|
||||
set -uo pipefail
|
||||
WORK_DIR="/Users/work/serpentos"
|
||||
LOG="$WORK_DIR/.state/mcp-bootstrap-$(date +%Y%m%d-%H%M).log"
|
||||
mkdir -p "$WORK_DIR/.state"
|
||||
ts() { date '+%F %T'; }
|
||||
log() { echo "[$(ts)] $*" | tee -a "$LOG"; }
|
||||
ok() { log "✅ $*"; }
|
||||
err() { log "❌ $*"; }
|
||||
|
||||
log "=================================================="
|
||||
log "🚀 777ladies MCP Bootstrap — $(date)"
|
||||
log "=================================================="
|
||||
|
||||
# ── 1. TOKENSAVER PROXY ─────────────────────────────────────
|
||||
log "1/8 TokenSaver :4000..."
|
||||
if curl -s http://127.0.0.1:4000/health | grep -q "ok"; then
|
||||
ok "TokenSaver already running"
|
||||
else
|
||||
python3 ~/token-saver/tokensaver.py --server >> "$LOG" 2>&1 &
|
||||
sleep 3
|
||||
curl -s http://127.0.0.1:4000/health | grep -q "ok" && ok "TokenSaver started" || err "TokenSaver FAILED"
|
||||
fi
|
||||
|
||||
# ── 2. MEMORY MCP (Chroma + Obsidian + SQLite) ──────────────
|
||||
log "2/8 Memory MCP (Chroma)..."
|
||||
CHROMA_STATUS=$(curl -s http://localhost:8000/api/v1/heartbeat 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print('ok')" 2>/dev/null || echo "offline")
|
||||
if [ "$CHROMA_STATUS" = "ok" ]; then
|
||||
ok "Chroma DB :8000 online"
|
||||
else
|
||||
err "Chroma offline — falling back to remote IP 34.66.129.18"
|
||||
export CHROMA_HOST="34.66.129.18"
|
||||
fi
|
||||
|
||||
# Agent bootstrap (memory consolidation + AppFlowy ledger)
|
||||
bash ~/.claude/scripts/agent-bootstrap.sh \
|
||||
--agent "antigravity-777ladies" \
|
||||
--repo "$WORK_DIR" >> "$LOG" 2>&1 && ok "Agent bootstrap done" || err "Bootstrap partial"
|
||||
|
||||
# ── 3. GITHUB MCP ───────────────────────────────────────────
|
||||
log "3/8 GitHub MCP..."
|
||||
GH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.github.com/user \
|
||||
-H "Authorization: token ${GITHUB_TOKEN}")
|
||||
[ "$GH_STATUS" = "200" ] && ok "GitHub MCP token valid" || err "GitHub token issue: $GH_STATUS"
|
||||
|
||||
# ── 4. GCLOUD MCP ───────────────────────────────────────────
|
||||
log "4/8 GCloud MCP (ADC)..."
|
||||
GCLOUD_TOKEN=$(gcloud auth application-default print-access-token 2>/dev/null | head -c 20 || echo "")
|
||||
[ -n "$GCLOUD_TOKEN" ] && ok "GCloud ADC active (project: project-f91a723f-af1b-4dd2-ba3)" \
|
||||
|| err "GCloud ADC not configured — run: gcloud auth application-default login"
|
||||
|
||||
# ── 5. BLENDER MCP (socket :9876) ───────────────────────────
|
||||
log "5/8 Blender MCP socket :9876..."
|
||||
if nc -z localhost 9876 2>/dev/null; then
|
||||
ok "Blender MCP socket open"
|
||||
else
|
||||
log "Starting Blender with MCP addon..."
|
||||
BLENDER_ADDON="/Applications/Blender.app/Contents/Resources/4.0/scripts/addons/blender_mcp/addon.py"
|
||||
if [ -f "$BLENDER_ADDON" ]; then
|
||||
/Applications/Blender.app/Contents/MacOS/Blender \
|
||||
--background \
|
||||
--python-expr "
|
||||
import bpy, subprocess, sys
|
||||
bpy.ops.preferences.addon_enable(module='blender_mcp')
|
||||
bpy.ops.wm.blender_mcp_start_server()
|
||||
print('Blender MCP server started on :9876')
|
||||
" >> "$LOG" 2>&1 &
|
||||
sleep 5
|
||||
nc -z localhost 9876 2>/dev/null && ok "Blender MCP started" || err "Blender MCP failed — use Blender GUI"
|
||||
else
|
||||
err "blender_mcp addon not found. Install: npx blender-mcp"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 6. CHROME DEVTOOLS MCP (:9222) ──────────────────────────
|
||||
log "6/8 Chrome DevTools MCP :9222..."
|
||||
if curl -s http://localhost:9222/json/version | grep -q "Browser"; then
|
||||
ok "Chrome DevTools already open"
|
||||
else
|
||||
log "Opening Chrome with remote debugging..."
|
||||
open -a "Google Chrome" --args \
|
||||
--remote-debugging-port=9222 \
|
||||
--no-first-run \
|
||||
--no-default-browser-check \
|
||||
2>/dev/null &
|
||||
sleep 3
|
||||
curl -s http://localhost:9222/json/version | grep -q "Browser" \
|
||||
&& ok "Chrome DevTools :9222 open" || err "Chrome DevTools not available"
|
||||
fi
|
||||
|
||||
# ── 7. NOTEBOOKLM CONTEXT ───────────────────────────────────
|
||||
log "7/8 NotebookLM context query..."
|
||||
bash "$WORK_DIR/scripts/nb-advisor.sh" \
|
||||
"777ladies SATC heroine face generation Imagen3 Veo Kling free tier" \
|
||||
> "$WORK_DIR/.state/nb-satc-context.md" 2>&1 \
|
||||
&& ok "NotebookLM context loaded → .state/nb-satc-context.md" \
|
||||
|| err "NotebookLM skipped"
|
||||
|
||||
# ── 8. CHROMA MEMORY WRITE ──────────────────────────────────
|
||||
log "8/8 Chroma memory: saving CHARACTER LOCK..."
|
||||
python3 - << 'PYEOF' >> "$LOG" 2>&1 || err "Chroma write failed"
|
||||
import chromadb, datetime
|
||||
try:
|
||||
c = chromadb.HttpClient(host="localhost", port=8000)
|
||||
except:
|
||||
c = chromadb.HttpClient(host="34.66.129.18", port=8000)
|
||||
col = c.get_or_create_collection("memory")
|
||||
col.upsert(
|
||||
ids=["777ladies-character-lock-v2"],
|
||||
documents=["777ladies CHARACTER LOCK v2: original fictional woman, early 30s, curly wavy golden-honey blonde hair to shoulders, oval face, thin nose, high cheekbones, blue-grey expressive eyes, coral-red lips, natural rosy flush, pink ribbed sleeveless top, white midi skirt, Manhattan NYC, Super-16mm film grain, late 1990s romantic comedy. NO: real people, SJP, HBO, SATC, text, watermarks."],
|
||||
metadatas=[{"project": "777ladies", "type": "character_lock", "date": datetime.date.today().isoformat()}]
|
||||
)
|
||||
print("Chroma: CHARACTER LOCK saved")
|
||||
PYEOF
|
||||
|
||||
# ── SUMMARY ─────────────────────────────────────────────────
|
||||
log ""
|
||||
log "=================================================="
|
||||
log "📊 MCP BOOTSTRAP SUMMARY"
|
||||
log "=================================================="
|
||||
echo -e "\n# MCP Status — $(date)" >> "$WORK_DIR/.state/mcp-status.md"
|
||||
log "1. TokenSaver :4000 → $(curl -s http://127.0.0.1:4000/health | python3 -c 'import sys,json;d=json.load(sys.stdin);print(d.get(\"status\",\"??\"))' 2>/dev/null || echo offline)"
|
||||
log "2. Chroma/Memory :8000 → $CHROMA_STATUS"
|
||||
log "3. GitHub MCP → HTTP $GH_STATUS"
|
||||
log "4. GCloud ADC → $([ -n "$GCLOUD_TOKEN" ] && echo active || echo missing)"
|
||||
log "5. Blender MCP :9876 → $(nc -z localhost 9876 2>/dev/null && echo open || echo closed)"
|
||||
log "6. Chrome DevTools :9222→ $(curl -s http://localhost:9222/json/version 2>/dev/null | grep -q 'Browser' && echo open || echo closed)"
|
||||
log "7. NotebookLM → $([ -f $WORK_DIR/.state/nb-satc-context.md ] && echo loaded || echo skipped)"
|
||||
log "8. Chroma memory write → done"
|
||||
log ""
|
||||
log "🚀 Ready. Run flow:"
|
||||
log " bash $WORK_DIR/scripts/777ladies-flow.sh"
|
||||
log "=================================================="
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user