mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
chore(repo): nest quality-gate state under config/quality, declutter root (#3896)
Move the committed quality-gate state files out of the repo root into
config/quality/ and the v3.8.24 documentation audit into docs/ops/, then
re-point every gate script, test and .gitignore entry at the new paths.
Refresh docs/architecture/REPOSITORY_MAP.md (stale since v3.8.2) to match
the current layout.
Moved -> config/quality/:
quality-baseline.json, complexity-baseline.json, duplication-baseline.json,
file-size-baseline.json, test-discovery-baseline.json,
dependency-allowlist.json, .license-allowlist.json
(generated quality-metrics.json now written here too; still gitignored)
Moved -> docs/ops/:
DOCUMENTATION_AUDIT_REPORT.md (+ meta.json entry + fabricated-docs skip)
Path updates: check-{complexity,duplication,file-size,test-discovery,deps,
licenses,dead-code,cognitive-complexity,type-coverage}.mjs, check-quality-
ratchet.mjs, collect-metrics.mjs, check-tracked-artifacts.mjs (+ its test and
check-deps test). Also gitignore /logs/ (was untracked-not-ignored).
Tracked root files: 56 -> 48. Tool configs left in root on purpose: most are
auto-discovered there, and the tsconfig variants have location-relative
files:[] arrays that would need 46 path rewrites for a 2-file gain.
This commit is contained in:
committed by
GitHub
parent
7eff73651a
commit
320a9d3f29
7
.gitignore
vendored
7
.gitignore
vendored
@@ -203,8 +203,11 @@ pr_reviews*.json
|
||||
|
||||
# internal setup prompts with personal credentials — never commit
|
||||
CODEX-SETUP-PROMPT.md
|
||||
# Quality ratchet — métricas efêmeras (baseline é commitado, métricas não)
|
||||
quality-metrics.json
|
||||
# Quality ratchet — métricas efêmeras (baseline commitado em config/quality/; métricas não)
|
||||
config/quality/quality-metrics.json
|
||||
|
||||
# Runtime logs (diretório local, nunca versionado)
|
||||
/logs/
|
||||
-home-diegosouzapw-dev-automações-bots-yt-downloader-20260504 .txt
|
||||
-home-diegosouzapw-dev-automações-bots-yt-downloader-20260410 .txt
|
||||
docs/prompts/AGENT-OWNERSHIP-PROTOCOL.omniroute.md
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Repository Map"
|
||||
version: 3.8.2
|
||||
lastUpdated: 2026-05-13
|
||||
version: 3.8.26
|
||||
lastUpdated: 2026-06-15
|
||||
---
|
||||
|
||||
# Repository Map
|
||||
|
||||
> **One-line description for every directory and root file.**
|
||||
> Last updated: 2026-05-13 — OmniRoute v3.8.0
|
||||
> Last updated: 2026-06-15 — OmniRoute v3.8.26
|
||||
>
|
||||
> Use this map to navigate the codebase quickly. For deep dives, follow links to dedicated docs.
|
||||
|
||||
@@ -23,8 +23,13 @@ OmniRoute/
|
||||
├── docs/ # Public documentation (you are here)
|
||||
├── tests/ # All test suites (unit, integration, e2e, protocols-e2e)
|
||||
├── public/ # Next.js static assets, PWA manifest, service worker, icons
|
||||
├── config/ # Static config files
|
||||
├── config/ # Static config + quality-gate state (i18n, payloadRules, quality/)
|
||||
├── images/ # Marketing / README image assets
|
||||
├── @omniroute/ # Publishable companion packages (opencode-plugin, opencode-provider)
|
||||
├── skills/ # CLI/agent skill packs (cli-* + omni-* + config-codex-cli)
|
||||
├── examples/ # Sample plugins + omniroute-cmd-hello starter
|
||||
├── contrib/ # Community contributions (podman/)
|
||||
├── .source/ # Fumadocs source config (source.config.mjs + server/browser/dynamic)
|
||||
├── .github/ # GitHub Actions workflows + issue templates + PR template
|
||||
├── .husky/ # Git hooks (pre-commit, pre-push)
|
||||
├── .claude/ # Claude Code slash commands (project-scoped)
|
||||
@@ -34,6 +39,7 @@ OmniRoute/
|
||||
├── _mono_repo/ # Historic subprojects (cloud, site, vscode-extension)
|
||||
├── _references/ # Read-only reference clones from related OSS projects
|
||||
├── _tasks/ # Per-release task tracking files (informal)
|
||||
├── .build/ .worktrees/ dist/ # local build / git-worktree / build-output scratch (gitignored)
|
||||
├── .issues/ # Local issue cache (gitignored)
|
||||
├── .playwright-mcp/ # Playwright MCP test artifacts
|
||||
├── coverage/ # c8 coverage output (gitignored)
|
||||
@@ -48,45 +54,63 @@ OmniRoute/
|
||||
|
||||
## Root files
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **README.md** | Marketing landing page + quick start + feature matrix (see also `llm.txt`) |
|
||||
| **CHANGELOG.md** | Per-release changelog (auto-generated by `/version-bump-cc` skill) |
|
||||
| **LICENSE** | MIT license text |
|
||||
| **CLAUDE.md** | Project rules for Claude Code agents (hard rules, conventions, scenarios) |
|
||||
| **AGENTS.md** | Same as CLAUDE.md but for non-Claude AI agents (Codex, Cursor, etc.) |
|
||||
| **GEMINI.md** | Concise rules for Gemini-based agents (subset of CLAUDE.md) |
|
||||
| **CONTRIBUTING.md** | Contributor guide: setup, conventional commits, testing, PR flow |
|
||||
| **SECURITY.md** | Vulnerability reporting policy, supported versions, threat model |
|
||||
| **CODE_OF_CONDUCT.md** | Contributor Covenant — community behavior expectations |
|
||||
| **llm.txt** | Plain-text landing optimized for LLM crawlers (SEO for AI assistants) |
|
||||
| **Tuto_Qdrant.md** | Tutorial for enabling Qdrant vector memory — **integration currently dormant** (see banner; primary memory docs in `docs/frameworks/MEMORY.md`) |
|
||||
| **package.json** | npm manifest, scripts, dependencies, engines, c8 coverage gate |
|
||||
| **package-lock.json** | Locked dependency tree |
|
||||
| **tsconfig.json** | Root TypeScript config |
|
||||
| **tsconfig.typecheck-core.json** | Typecheck config for `src/` core |
|
||||
| **tsconfig.typecheck-noimplicit-core.json** | Strict (`noImplicitAny`) typecheck |
|
||||
| **tsconfig.tsbuildinfo** | TS incremental build cache (gitignored) |
|
||||
| **next.config.mjs** | Next.js 16 build configuration (standalone output) |
|
||||
| **next-env.d.ts** | Next.js auto-generated env types |
|
||||
| **eslint.config.mjs** | ESLint flat config (rules per project area) |
|
||||
| **prettier.config.mjs** | Prettier formatting rules |
|
||||
| **postcss.config.mjs** | PostCSS config for Tailwind/CSS pipeline |
|
||||
| **playwright.config.ts** | Playwright E2E test config |
|
||||
| **vitest.config.ts** | Vitest config (default suite) |
|
||||
| **vitest.mcp.config.ts** | Vitest config for MCP server / autoCombo / cache suites |
|
||||
| **sonar-project.properties** | SonarQube/SonarCloud config (code quality) |
|
||||
| **Dockerfile** | Multi-stage Docker build (builder → runner-base → runner-cli) |
|
||||
| **docker-compose.yml** | Dev compose with 4 profiles (base, cli, host, cliproxyapi) + redis sidecar |
|
||||
| **docker-compose.prod.yml** | Production compose (port 20130, redis, named volumes) |
|
||||
| **.dockerignore** | Files excluded from Docker context |
|
||||
| **fly.toml** | Fly.io deployment config (region `sin`, port 20128, /data volume) |
|
||||
| **.env.example** | Template env file (815 lines, auto-copied to `.env` on first install) |
|
||||
| **.gitignore** | Git ignore patterns |
|
||||
| **.npmignore** | npm publish exclusion list |
|
||||
| **.npmrc** | npm config (registry, lockfile policy) |
|
||||
| **.node-version** | Node version pin (used by nvm-compatible tools) |
|
||||
| **.nvmrc** | Node version pin for nvm |
|
||||
| File | Purpose |
|
||||
| ------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| **README.md** | Marketing landing page + quick start + feature matrix (see also `llm.txt`) |
|
||||
| **CHANGELOG.md** | Per-release changelog (auto-generated by `/version-bump-cc` skill) |
|
||||
| **LICENSE** | MIT license text |
|
||||
| **CLAUDE.md** | Project rules for Claude Code agents (hard rules, conventions, scenarios) |
|
||||
| **AGENTS.md** | Same as CLAUDE.md but for non-Claude AI agents (Codex, Cursor, etc.) |
|
||||
| **GEMINI.md** | Concise rules for Gemini-based agents (subset of CLAUDE.md) |
|
||||
| **CONTRIBUTING.md** | Contributor guide: setup, conventional commits, testing, PR flow |
|
||||
| **SECURITY.md** | Vulnerability reporting policy, supported versions, threat model |
|
||||
| **CODE_OF_CONDUCT.md** | Contributor Covenant — community behavior expectations |
|
||||
| **llm.txt** | Plain-text landing optimized for LLM crawlers (SEO for AI assistants) |
|
||||
| **package.json** | npm manifest, scripts, dependencies, engines, c8 coverage gate |
|
||||
| **package-lock.json** | Locked dependency tree |
|
||||
| **tsconfig.json** | Root TypeScript config |
|
||||
| **tsconfig.typecheck-core.json** | Typecheck config for `src/` core |
|
||||
| **tsconfig.typecheck-noimplicit-core.json** | Strict (`noImplicitAny`) typecheck |
|
||||
| **tsconfig.tsbuildinfo** | TS incremental build cache (gitignored) |
|
||||
| **next.config.mjs** | Next.js 16 build configuration (standalone output) |
|
||||
| **next-env.d.ts** | Next.js auto-generated env types |
|
||||
| **eslint.config.mjs** | ESLint flat config (rules per project area) |
|
||||
| **prettier.config.mjs** | Prettier formatting rules |
|
||||
| **postcss.config.mjs** | PostCSS config for Tailwind/CSS pipeline |
|
||||
| **playwright.config.ts** | Playwright E2E test config |
|
||||
| **vitest.config.ts** | Vitest config (default suite) |
|
||||
| **vitest.mcp.config.ts** | Vitest config for MCP server / autoCombo / cache suites |
|
||||
| **sonar-project.properties** | SonarQube/SonarCloud config (code quality) |
|
||||
| **Dockerfile** | Multi-stage Docker build (builder → runner-base → runner-cli) |
|
||||
| **docker-compose.yml** | Dev compose with 4 profiles (base, cli, host, cliproxyapi) + redis sidecar |
|
||||
| **docker-compose.prod.yml** | Production compose (port 20130, redis, named volumes) |
|
||||
| **.dockerignore** | Files excluded from Docker context |
|
||||
| **fly.toml** | Fly.io deployment config (region `sin`, port 20128, /data volume) |
|
||||
| **.env.example** | Template env file (auto-copied to `.env` on first install) |
|
||||
| **.gitignore** | Git ignore patterns |
|
||||
| **.npmignore** | npm publish exclusion list |
|
||||
| **.npmrc** | npm config (registry, lockfile policy) |
|
||||
| **.node-version** | Node version pin (used by nvm-compatible tools) |
|
||||
| **.nvmrc** | Node version pin for nvm |
|
||||
| **eslint.complexity.config.mjs** | ESLint config for the complexity ratchet (`scripts/check/check-complexity.mjs --config`) |
|
||||
| **eslint.sonarjs.config.mjs** | ESLint config for SonarJS rules (cognitive complexity / duplication) |
|
||||
| **source.config.ts** | Fumadocs `defineDocs` source config (feeds `.source/`) |
|
||||
| **knip.json** | Knip config — unused files/exports/deps (feeds the dead-code gate) |
|
||||
| **stryker.conf.json** | Stryker mutation-testing config |
|
||||
| **.size-limit.json** | size-limit bundle budget config |
|
||||
| **semcheck.yaml** | semcheck (spec↔code drift) config |
|
||||
| **promptfooconfig.yaml** | promptfoo eval config |
|
||||
| **.gitleaks.toml** | gitleaks secret-scan ruleset |
|
||||
| **.zizmor.yml** | zizmor GitHub-Actions security-lint config |
|
||||
| **socket.yml** | Socket.dev supply-chain config |
|
||||
| **news.json** | In-app release-notes feed (read by `src/shared/utils/releaseNotes.ts`) |
|
||||
| **flake.nix** / **flake.lock** | Nix dev-shell definition + lock |
|
||||
| **.env** | Local secrets (gitignored — generated from `.env.example`) |
|
||||
|
||||
> **Moved out of the root in v3.8.26 (declutter):**
|
||||
>
|
||||
> - **→ `config/quality/`:** `quality-baseline.json`, `complexity-baseline.json`, `duplication-baseline.json`, `file-size-baseline.json`, `test-discovery-baseline.json`, `dependency-allowlist.json`, `.license-allowlist.json`, and the generated `quality-metrics.json` (gitignored). See [`## config/`](#config--static-configs--quality-gate-state).
|
||||
> - **→ `docs/ops/`:** `DOCUMENTATION_AUDIT_REPORT.md`.
|
||||
|
||||
---
|
||||
|
||||
@@ -117,84 +141,84 @@ src/
|
||||
|
||||
### `src/app/` — App Router (Next.js 16)
|
||||
|
||||
| Path | Purpose |
|
||||
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `app/api/v1/` | Public OpenAI-compat API (~25 sub-routes: chat, completions, embeddings, files, batches, audio, images, videos, music, rerank, moderations, search, ws, agents, accounts, providers, etc.) |
|
||||
| `app/api/v1beta/` | Gemini-style API endpoints |
|
||||
| `app/api/playground/` | Playground Studio routes: `improve-prompt/` (POST — LLM prompt rewriter), `presets/` (GET list / POST create), `presets/[id]/` (GET / PUT / DELETE) — see `docs/frameworks/PLAYGROUND_STUDIO.md` |
|
||||
| `app/api/` (non-v1) | Management/admin routes (~60 directories: providers, combos, settings, mcp, a2a, evals, memory, skills, webhooks, compliance, resilience, monitoring, tunnels, cli-tools, etc.) |
|
||||
| `app/api/tools/agent-bridge/` | AgentBridge REST API — 12 routes (server control, agent state/DNS/mappings, bypass, cert, upstream-CA). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/AGENTBRIDGE.md §7`. |
|
||||
| `app/api/tools/traffic-inspector/` | Traffic Inspector REST + WS API — 16+ routes (requests, sessions, hosts, capture-modes, export, ws). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/TRAFFIC_INSPECTOR.md §8`. |
|
||||
| `app/a2a/` | A2A JSON-RPC 2.0 entry point (`POST /a2a`) |
|
||||
| `app/.well-known/agent.json/` | A2A Agent Card (discovery) |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~35 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) |
|
||||
| `app/(dashboard)/dashboard/search-tools/` | Search Tools Studio UI (3 tabs: Search/Scrape/Compare + SearchConceptCard + ProviderCatalog) — see `docs/frameworks/SEARCH_TOOLS_STUDIO.md` |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~30 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, etc.) |
|
||||
| Path | Purpose |
|
||||
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `app/api/v1/` | Public OpenAI-compat API (~25 sub-routes: chat, completions, embeddings, files, batches, audio, images, videos, music, rerank, moderations, search, ws, agents, accounts, providers, etc.) |
|
||||
| `app/api/v1beta/` | Gemini-style API endpoints |
|
||||
| `app/api/playground/` | Playground Studio routes: `improve-prompt/` (POST — LLM prompt rewriter), `presets/` (GET list / POST create), `presets/[id]/` (GET / PUT / DELETE) — see `docs/frameworks/PLAYGROUND_STUDIO.md` |
|
||||
| `app/api/` (non-v1) | Management/admin routes (~60 directories: providers, combos, settings, mcp, a2a, evals, memory, skills, webhooks, compliance, resilience, monitoring, tunnels, cli-tools, etc.) |
|
||||
| `app/api/tools/agent-bridge/` | AgentBridge REST API — 12 routes (server control, agent state/DNS/mappings, bypass, cert, upstream-CA). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/AGENTBRIDGE.md §7`. |
|
||||
| `app/api/tools/traffic-inspector/` | Traffic Inspector REST + WS API — 16+ routes (requests, sessions, hosts, capture-modes, export, ws). LOCAL_ONLY + SPAWN_CAPABLE. See `docs/frameworks/TRAFFIC_INSPECTOR.md §8`. |
|
||||
| `app/a2a/` | A2A JSON-RPC 2.0 entry point (`POST /a2a`) |
|
||||
| `app/.well-known/agent.json/` | A2A Agent Card (discovery) |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~35 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, activity, etc.) |
|
||||
| `app/(dashboard)/dashboard/search-tools/` | Search Tools Studio UI (3 tabs: Search/Scrape/Compare + SearchConceptCard + ProviderCatalog) — see `docs/frameworks/SEARCH_TOOLS_STUDIO.md` |
|
||||
| `app/(dashboard)/dashboard/` | Dashboard UI pages (~30 pages: providers, combos, settings, memory, skills, webhooks, evals, audit, batch, cache, costs, health, system, etc.) |
|
||||
| `app/(dashboard)/dashboard/memory/` | Memory Studio (plan 21): `page.tsx` (3-tab shell), `components/` (MemoryConceptCard, MemoryEngineStatus, EmbeddingSourceSelector, EditMemoryModal, RetrievePreview, QdrantConfigCard, RerankConfigCard), `components/tabs/` (MemoriesTab, PlaygroundTab, EngineTab), `hooks/` (useEngineStatus, useMemorySettings) |
|
||||
| `app/(dashboard)/dashboard/tools/agent-bridge/` | AgentBridge dashboard page — server card, 9 agent cards, setup wizard, model mapping, bypass list. i18n PT-BR + EN. See `docs/frameworks/AGENTBRIDGE.md`. |
|
||||
| `app/(dashboard)/dashboard/tools/traffic-inspector/` | Traffic Inspector dashboard page — DevTools split, 7 detail tabs, 4 capture mode toggles, session recorder, context colorization. i18n PT-BR + EN. See `docs/frameworks/TRAFFIC_INSPECTOR.md`. |
|
||||
| `app/(dashboard)/dashboard/activity/` | Activity feed page (Group B): `page.tsx` (server) + `ActivityFeedClient.tsx` + `components/{ActivityFeed,ActivityItem,DayHeader,EventTypeFilter}.tsx` — see `docs/architecture/MONITORING_SECTIONS.md` |
|
||||
| `app/(dashboard)/dashboard/costs/quota-share/` | Quota Sharing page (Group B): `QuotaSharePageClient.tsx` + `components/{PoolCard,DimensionBar,AllocationTable,BurnRateChart,QuotaConceptCard,CreatePoolModal,EditAllocationsModal}.tsx` + `hooks/{usePools,usePoolUsage,useLocalStoragePoolMigration}.ts` |
|
||||
| `app/(dashboard)/dashboard/costs/quota-share/plans/` | Provider plan config page (Group B): `page.tsx` + `ProviderPlanConfigClient.tsx` — quota dimensions per connection override |
|
||||
| `app/docs/` | Embedded documentation viewer (renders `docs/*.md`) |
|
||||
| `app/landing/` | Marketing landing page |
|
||||
| `app/login/`, `forgot-password/`, `forbidden/` | Auth-related pages |
|
||||
| `app/{400,401,403,408,429,500,502,503}/` | HTTP error pages |
|
||||
| `app/maintenance/`, `offline/`, `status/`, `privacy/`, `terms/`, `callback/` | Static/status pages |
|
||||
| `app/layout.tsx`, `page.tsx`, `manifest.ts`, `globals.css` | Root layout, home, PWA manifest, global CSS |
|
||||
| `app/error.tsx`, `global-error.tsx`, `not-found.tsx`, `loading.tsx` | Error boundaries |
|
||||
| `app/(dashboard)/dashboard/tools/agent-bridge/` | AgentBridge dashboard page — server card, 9 agent cards, setup wizard, model mapping, bypass list. i18n PT-BR + EN. See `docs/frameworks/AGENTBRIDGE.md`. |
|
||||
| `app/(dashboard)/dashboard/tools/traffic-inspector/` | Traffic Inspector dashboard page — DevTools split, 7 detail tabs, 4 capture mode toggles, session recorder, context colorization. i18n PT-BR + EN. See `docs/frameworks/TRAFFIC_INSPECTOR.md`. |
|
||||
| `app/(dashboard)/dashboard/activity/` | Activity feed page (Group B): `page.tsx` (server) + `ActivityFeedClient.tsx` + `components/{ActivityFeed,ActivityItem,DayHeader,EventTypeFilter}.tsx` — see `docs/architecture/MONITORING_SECTIONS.md` |
|
||||
| `app/(dashboard)/dashboard/costs/quota-share/` | Quota Sharing page (Group B): `QuotaSharePageClient.tsx` + `components/{PoolCard,DimensionBar,AllocationTable,BurnRateChart,QuotaConceptCard,CreatePoolModal,EditAllocationsModal}.tsx` + `hooks/{usePools,usePoolUsage,useLocalStoragePoolMigration}.ts` |
|
||||
| `app/(dashboard)/dashboard/costs/quota-share/plans/` | Provider plan config page (Group B): `page.tsx` + `ProviderPlanConfigClient.tsx` — quota dimensions per connection override |
|
||||
| `app/docs/` | Embedded documentation viewer (renders `docs/*.md`) |
|
||||
| `app/landing/` | Marketing landing page |
|
||||
| `app/login/`, `forgot-password/`, `forbidden/` | Auth-related pages |
|
||||
| `app/{400,401,403,408,429,500,502,503}/` | HTTP error pages |
|
||||
| `app/maintenance/`, `offline/`, `status/`, `privacy/`, `terms/`, `callback/` | Static/status pages |
|
||||
| `app/layout.tsx`, `page.tsx`, `manifest.ts`, `globals.css` | Root layout, home, PWA manifest, global CSS |
|
||||
| `app/error.tsx`, `global-error.tsx`, `not-found.tsx`, `loading.tsx` | Error boundaries |
|
||||
|
||||
### `src/lib/` — Core libraries (~50 modules)
|
||||
|
||||
| Module | Purpose |
|
||||
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `a2a/` | A2A protocol task manager, skills (5), streaming |
|
||||
| `acp/` | CLI Agent Registry (local CLI discovery — see `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`) |
|
||||
| `api/` | Shared API helpers (`requireManagementAuth`, validation) |
|
||||
| `auth/` | Session, password hashing, token validation |
|
||||
| `batches/` | OpenAI Batches API handlers |
|
||||
| `catalog/` | Provider catalog Zod validation + capability resolution |
|
||||
| `cloudAgent/` | Cloud Agents (Codex Cloud, Devin, Jules) — see `docs/frameworks/CLOUD_AGENT.md` |
|
||||
| `combos/` | Combo resolution + reorder helpers |
|
||||
| `audit/` | Activity feed helpers: `highLevelActions.ts` (allowlist + `isHighLevelAction()`), `activityIcons.ts` (action → icon/verb map), `timeline.ts` (groupByDay/relativeTime) — see `docs/architecture/MONITORING_SECTIONS.md` |
|
||||
| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` |
|
||||
| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) |
|
||||
| `config/` | Runtime config helpers |
|
||||
| `db/` | 45+ domain DB modules + 55 migrations (always go through here for SQLite) |
|
||||
| Module | Purpose |
|
||||
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `a2a/` | A2A protocol task manager, skills (5), streaming |
|
||||
| `acp/` | CLI Agent Registry (local CLI discovery — see `docs/frameworks/AGENT_PROTOCOLS_GUIDE.md`) |
|
||||
| `api/` | Shared API helpers (`requireManagementAuth`, validation) |
|
||||
| `auth/` | Session, password hashing, token validation |
|
||||
| `batches/` | OpenAI Batches API handlers |
|
||||
| `catalog/` | Provider catalog Zod validation + capability resolution |
|
||||
| `cloudAgent/` | Cloud Agents (Codex Cloud, Devin, Jules) — see `docs/frameworks/CLOUD_AGENT.md` |
|
||||
| `combos/` | Combo resolution + reorder helpers |
|
||||
| `audit/` | Activity feed helpers: `highLevelActions.ts` (allowlist + `isHighLevelAction()`), `activityIcons.ts` (action → icon/verb map), `timeline.ts` (groupByDay/relativeTime) — see `docs/architecture/MONITORING_SECTIONS.md` |
|
||||
| `compliance/` | Audit log + provider audit — see `docs/security/COMPLIANCE.md` |
|
||||
| `compression/` | Compression engine glue (engines live in `open-sse/services/compression/`) |
|
||||
| `config/` | Runtime config helpers |
|
||||
| `db/` | 45+ domain DB modules + 55 migrations (always go through here for SQLite) |
|
||||
| `quota/` | Quota Sharing Engine: `dimensions.ts` (types/Zod), `types.ts` (QuotaStore interface), `sqliteQuotaStore.ts`, `redisQuotaStore.ts`, `storeFactory.ts`, `fairShare.ts`, `burnRate.ts`, `planResolver.ts`, `planRegistry.ts`, `saturationSignals.ts`, `enforce.ts`, `spendRecorder.ts` — see `docs/routing/QUOTA_SHARE.md` |
|
||||
| `display/` | UI formatting helpers (cost, latency, etc.) |
|
||||
| `embeddings/` | Embeddings service helpers |
|
||||
| `env/` | Env variable parsing + validation |
|
||||
| `evals/` | Eval framework (suites, runner, runtime) — see `docs/frameworks/EVALS.md` |
|
||||
| `guardrails/` | PII masker, prompt injection, vision bridge — see `docs/security/GUARDRAILS.md` |
|
||||
| `jobs/` | Background jobs (cron-like) |
|
||||
| `memory/` | Conversational memory (SQLite FTS5 + sqlite-vec hybrid RRF + Qdrant tier 2) — see `docs/frameworks/MEMORY.md` |
|
||||
| `memory/embedding/` | Multi-source embedding layer: `index.ts` (resolver), `remote.ts`, `staticPotion.ts`, `transformersLocal.ts`, `cache.ts`, `types.ts` (plan 21) |
|
||||
| `memory/vectorStore.ts` | sqlite-vec v0.1.9 wrapper — KNN brute-force + hybrid RRF (FTS5 + vector, k=60). Lazy-init, degrades gracefully when sqlite-vec unavailable. (plan 21) |
|
||||
| `memory/reindex.ts` | `runReindexBatch()` — processes memories with `needs_reindex=1` in background; called by `POST /api/memory/reindex` and lazy-backfill path. (plan 21) |
|
||||
| `monitoring/` | Health checks, metrics emission |
|
||||
| `oauth/` | OAuth flows for 14 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, qwen, kiro, qoder, gitlab-duo, windsurf) |
|
||||
| `plugins/` | Plugin registry |
|
||||
| `promptCache/` | Anthropic-style prompt cache breakpoints |
|
||||
| `skills/` | Skills framework (built-in + marketplace + SkillsSH) — see `docs/frameworks/SKILLS.md` |
|
||||
| `playground/` | Playground Studio shared helpers: `codeExport.ts` (curl/Python/TS generator), `promptImprover.ts` (meta-prompt builder), `streamMetrics.ts` (pure TTFT/TPS), `types.ts` (pricing table) — see `docs/frameworks/PLAYGROUND_STUDIO.md` |
|
||||
| `webhookDispatcher.ts` | HMAC webhook delivery — see `docs/frameworks/WEBHOOKS.md` |
|
||||
| `cloudflaredTunnel.ts`, `ngrokTunnel.ts` | Tunnel managers — see `docs/ops/TUNNELS_GUIDE.md` |
|
||||
| `oneproxySync.ts`, `oneproxyRotator.ts` | 1proxy free proxy marketplace — see `docs/ops/PROXY_GUIDE.md` |
|
||||
| `cloudSync.ts`, `initCloudSync.ts` | Optional cloud sync of state |
|
||||
| `localDb.ts` | Re-export barrel for db modules (no logic — re-exports only) |
|
||||
| `cacheLayer.ts`, `idempotencyLayer.ts` | Request caching + idempotency |
|
||||
| (~30 more top-level files) | Specialized helpers (logEnv, modelsDevSync, piiSanitizer, etc.) |
|
||||
| `display/` | UI formatting helpers (cost, latency, etc.) |
|
||||
| `embeddings/` | Embeddings service helpers |
|
||||
| `env/` | Env variable parsing + validation |
|
||||
| `evals/` | Eval framework (suites, runner, runtime) — see `docs/frameworks/EVALS.md` |
|
||||
| `guardrails/` | PII masker, prompt injection, vision bridge — see `docs/security/GUARDRAILS.md` |
|
||||
| `jobs/` | Background jobs (cron-like) |
|
||||
| `memory/` | Conversational memory (SQLite FTS5 + sqlite-vec hybrid RRF + Qdrant tier 2) — see `docs/frameworks/MEMORY.md` |
|
||||
| `memory/embedding/` | Multi-source embedding layer: `index.ts` (resolver), `remote.ts`, `staticPotion.ts`, `transformersLocal.ts`, `cache.ts`, `types.ts` (plan 21) |
|
||||
| `memory/vectorStore.ts` | sqlite-vec v0.1.9 wrapper — KNN brute-force + hybrid RRF (FTS5 + vector, k=60). Lazy-init, degrades gracefully when sqlite-vec unavailable. (plan 21) |
|
||||
| `memory/reindex.ts` | `runReindexBatch()` — processes memories with `needs_reindex=1` in background; called by `POST /api/memory/reindex` and lazy-backfill path. (plan 21) |
|
||||
| `monitoring/` | Health checks, metrics emission |
|
||||
| `oauth/` | OAuth flows for 14 providers (claude, codex, antigravity, cursor, github, gemini, kimi-coding, kilocode, cline, qwen, kiro, qoder, gitlab-duo, windsurf) |
|
||||
| `plugins/` | Plugin registry |
|
||||
| `promptCache/` | Anthropic-style prompt cache breakpoints |
|
||||
| `skills/` | Skills framework (built-in + marketplace + SkillsSH) — see `docs/frameworks/SKILLS.md` |
|
||||
| `playground/` | Playground Studio shared helpers: `codeExport.ts` (curl/Python/TS generator), `promptImprover.ts` (meta-prompt builder), `streamMetrics.ts` (pure TTFT/TPS), `types.ts` (pricing table) — see `docs/frameworks/PLAYGROUND_STUDIO.md` |
|
||||
| `webhookDispatcher.ts` | HMAC webhook delivery — see `docs/frameworks/WEBHOOKS.md` |
|
||||
| `cloudflaredTunnel.ts`, `ngrokTunnel.ts` | Tunnel managers — see `docs/ops/TUNNELS_GUIDE.md` |
|
||||
| `oneproxySync.ts`, `oneproxyRotator.ts` | 1proxy free proxy marketplace — see `docs/ops/PROXY_GUIDE.md` |
|
||||
| `cloudSync.ts`, `initCloudSync.ts` | Optional cloud sync of state |
|
||||
| `localDb.ts` | Re-export barrel for db modules (no logic — re-exports only) |
|
||||
| `cacheLayer.ts`, `idempotencyLayer.ts` | Request caching + idempotency |
|
||||
| (~30 more top-level files) | Specialized helpers (logEnv, modelsDevSync, piiSanitizer, etc.) |
|
||||
|
||||
### `src/db/` — Database (45+ modules + 55 migrations)
|
||||
|
||||
| Subdir | Purpose |
|
||||
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `db/core.ts` | `getDbInstance()` singleton with WAL journaling |
|
||||
| `db/migrations/` | Versioned SQL files (idempotent, transactional). `073_memory_vec.sql` adds `memory_vec_meta` + `needs_reindex` column (plan 21). |
|
||||
| `db/playgroundPresets.ts` | CRUD module for Playground Studio presets (`listPlaygroundPresets`, `getPlaygroundPreset`, `createPlaygroundPreset`, `updatePlaygroundPreset`, `deletePlaygroundPreset`) |
|
||||
| `db/memoryVec.ts`| CRUD for `memory_vec_meta` (active_dim, embedding_signature, last_reset_at, vec_loaded) + `markMemoryNeedsReindex`, `getMemoryReindexQueue`, etc. (plan 21) |
|
||||
| `db/<domain>.ts` | One module per domain: providers, combos, apiKeys, users, sessions, usage, audit*log, webhooks, skills, memory_entries, cloud_agent_tasks, evals*\*, reasoning_cache, etc. |
|
||||
| Subdir | Purpose |
|
||||
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `db/core.ts` | `getDbInstance()` singleton with WAL journaling |
|
||||
| `db/migrations/` | Versioned SQL files (idempotent, transactional). `073_memory_vec.sql` adds `memory_vec_meta` + `needs_reindex` column (plan 21). |
|
||||
| `db/playgroundPresets.ts` | CRUD module for Playground Studio presets (`listPlaygroundPresets`, `getPlaygroundPreset`, `createPlaygroundPreset`, `updatePlaygroundPreset`, `deletePlaygroundPreset`) |
|
||||
| `db/memoryVec.ts` | CRUD for `memory_vec_meta` (active_dim, embedding_signature, last_reset_at, vec_loaded) + `markMemoryNeedsReindex`, `getMemoryReindexQueue`, etc. (plan 21) |
|
||||
| `db/<domain>.ts` | One module per domain: providers, combos, apiKeys, users, sessions, usage, audit*log, webhooks, skills, memory_entries, cloud_agent_tasks, evals*\*, reasoning_cache, etc. |
|
||||
|
||||
### `src/domain/`
|
||||
|
||||
@@ -455,9 +479,24 @@ open-sse/
|
||||
|
||||
---
|
||||
|
||||
## `config/` — Static Configs
|
||||
## `config/` — Static Configs + Quality-Gate State
|
||||
|
||||
Shipped configuration templates and sample files (referenced by setup wizard).
|
||||
Shipped configuration templates plus the committed quality-gate baselines
|
||||
(moved here from the repo root in v3.8.26 to keep the root lean).
|
||||
|
||||
| Path | Purpose |
|
||||
| --------------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| `config/i18n.json` | Locale list + metadata (canonical source for the 42-locale count) |
|
||||
| `config/i18n-schema.json` | JSON schema validating `i18n.json` |
|
||||
| `config/payloadRules.json` | Upstream payload sanitization rules |
|
||||
| `config/quality/quality-baseline.json` | Multi-metric ratchet baseline (`scripts/quality/check-quality-ratchet.mjs`) |
|
||||
| `config/quality/complexity-baseline.json` | Frozen ESLint-complexity baseline (`check-complexity.mjs`) |
|
||||
| `config/quality/duplication-baseline.json` | Frozen jscpd duplication baseline (`check-duplication.mjs`) |
|
||||
| `config/quality/file-size-baseline.json` | Frozen per-file size baseline (`check-file-size.mjs`) |
|
||||
| `config/quality/test-discovery-baseline.json` | Frozen orphan-test baseline (`check-test-discovery.mjs`) |
|
||||
| `config/quality/dependency-allowlist.json` | Approved dependencies allowlist (`check-deps.mjs`) |
|
||||
| `config/quality/.license-allowlist.json` | SPDX license allowlist (`check-licenses.mjs`) |
|
||||
| `config/quality/quality-metrics.json` | Ephemeral collected metrics (generated by `collect-metrics.mjs`; **gitignored**) |
|
||||
|
||||
---
|
||||
|
||||
@@ -484,15 +523,15 @@ Shipped configuration templates and sample files (referenced by setup wizard).
|
||||
|
||||
## `.claude/` — Claude Code Slash Commands
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------------------- | -------------------------------------------------- |
|
||||
| `commands/version-bump-cc.md` | `/version-bump-cc` — bump version + auto-changelog |
|
||||
| `commands/generate-release-cc.md` | `/generate-release-cc` — full release workflow |
|
||||
| `commands/deploy-vps-{local,akamai,both}-cc.md` | Deploy to VPS |
|
||||
| `commands/capture-release-evidences-cc.md` | Browser-record new features as WebP |
|
||||
| `commands/review-{prs,discussions}-cc.md` | Triage GitHub PRs/discussions |
|
||||
| `commands/{review-issues,implement-features}-cc.md` | Issue workflows |
|
||||
| `settings.local.json` | Per-project Claude Code settings |
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------- | -------------------------------------------------- |
|
||||
| `commands/version-bump-cc.md` | `/version-bump-cc` — bump version + auto-changelog |
|
||||
| `commands/generate-release-cc.md` | `/generate-release-cc` — full release workflow |
|
||||
| `commands/deploy-vps-{local,akamai,both}-cc.md` | Deploy to VPS |
|
||||
| `commands/capture-release-evidences-cc.md` | Browser-record new features as WebP |
|
||||
| `commands/review-{prs,discussions}-cc.md` | Triage GitHub PRs/discussions |
|
||||
| `commands/{review-issues,implement-features}-cc.md` | Issue workflows |
|
||||
| `settings.local.json` | Per-project Claude Code settings |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"PROXY_GUIDE",
|
||||
"SQLITE_RUNTIME",
|
||||
"COVERAGE_PLAN",
|
||||
"E2E_DASHBOARD_SHAKEDOWN_v3.8.0"
|
||||
"E2E_DASHBOARD_SHAKEDOWN_v3.8.0",
|
||||
"DOCUMENTATION_AUDIT_REPORT"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const ESLINT_BIN = path.join(ROOT, "node_modules", ".bin", "eslint");
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "quality-baseline.json")
|
||||
: path.join(ROOT, "config/quality/quality-baseline.json")
|
||||
);
|
||||
|
||||
const ESLINT_ARGS = [
|
||||
|
||||
@@ -19,7 +19,7 @@ const ROOT = process.cwd();
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "complexity-baseline.json")
|
||||
: path.join(ROOT, "config/quality/complexity-baseline.json")
|
||||
);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
const CONFIG_PATH = path.join(ROOT, "eslint.complexity.config.mjs");
|
||||
|
||||
@@ -28,7 +28,7 @@ const UPDATE = process.argv.includes("--update");
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "quality-baseline.json")
|
||||
: path.join(ROOT, "config/quality/quality-baseline.json")
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ import { execFileSync } from "node:child_process";
|
||||
import { assertNoStale } from "./lib/allowlist.mjs";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const ALLOWLIST_PATH = path.join(ROOT, "dependency-allowlist.json");
|
||||
const ALLOWLIST_PATH = path.join(ROOT, "config/quality/dependency-allowlist.json");
|
||||
|
||||
// Directories to exclude when discovering package.json files.
|
||||
// Using a set of path segment prefixes (relative to ROOT, forward slashes).
|
||||
@@ -141,16 +141,12 @@ export function queryNpmRegistry(pkgName, timeoutMs = 8000) {
|
||||
// Scope packages need URL-encoding for the `npm view` command.
|
||||
// `npm view` accepts scoped packages natively — no encoding needed.
|
||||
try {
|
||||
const raw = execFileSync(
|
||||
"npm",
|
||||
["view", pkgName, "time.created", "--json"],
|
||||
{
|
||||
encoding: "utf8",
|
||||
timeout: timeoutMs,
|
||||
// Suppress npm progress/warn output on stderr
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
}
|
||||
);
|
||||
const raw = execFileSync("npm", ["view", pkgName, "time.created", "--json"], {
|
||||
encoding: "utf8",
|
||||
timeout: timeoutMs,
|
||||
// Suppress npm progress/warn output on stderr
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
// npm view --json emits a quoted string or null/empty for missing fields
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) {
|
||||
@@ -165,7 +161,11 @@ export function queryNpmRegistry(pkgName, timeoutMs = 8000) {
|
||||
// npm exits with code 1 when the package is NOT found ("E404")
|
||||
const stderr = err.stderr?.toString() || "";
|
||||
const stdout = err.stdout?.toString() || "";
|
||||
if (stderr.includes("E404") || stdout.includes("E404") || stderr.includes("npm ERR! code E404")) {
|
||||
if (
|
||||
stderr.includes("E404") ||
|
||||
stdout.includes("E404") ||
|
||||
stderr.includes("npm ERR! code E404")
|
||||
) {
|
||||
return { exists: false, createdMs: null };
|
||||
}
|
||||
// Any other error (ETIMEDOUT, ENOTFOUND, etc.) = network/offline — return null
|
||||
|
||||
@@ -15,13 +15,23 @@ const ROOT = process.cwd();
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "duplication-baseline.json")
|
||||
: path.join(ROOT, "config/quality/duplication-baseline.json")
|
||||
);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
const EPS = 0.05; // tolerância de ruído de float (jscpd é determinístico; isto é margem)
|
||||
// Use local binary (pinned in package.json devDependencies — no registry download at CI time)
|
||||
const JSCPD_BIN = path.join(ROOT, "node_modules", ".bin", "jscpd");
|
||||
const JSCPD_FIXED_ARGS = ["src", "open-sse", "--reporters", "json", "--silent", "--min-tokens", "50", "--ignore", "**/*.test.ts,**/*.test.tsx,**/__tests__/**"];
|
||||
const JSCPD_FIXED_ARGS = [
|
||||
"src",
|
||||
"open-sse",
|
||||
"--reporters",
|
||||
"json",
|
||||
"--silent",
|
||||
"--min-tokens",
|
||||
"50",
|
||||
"--ignore",
|
||||
"**/*.test.ts,**/*.test.tsx,**/__tests__/**",
|
||||
];
|
||||
|
||||
/** Avalia a % atual contra o baseline. */
|
||||
export function evaluateDuplication(current, baseline, eps = EPS) {
|
||||
|
||||
@@ -273,8 +273,8 @@ const ENV_VAR_DENYLIST = new Set([
|
||||
// Gate allowlist constant names (JS identifiers, not env vars) — documented in
|
||||
// docs/architecture/QUALITY_GATES.md and docs/research/DISCOVERY_TOOL_DESIGN.md
|
||||
"KNOWN_STALE_DOC_REFS", // export const in check-docs-symbols.mjs
|
||||
"KNOWN_MISSING", // export const in check-fetch-targets.mjs
|
||||
"KNOWN_RAW_SQL", // export const in check-db-rules.mjs
|
||||
"KNOWN_MISSING", // export const in check-fetch-targets.mjs
|
||||
"KNOWN_RAW_SQL", // export const in check-db-rules.mjs
|
||||
]);
|
||||
|
||||
/** Endpoints that don't follow the standard route.ts pattern. */
|
||||
@@ -313,6 +313,9 @@ const SKIP_DOC_FILES = new Set([
|
||||
"docs/reference/PROVIDER_REFERENCE.md", // auto-generated from providers.ts
|
||||
"docs/reference/openapi.yaml",
|
||||
"docs/i18n", // translations — separate workflow
|
||||
// Point-in-time documentation audit (v3.8.24): intentionally references drift,
|
||||
// counts, and not-yet-existing files as part of documenting them — not living docs.
|
||||
"docs/ops/DOCUMENTATION_AUDIT_REPORT.md",
|
||||
]);
|
||||
|
||||
// ── File discovery ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -15,7 +15,9 @@ function getArg(name, fallback) {
|
||||
const i = process.argv.indexOf(name);
|
||||
return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback;
|
||||
}
|
||||
const BASELINE_PATH = path.resolve(getArg("--baseline", path.join(ROOT, "file-size-baseline.json")));
|
||||
const BASELINE_PATH = path.resolve(
|
||||
getArg("--baseline", path.join(ROOT, "config/quality/file-size-baseline.json"))
|
||||
);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
const SCAN_DIRS = ["src", "open-sse", "electron", "bin"];
|
||||
// Directories to skip when walking — build artifacts and installed packages.
|
||||
@@ -30,7 +32,8 @@ export function evaluateFileSizes(currentLocByFile, frozen, cap) {
|
||||
const improvements = [];
|
||||
for (const [file, loc] of Object.entries(currentLocByFile)) {
|
||||
if (file in frozen) {
|
||||
if (loc > frozen[file]) violations.push(`${file}: ${loc} > congelado ${frozen[file]} (não pode crescer)`);
|
||||
if (loc > frozen[file])
|
||||
violations.push(`${file}: ${loc} > congelado ${frozen[file]} (não pode crescer)`);
|
||||
else if (loc < frozen[file]) improvements.push([file, loc]);
|
||||
} else if (loc > cap) {
|
||||
violations.push(`${file}: ${loc} > cap ${cap} (arquivo novo acima do limite)`);
|
||||
@@ -49,7 +52,11 @@ function walk(dir, acc = []) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
if (!SKIP_DIRS.has(e.name)) walk(p, acc);
|
||||
} else if (/\.(ts|tsx)$/.test(e.name) && !/\.test\.tsx?$/.test(e.name) && !/\.d\.ts$/.test(e.name)) {
|
||||
} else if (
|
||||
/\.(ts|tsx)$/.test(e.name) &&
|
||||
!/\.test\.tsx?$/.test(e.name) &&
|
||||
!/\.d\.ts$/.test(e.name)
|
||||
) {
|
||||
acc.push(p);
|
||||
}
|
||||
}
|
||||
@@ -59,7 +66,8 @@ function walk(dir, acc = []) {
|
||||
function collectLoc() {
|
||||
const out = {};
|
||||
for (const d of SCAN_DIRS)
|
||||
for (const f of walk(path.join(ROOT, d))) out[path.relative(ROOT, f).replace(/\\/g, "/")] = countLines(f);
|
||||
for (const f of walk(path.join(ROOT, d)))
|
||||
out[path.relative(ROOT, f).replace(/\\/g, "/")] = countLines(f);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -76,7 +84,8 @@ function main() {
|
||||
|
||||
if (UPDATE && violations.length === 0 && improvements.length) {
|
||||
for (const [file, loc] of improvements) {
|
||||
if (loc <= cap) delete frozen[file]; // caiu para dentro do cap → sai do baseline
|
||||
if (loc <= cap)
|
||||
delete frozen[file]; // caiu para dentro do cap → sai do baseline
|
||||
else frozen[file] = loc; // continua grande mas encolheu → trava no novo valor
|
||||
}
|
||||
baseline.frozen = Object.fromEntries(Object.entries(frozen).sort());
|
||||
|
||||
@@ -22,7 +22,7 @@ import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const ALLOWLIST_PATH = path.join(ROOT, ".license-allowlist.json");
|
||||
const ALLOWLIST_PATH = path.join(ROOT, "config/quality/.license-allowlist.json");
|
||||
const CHECKER_BIN = path.join(ROOT, "node_modules", ".bin", "license-checker-rseidelsohn");
|
||||
|
||||
const VERBOSE = process.argv.includes("--verbose");
|
||||
@@ -39,7 +39,9 @@ const PRINT_JSON = process.argv.includes("--json");
|
||||
*/
|
||||
export function loadAllowlist() {
|
||||
if (!fs.existsSync(ALLOWLIST_PATH)) {
|
||||
throw new Error(`Allowlist not found: ${ALLOWLIST_PATH}. Create .license-allowlist.json first.`);
|
||||
throw new Error(
|
||||
`Allowlist not found: ${ALLOWLIST_PATH}. Create .license-allowlist.json first.`
|
||||
);
|
||||
}
|
||||
const raw = fs.readFileSync(ALLOWLIST_PATH, "utf-8");
|
||||
const parsed = JSON.parse(raw);
|
||||
@@ -86,7 +88,10 @@ export function classifyLicense(packageName, license, allowlist) {
|
||||
}
|
||||
|
||||
// 4. Denied
|
||||
return { status: "denied", reason: `license '${license}' not in allowlist and no exception registered for '${baseName}'` };
|
||||
return {
|
||||
status: "denied",
|
||||
reason: `license '${license}' not in allowlist and no exception registered for '${baseName}'`,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,7 +200,9 @@ function main() {
|
||||
|
||||
// Print exceptions (informational)
|
||||
if (exceptions.length > 0) {
|
||||
console.log("\n[check-licenses] Exceções registradas (não bloqueantes, revisar periodicamente):");
|
||||
console.log(
|
||||
"\n[check-licenses] Exceções registradas (não bloqueantes, revisar periodicamente):"
|
||||
);
|
||||
for (const { pkgKey, license } of exceptions) {
|
||||
const baseName = stripVersion(pkgKey);
|
||||
const exc = allowlist.exceptions[baseName];
|
||||
@@ -217,7 +224,9 @@ function main() {
|
||||
|
||||
// Print violations and fail
|
||||
if (violations.length > 0) {
|
||||
console.error("\n[check-licenses] ❌ VIOLAÇÕES DE POLÍTICA — deps de produção com licença não permitida:");
|
||||
console.error(
|
||||
"\n[check-licenses] ❌ VIOLAÇÕES DE POLÍTICA — deps de produção com licença não permitida:"
|
||||
);
|
||||
for (const { pkgKey, license, reason } of violations) {
|
||||
console.error(` ✗ ${pkgKey}: ${license}`);
|
||||
console.error(` → ${reason}`);
|
||||
@@ -231,11 +240,14 @@ function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("\n[check-licenses] ✅ Todos os pacotes de produção estão em conformidade com a política de licenças.");
|
||||
console.log(
|
||||
"\n[check-licenses] ✅ Todos os pacotes de produção estão em conformidade com a política de licenças."
|
||||
);
|
||||
}
|
||||
|
||||
// Run only when invoked directly (not when imported by tests)
|
||||
const isMain = process.argv[1] === pathToFileURL(import.meta.url).pathname ||
|
||||
const isMain =
|
||||
process.argv[1] === pathToFileURL(import.meta.url).pathname ||
|
||||
process.argv[1]?.endsWith("check-licenses.mjs");
|
||||
|
||||
if (isMain) {
|
||||
|
||||
@@ -34,7 +34,7 @@ const ROOT = process.cwd();
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "test-discovery-baseline.json")
|
||||
: path.join(ROOT, "config/quality/test-discovery-baseline.json")
|
||||
);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ import { execFileSync } from "node:child_process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const FORBIDDEN_PREFIXES = ["node_modules/", ".next/", "coverage/"];
|
||||
const FORBIDDEN_EXACT = new Set(["quality-metrics.json"]);
|
||||
const FORBIDDEN_EXACT = new Set([
|
||||
"quality-metrics.json", // legacy root location (still forbidden if a stale run writes it)
|
||||
"config/quality/quality-metrics.json", // current generated location (collect-metrics.mjs)
|
||||
]);
|
||||
|
||||
/**
|
||||
* Verifica se algum caminho na lista de arquivos rastreados corresponde a um
|
||||
@@ -80,7 +83,9 @@ function main() {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
console.error(`[tracked-artifacts] FAIL — ${violations.length} forbidden artifact(s) tracked by git:`);
|
||||
console.error(
|
||||
`[tracked-artifacts] FAIL — ${violations.length} forbidden artifact(s) tracked by git:`
|
||||
);
|
||||
for (const v of violations) {
|
||||
console.error(` ✗ ${v}`);
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ const UPDATE = process.argv.includes("--update");
|
||||
const BASELINE_PATH = path.resolve(
|
||||
process.argv.includes("--baseline")
|
||||
? process.argv[process.argv.indexOf("--baseline") + 1]
|
||||
: path.join(ROOT, "quality-baseline.json")
|
||||
: path.join(ROOT, "config/quality/quality-baseline.json")
|
||||
);
|
||||
|
||||
// Small epsilon to absorb float noise between runs (type-coverage can vary ~0.01%).
|
||||
|
||||
@@ -12,8 +12,12 @@ function getArg(name, fallback) {
|
||||
const i = process.argv.indexOf(name);
|
||||
return i >= 0 && process.argv[i + 1] ? process.argv[i + 1] : fallback;
|
||||
}
|
||||
const BASELINE = path.resolve(getArg("--baseline", path.join(cwd, "quality-baseline.json")));
|
||||
const METRICS = path.resolve(getArg("--metrics", path.join(cwd, "quality-metrics.json")));
|
||||
const BASELINE = path.resolve(
|
||||
getArg("--baseline", path.join(cwd, "config/quality/quality-baseline.json"))
|
||||
);
|
||||
const METRICS = path.resolve(
|
||||
getArg("--metrics", path.join(cwd, "config/quality/quality-metrics.json"))
|
||||
);
|
||||
const SUMMARY = getArg("--summary", null);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
// --allow-missing: pula métricas do baseline ausentes do metrics (em vez de falhar).
|
||||
@@ -73,7 +77,7 @@ for (const [key, spec] of Object.entries(baseline.metrics)) {
|
||||
status = "↑ melhorou";
|
||||
if (REQUIRE_TIGHTEN && base - current > tightenSlack) {
|
||||
tightenFailures.push(
|
||||
`${key}: melhorou de ${base} para ${current} (delta ${(base - current).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`,
|
||||
`${key}: melhorou de ${base} para ${current} (delta ${(base - current).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -86,7 +90,7 @@ for (const [key, spec] of Object.entries(baseline.metrics)) {
|
||||
status = "↑ melhorou";
|
||||
if (REQUIRE_TIGHTEN && current - base > tightenSlack) {
|
||||
tightenFailures.push(
|
||||
`${key}: melhorou de ${base} para ${current} (delta ${(current - base).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`,
|
||||
`${key}: melhorou de ${base} para ${current} (delta ${(current - base).toFixed(4)} > slack ${tightenSlack}) — rode 'npm run quality:ratchet -- --update' e commite o baseline apertado neste PR`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -99,10 +103,10 @@ const baselineKeys = new Set(Object.keys(baseline.metrics));
|
||||
const orphans = Object.keys(metrics).filter((k) => !baselineKeys.has(k));
|
||||
if (orphans.length > 0) {
|
||||
console.warn(
|
||||
`[quality-ratchet] WARN: ${orphans.length} métrica(s) órfã(s) — presente(s) em ${path.basename(METRICS)} mas sem entrada no baseline: ${orphans.join(", ")}`,
|
||||
`[quality-ratchet] WARN: ${orphans.length} métrica(s) órfã(s) — presente(s) em ${path.basename(METRICS)} mas sem entrada no baseline: ${orphans.join(", ")}`
|
||||
);
|
||||
console.warn(
|
||||
`[quality-ratchet] WARN: adicione ${orphans.length === 1 ? "essa métrica" : "essas métricas"} ao baseline (com value/direction) para que sejam catraceadas.`,
|
||||
`[quality-ratchet] WARN: adicione ${orphans.length === 1 ? "essa métrica" : "essas métricas"} ao baseline (com value/direction) para que sejam catraceadas.`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,7 +144,7 @@ if (failures.length) {
|
||||
if (REQUIRE_TIGHTEN && !UPDATE && tightenFailures.length > 0) {
|
||||
console.error(
|
||||
"[quality-ratchet] FALHOU (--require-tighten): métrica(s) melhoraram mas o baseline não foi apertado:\n" +
|
||||
tightenFailures.map((f) => " ✗ " + f).join("\n"),
|
||||
tightenFailures.map((f) => " ✗ " + f).join("\n")
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -250,6 +250,9 @@ if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
|
||||
coverageByModule();
|
||||
openapiCoverage();
|
||||
await i18nUiCoverage();
|
||||
fs.writeFileSync(path.join(cwd, "quality-metrics.json"), JSON.stringify(out, null, 2) + "\n");
|
||||
fs.writeFileSync(
|
||||
path.join(cwd, "config/quality/quality-metrics.json"),
|
||||
JSON.stringify(out, null, 2) + "\n"
|
||||
);
|
||||
console.log("[collect-metrics]", JSON.stringify(out));
|
||||
}
|
||||
|
||||
@@ -30,6 +30,11 @@ test("checkTrackedArtifacts: quality-metrics.json is flagged", () => {
|
||||
assert.equal(result.length, 1);
|
||||
});
|
||||
|
||||
test("checkTrackedArtifacts: config/quality/quality-metrics.json is flagged", () => {
|
||||
const result = checkTrackedArtifacts(["config/quality/quality-metrics.json"]);
|
||||
assert.equal(result.length, 1);
|
||||
});
|
||||
|
||||
test("checkTrackedArtifacts: symlink mode (120000) is flagged", () => {
|
||||
const result = checkTrackedArtifacts([], ["node_modules"]);
|
||||
assert.equal(result.length, 1);
|
||||
|
||||
@@ -18,17 +18,13 @@ test("no unapproved deps when all are allowlisted", () => {
|
||||
});
|
||||
|
||||
test("flags a dependency not on the allowlist (potential slopsquat)", () => {
|
||||
assert.deepEqual(
|
||||
findUnapprovedDeps(["react", "reactt-router"], new Set(["react"])),
|
||||
["reactt-router"]
|
||||
);
|
||||
assert.deepEqual(findUnapprovedDeps(["react", "reactt-router"], new Set(["react"])), [
|
||||
"reactt-router",
|
||||
]);
|
||||
});
|
||||
|
||||
test("flags multiple new deps, preserves order, de-dupes", () => {
|
||||
assert.deepEqual(
|
||||
findUnapprovedDeps(["a", "b", "a", "c"], new Set(["a"])),
|
||||
["b", "c"]
|
||||
);
|
||||
assert.deepEqual(findUnapprovedDeps(["a", "b", "a", "c"], new Set(["a"])), ["b", "c"]);
|
||||
});
|
||||
|
||||
// --- 6A.8: automatic workspace discovery ---
|
||||
@@ -64,7 +60,7 @@ test("6A.8: discoverManifests does NOT include node_modules, .next, or deep refe
|
||||
});
|
||||
|
||||
test("6A.8: all workspace package deps are in the allowlist (gate exits 0 with expanded scope)", () => {
|
||||
const allowlistPath = path.join(repoRoot, "dependency-allowlist.json");
|
||||
const allowlistPath = path.join(repoRoot, "config/quality/dependency-allowlist.json");
|
||||
const allowlist = new Set(JSON.parse(fs.readFileSync(allowlistPath, "utf8")).allowed || []);
|
||||
const manifests = discoverManifests(repoRoot);
|
||||
const allDeps: string[] = [];
|
||||
@@ -80,7 +76,11 @@ test("6A.8: all workspace package deps are in the allowlist (gate exits 0 with e
|
||||
);
|
||||
}
|
||||
const unapproved = findUnapprovedDeps(allDeps, allowlist);
|
||||
assert.deepEqual(unapproved, [], `expected all deps to be approved, got: ${unapproved.join(", ")}`);
|
||||
assert.deepEqual(
|
||||
unapproved,
|
||||
[],
|
||||
`expected all deps to be approved, got: ${unapproved.join(", ")}`
|
||||
);
|
||||
});
|
||||
|
||||
// --- 6A.8: stale-allowlist enforcement ---
|
||||
@@ -102,10 +102,9 @@ test("6A.8 stale: a dep removed from all manifests is detected as stale in allow
|
||||
test("7.8 evaluateDepAge: package older than 72h is OK", () => {
|
||||
const now = Date.now();
|
||||
const createdMs = now - 73 * 60 * 60 * 1000; // 73 hours ago
|
||||
const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now
|
||||
);
|
||||
const { ok, ageHours } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now);
|
||||
assert.ok(ok, "package older than 72h should be ok");
|
||||
assert.ok(ageHours >= 73, "ageHours should reflect elapsed time");
|
||||
});
|
||||
@@ -113,20 +112,18 @@ test("7.8 evaluateDepAge: package older than 72h is OK", () => {
|
||||
test("7.8 evaluateDepAge: package exactly at 72h boundary is OK (boundary inclusive)", () => {
|
||||
const now = Date.now();
|
||||
const createdMs = now - 72 * 60 * 60 * 1000; // exactly 72 hours ago
|
||||
const { ok } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now
|
||||
);
|
||||
const { ok } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now);
|
||||
assert.ok(ok, "package at exactly 72h should be ok (inclusive boundary)");
|
||||
});
|
||||
|
||||
test("7.8 evaluateDepAge: package published 1h ago is NOT OK", () => {
|
||||
const now = Date.now();
|
||||
const createdMs = now - 1 * 60 * 60 * 1000; // 1 hour ago
|
||||
const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now
|
||||
);
|
||||
const { ok, ageHours } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now);
|
||||
assert.ok(!ok, "package published 1h ago should NOT be ok");
|
||||
assert.ok(ageHours < 72, "ageHours should reflect < 72h");
|
||||
});
|
||||
@@ -135,28 +132,23 @@ test("7.8 evaluateDepAge: respects custom minAgeHours", () => {
|
||||
const now = Date.now();
|
||||
const createdMs = now - 10 * 60 * 60 * 1000; // 10 hours ago
|
||||
// With 24h minimum, 10h-old package should fail
|
||||
const { ok: failWith24 } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now,
|
||||
24
|
||||
);
|
||||
const { ok: failWith24 } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now, 24);
|
||||
assert.ok(!failWith24, "10h-old package should fail with 24h minimum");
|
||||
// With 6h minimum, 10h-old package should pass
|
||||
const { ok: passWith6 } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now,
|
||||
6
|
||||
);
|
||||
const { ok: passWith6 } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now, 6);
|
||||
assert.ok(passWith6, "10h-old package should pass with 6h minimum");
|
||||
});
|
||||
|
||||
test("7.8 evaluateDepAge: future timestamp (time.created in future) is NOT OK", () => {
|
||||
const now = Date.now();
|
||||
const createdMs = now + 1000; // 1s in future (clock skew edge case)
|
||||
const { ok, ageHours } = (evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number })(
|
||||
createdMs,
|
||||
now
|
||||
);
|
||||
const { ok, ageHours } = (
|
||||
evaluateDepAge as (a: number, b: number, c?: number) => { ok: boolean; ageHours: number }
|
||||
)(createdMs, now);
|
||||
assert.ok(!ok, "future timestamp should not be ok");
|
||||
assert.ok(ageHours < 0, "ageHours should be negative for future timestamp");
|
||||
});
|
||||
@@ -169,11 +161,17 @@ test("7.8 evaluateDepAge: future timestamp (time.created in future) is NOT OK",
|
||||
// - any real npm package like "react" is found and old → does NOT appear in any list
|
||||
|
||||
test("7.8 auditNewDepsRegistry: empty dep list returns all-empty results", () => {
|
||||
const result = (auditNewDepsRegistry as (deps: string[], minAge?: number, now?: number) => {
|
||||
notFound: string[];
|
||||
tooNew: Array<{ name: string; ageHours: number }>;
|
||||
offline: string[];
|
||||
})([], 72, Date.now());
|
||||
const result = (
|
||||
auditNewDepsRegistry as (
|
||||
deps: string[],
|
||||
minAge?: number,
|
||||
now?: number
|
||||
) => {
|
||||
notFound: string[];
|
||||
tooNew: Array<{ name: string; ageHours: number }>;
|
||||
offline: string[];
|
||||
}
|
||||
)([], 72, Date.now());
|
||||
assert.deepEqual(result.notFound, []);
|
||||
assert.deepEqual(result.tooNew, []);
|
||||
assert.deepEqual(result.offline, []);
|
||||
|
||||
Reference in New Issue
Block a user