Skip to content

feat: plugins framework (#2913) + disable-non-public-models (#3017) — integrated + security-hardened - #3041

Merged
diegosouzapw merged 90 commits into
release/v3.8.8from
feat/plugins-and-model-policy
Jun 1, 2026
Merged

diegosouzapw merged 90 commits into
release/v3.8.8from
feat/plugins-and-model-policy

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Resumo

Integra duas PRs de comunidade em uma branch consolidada → release/v3.8.8, resolvidas com segurança (sem regressão, sem quebra de padrão, sem abrir RCE):

⚠️ Esta PR é security-sensitive (plugins executam código). Recomendo revisão cuidadosa das partes de segurança antes do merge — ver seção Segurança abaixo.

#3017 — disable_non_public_models

  • Coluna disable_non_public_models coexiste com allowed_quotas + stream_default_mode no api_keys (merge cuidadoso do core).
  • Migration renumerada 077 → 089 (077 colidia com api_key_stream_default_mode).
  • Dynamic-imports do hot-path isModelAllowedForKey hoisted (perf).
  • Semântica travada: o gate de modelo-público só bloqueia modelo direto — auto/*, combos e qtSd/* (quota) bypassam (são combo-routed). Testado.

#2913 — Plugins framework

  • Strays não-canônicos db/migrations/* removidos; migrations plugin em src/lib/db/migrations/ renumeradas: 090_plugin_metrics, 091_plugin_analytics (esta estava FALTANDO — ver Segurança).
  • Implementações completadas (os testes do PR estavam à frente do código): analytics (sha real), config-validation (Zod), rate-limiting de hooks (sliding window 100/1s), integrity SHA-256 (SRI), upgrade semver-gated. ~400 linhas novas — revisar.

🔒 Segurança (3 correções + 1 masking pego)

  1. RCE remoto fechado: /api/plugins/ classificado como LOCAL_ONLY em routeGuard.ts (loopback enforçado antes da auth; Hard Rule feat(npm): add npm package publishing with CLI entry point #15/fix(ci): explicit .npmrc auth for npm publish #17). Plugins spawnam worker_threads + child_process — sem isso, JWT vazado via túnel = execução remota.
  2. child_process.exec opt-in: o sandbox só expõe child_process quando OMNIROUTE_PLUGINS_ALLOW_EXEC=1 (default OFF; senão throw). vm.runInContext sempre com timeout. Comentário documenta que vm≠fronteira de segurança (execução só-loopback).
  3. Rule fix(ui): fix Select dropdown dark theme inconsistency #12: todas as rotas /api/plugins → buildErrorBody() (sem err.message cru).
  4. Masking pego na auditoria: recordPluginExecution/getPluginAnalytics faziam INSERT/FROM plugin_analytics mas não havia migration (o teste criava a tabela inline, mascarando) → quebraria em produção. Adicionei a migration canônica 091 + alinhei o teste pra depender dela.

Validação (branch integrado)

npm run build completo é OOM nesta sessão (ambiental) — CI faz o build autoritativo.

Créditos no CHANGELOG: (#2913 — thanks @oyi77), (#3017 — thanks @androw). As PRs originais ficam abertas (não fechar — crédito).

🤖 Generated with Claude Code

oyi77 and others added 30 commits May 29, 2026 23:48
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Addresses all remaining code review feedback:

1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
   for proper process-level isolation. Complies with Rule 3 (no eval).
   Each plugin runs in a separate Node.js process with IPC communication.

2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
   API route files (list, install, scan, details, activate, deactivate, config).

3. **Env filtering**: Only safe env vars passed to plugin processes unless
   "env" permission is granted.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)

manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)

hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
- scanner: 9 tests (discovery, hidden dirs, validation, entry point, multiple)
- loader: 5 tests (type contracts, Plugin/PluginContext/PluginResult interfaces)
- manager: 6 tests (singleton, lifecycle methods, error on unknown)
- Total: 20 tests, all passing
- Path traversal guard: validate entryPoint stays within plugin dir
- install() now handles direct plugin directories (not just parent dirs)
- Non-null assertion replaced with explicit null check
- require efficiency: allowedModules map moved outside function
- Source wrapper: add newlines to prevent trailing comment issues
- Config validation: validate values against configSchema on save
- Dynamic import comment: clarify Node.js caching behavior

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
Addresses all remaining code review feedback:

1. **Loader rewrite**: Replaced Node.js vm module with child_process.fork()
   for proper process-level isolation. Complies with Rule 3 (no eval).
   Each plugin runs in a separate Node.js process with IPC communication.

2. **Auth on all routes**: Added requireManagementAuth to all 6 plugin
   API route files (list, install, scan, details, activate, deactivate, config).

3. **Env filtering**: Only safe env vars passed to plugin processes unless
   "env" permission is granted.

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
loader.ts:
- Fix IPC: use process.send()/process.on("message") instead of worker_threads.parentPort
- Fix ESM: write host script as .mjs (not .js) to force ESM execution
- Add timeout: 10s default on callHook() with Promise.race
- Add SIGKILL escalation: SIGTERM first, then SIGKILL after 3s grace
- Fix env filtering: use allowlist (safeKeys) instead of passing all env vars
- Clear timeout on successful IPC response (no timer leak)

manager.ts:
- Fix path traversal: use fs.realpath() instead of startsWith()
- Fix imports: use registerHook/unregisterHooks from hooks.ts
- Register hooks individually via registerHook(event, name, handler)

hooks.ts:
- Copied from feat/plugin-custom-hooks (canonical registry)
Phase 1 scaffold for automated provider discovery:
- DiscoveryConfig, DiscoveryResult types
- probeEndpoint() for URL availability checking
- scanProvider() stub (Phase 2 will implement real scanning)
- getDiscoveryResults() stub
- Default config: disabled (opt-in)
- index.ts: replace console.log/error with pino structured logging
- hooks.ts: remove redundant .sort() in emitHookBlocking/runOnResponse (already sorted on registration)
- manager.ts: add readFile import
…cation

Add `integrity: z.string().optional()` to `PluginManifestSchema` so the
SHA-256 tamper-detection check in loader.ts is no longer dead code.

The field is opt-in: a plugin without `integrity` loads normally (local-
operator trust model). When present, the loader verifies the entry-file
hash and throws on mismatch. The schema field includes a doc-comment
explaining the security boundary (loopback routing + exec opt-in are the
real guards; integrity is tamper-detection only).

Existing tests in plugins-signing.test.ts cover all three cases (correct
hash passes, wrong hash throws, absent field passes).
…ister

IMPORTANT-8: emitHookBlocking (the onRequest/onResponse hot path) was not
checking isRateLimited(), unlike emitHook. Add the same guard at the top
of the per-handler loop so abusive plugins can't bypass the 100-calls/s
cap via the blocking path.

IMPORTANT-4: unregisterHooks() now calls rateLimitMap.delete(pluginName)
so uninstalled plugins don't leak rate-limit state indefinitely. resetHooks()
already called rateLimitMap.clear(); this aligns the partial-unregister path.

Tests added to plugins-hooks-rate-limit.test.ts:
- emitHookBlocking skips a rate-limited plugin
- resetHooks clears rate-limit state so counts start fresh
…ecutions source

IMPORTANT-7: plugin_configure now calls validatePluginConfig() before
updatePluginConfig() when the plugin declares a configSchema. On failure
it returns { success: false, error: "Config validation failed: ..." }
without leaking raw field-level detail. Plugins without a configSchema
skip validation (nothing to validate against). Import of validatePluginConfig
added from manifest.ts.

IMPORTANT-9: plugin_executions description corrected from "plugin_metrics
table" to "plugin_analytics table" — the handler has always read
plugin_analytics (migration 091), never plugin_metrics. Added a doc-
comment to pluginMetrics.ts explaining that plugin_metrics (migration 090)
is a reserved aggregate table whose write path (recordPluginMetric) is not
yet wired into production code, and that the migration should not be removed.

Tests added to plugins-tools.test.ts:
- plugin_configure rejects wrong type (number for string field)
- plugin_configure rejects out-of-range number
- plugin_configure accepts valid config
- plugin_configure allows any config when no configSchema declared
…ted_at

MINOR-10: compareSemver now strips a '-prerelease' suffix (e.g. '1.0.0-beta'
from a legacy DB row) before parsing segments, and coerces non-numeric
segments to 0 instead of NaN. Prevents silent NaN comparisons that could
mark '1.0.0-beta' as equal to '1.0.0' via NaN !== NaN short-circuit.
compareSemver exported for unit-testability.

MINOR-11: updatePluginStatus in db/plugins.ts gains a one-line comment
documenting that activated_at is the most-recent activation timestamp,
intentionally preserved on deactivation via COALESCE. No logic change.

Tests added to plugins-upgrade.test.ts:
- compareSemver: valid semver compares correctly
- compareSemver: pre-release suffix strips cleanly (no NaN)
- compareSemver: NaN segments coerce to 0, result is not NaN
@diegosouzapw

Copy link
Copy Markdown
Collaborator Author

🔒 Security hardening complete — a deep code-review of the auto-completed plugin code found 11 issues (3 CRITICAL, 6 IMPORTANT, 2 MINOR); all are now fixed on this branch:

CRITICAL:

  • Integrity verification was dead code (integrity not in the manifest schema) → added the field; opt-in SHA-256 tamper-check now active (verify-when-present, fatal on mismatch).
  • rm(pluginDir) in upgrade/uninstall now asserts the path is contained under the plugin root before deletion (no arbitrary-path deletion from a tampered DB).
  • manifest.main validated as a contained path AT INSTALL time; install/upgrade are now atomic (staging dir → validate → rename, with rollback on failure).

IMPORTANT: emitHookBlocking (the onRequest hot path) now rate-limits; rateLimitMap evicts on unregister (no leak); plugin_configure validates against configSchema; plugin host tmp script written with O_EXCL+0600 (TOCTOU); plugin_executions description corrected; plugin_metrics orphan documented.

MINOR: compareSemver NaN-safe; activated_at semantics documented.

Validation: typecheck clean · check:cycles OK · 246/246 tests (incl. 43 security tests + the new hardening tests). Recommend a final human pass on the sandbox trust model before merge.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

https://github.com/diegosouzapw/OmniRoute/blob/90641886a5694948dee419121206bebcb3cecbd5/src/lib/plugins/manager.ts#L373
P1 Badge Register activated plugins with the request pipeline

When a plugin is activated through the new API/MCP flow, this registers the handlers only in src/lib/plugins/hooks.ts; however the actual chat pipeline still imports runOnRequest/runOnError from @/lib/plugins/index in open-sse/handlers/chatCore.ts, which iterates its separate _plugins registry. As a result, plugins installed and activated by this framework are marked active but their hooks never run for real chat requests.


https://github.com/diegosouzapw/OmniRoute/blob/90641886a5694948dee419121206bebcb3cecbd5/src/lib/plugins/loader.ts#L50
P1 Badge Enforce plugin permissions in the actual loader

This host script imports the plugin entry point in a normal Node child process, so a plugin that declares no permissions can still import('node:child_process'), fs, or use unrestricted network APIs as soon as it is activated. The permission-gated sandbox in pluginWorker.ts is not used by loadPlugin (repo search shows only tests reference it), so the advertised exec permission and OMNIROUTE_PLUGINS_ALLOW_EXEC opt-in are bypassed for any installed plugin.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -70,21 +71,21 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
if (!field) continue; // Allow extra keys
if (field.type === "number" && typeof value === "number") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject config values with the wrong declared type

For plugins that declare a configSchema, this validation only checks numeric min/max when the value is already a number and select enums, so PUT /api/plugins/:name/config accepts values like { "maxRetries": "five", "debug": "yes", "apiUrl": 123 } even though the manifest declares number/boolean/string fields. That stores invalid config for the plugin to consume, while the SDK/tests expect type validation for these schema types.

Useful? React with 👍 / 👎.

…s-and-model-policy

# Conflicts:
#	tests/unit/apikeypolicy-quota-only.test.ts
@diegosouzapw
diegosouzapw merged commit 20c3149 into release/v3.8.8 Jun 1, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc1b92663c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"/api/services/", // T-10: can run npm install + spawn node processes
"/api/tools/agent-bridge/", // start/stop MITM server + DNS edits (Hard Rules #15 + #17)
"/api/tools/traffic-inspector/", // http-proxy listener + system proxy (Hard Rules #15 + #17)
"/api/plugins/", // plugins: load/execute via worker_threads + child_process (Hard Rules #15 + #17)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the bare plugins path to the spawn denylist

This adds both /api/plugins/ and the bare /api/plugins route to LOCAL_ONLY_API_PREFIXES, but only the slash-suffixed prefix is listed as spawn-capable. isLocalOnlyBypassableByManageScope() rejects configured bypass prefixes by comparing them against SPAWN_CAPABLE_PREFIXES, so a runtime bypass prefix of /api/plugins is not considered equal to, a child of, or a parent of /api/plugins/; with manage-scope bypass enabled, remote manage-key callers could reach POST /api/plugins even though that endpoint installs/loads local code. Include the bare path in the spawn-capable list as well.

Useful? React with 👍 / 👎.

Comment on lines +188 to +190
const { getPluginAnalytics, getPluginAnalyticsSummary } = await import(
"../../../src/lib/db/plugins"
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Record executions before serving plugin metrics

The tool now reads plugin_analytics, but I searched production code for recordPluginExecution/recordPluginMetric and found no hook or loader path that writes rows when plugins actually run. In normal use, plugin_executions will therefore return empty per-plugin rows and zero summaries no matter how often hooks execute; wire the recording into the hook execution path before switching the MCP tool to this table.

Useful? React with 👍 / 👎.

@diegosouzapw

Copy link
Copy Markdown
Collaborator Author

Merged into release/v3.8.8 as 20c31493a.

This integration branch combines two community contributions, both credited in the CHANGELOG and as co-authors on the squash commit:

The original PRs (#2913, #3017) have been closed in favor of this integrated, security-hardened branch. Thank you both! 🚀

@diegosouzapw
diegosouzapw deleted the feat/plugins-and-model-policy branch June 1, 2026 18:50
diegosouzapw pushed a commit that referenced this pull request Jun 1, 2026
…e-banner example (#3045)

Follow-up to the plugins framework (#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Jun 1, 2026
…env-doc fixes

- bump package.json / open-sse / electron / openapi / llm.txt to 3.8.8
- restructure CHANGELOG: Unreleased -> [3.8.8], dedup broken Notion/MCP block (was 12x)
- add every PR since v3.8.7 that was missing: Quota Share Engine (#2859/#3022/#3032),
  page redesigns (#2827/#2839/#2847/#2849/#2869/#2873), and fixes #2960/#2973/#2984/
  #3021/#3029/#3031/#3035/#3036/#3037/#3039/#3043/#3028; folded #2978/#2988/#3041
- insert [3.8.8] section into all 41 i18n CHANGELOGs + sync llm.txt mirrors
- document OMNIROUTE_PLUGINS_ALLOW_EXEC in .env.example + ENVIRONMENT.md (env-doc-sync gap)
diegosouzapw added a commit to oyi77/OmniRoute that referenced this pull request Jun 6, 2026
…r-inference#3286)

Each blocking handler received the original static payload, so a later plugin
could not observe an earlier plugin's body/metadata mutations — the accumulated
mergedBody/mergedMetadata were only used for the final return value. Feed each
handler the chained { ...ctx, body: mergedBody, metadata: mergedMetadata }.

Salvaged from the now-closed plugins-v4 branch (cheaper-inference#3221, superseded by the shipped
cheaper-inference#3041/cheaper-inference#3045 framework) and adapted to the current release hooks.ts. The other
two changes from the original PR don't apply: runOnResponse is fire-and-forget
by design on release, and the 086 migration was superseded by 090.

Regression test plugins-hook-payload-chaining-3286.test.ts: RED before, GREEN
after; existing plugins-hooks suites stay green (19+5).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…e-banner example (cheaper-inference#3045)

Follow-up to the plugins framework (cheaper-inference#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…env-doc fixes

- bump package.json / open-sse / electron / openapi / llm.txt to 3.8.8
- restructure CHANGELOG: Unreleased -> [3.8.8], dedup broken Notion/MCP block (was 12x)
- add every PR since v3.8.7 that was missing: Quota Share Engine (cheaper-inference#2859/cheaper-inference#3022/cheaper-inference#3032),
  page redesigns (cheaper-inference#2827/cheaper-inference#2839/cheaper-inference#2847/cheaper-inference#2849/cheaper-inference#2869/cheaper-inference#2873), and fixes cheaper-inference#2960/cheaper-inference#2973/cheaper-inference#2984/
  cheaper-inference#3021/cheaper-inference#3029/cheaper-inference#3031/cheaper-inference#3035/cheaper-inference#3036/cheaper-inference#3037/cheaper-inference#3039/cheaper-inference#3043/cheaper-inference#3028; folded cheaper-inference#2978/cheaper-inference#2988/cheaper-inference#3041
- insert [3.8.8] section into all 41 i18n CHANGELOGs + sync llm.txt mirrors
- document OMNIROUTE_PLUGINS_ALLOW_EXEC in .env.example + ENVIRONMENT.md (env-doc-sync gap)
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…e-banner example (cheaper-inference#3045)

Follow-up to the plugins framework (cheaper-inference#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
…env-doc fixes

- bump package.json / open-sse / electron / openapi / llm.txt to 3.8.8
- restructure CHANGELOG: Unreleased -> [3.8.8], dedup broken Notion/MCP block (was 12x)
- add every PR since v3.8.7 that was missing: Quota Share Engine (cheaper-inference#2859/cheaper-inference#3022/cheaper-inference#3032),
  page redesigns (cheaper-inference#2827/cheaper-inference#2839/cheaper-inference#2847/cheaper-inference#2849/cheaper-inference#2869/cheaper-inference#2873), and fixes cheaper-inference#2960/cheaper-inference#2973/cheaper-inference#2984/
  cheaper-inference#3021/cheaper-inference#3029/cheaper-inference#3031/cheaper-inference#3035/cheaper-inference#3036/cheaper-inference#3037/cheaper-inference#3039/cheaper-inference#3043/cheaper-inference#3028; folded cheaper-inference#2978/cheaper-inference#2988/cheaper-inference#3041
- insert [3.8.8] section into all 41 i18n CHANGELOGs + sync llm.txt mirrors
- document OMNIROUTE_PLUGINS_ALLOW_EXEC in .env.example + ENVIRONMENT.md (env-doc-sync gap)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…els (cheaper-inference#3041)

Integrates two community contributions into release/v3.8.8 with security hardening and conflict resolution.

- **Plugins framework** (cheaper-inference#2913 — thanks @oyi77): hooks + registry unification, plugin SDK (`definePlugin`), worker-thread sandbox, per-plugin hook rate limiting, SHA-256 integrity verification, semver-gated upgrade, and execution analytics. Plugin routes are loopback-only (`isLocalOnlyPath`); `child_process` exec is opt-in via `OMNIROUTE_PLUGINS_ALLOW_EXEC` (default off).
- **API key option: disable non-published models** (cheaper-inference#3017 — thanks @androw): a per-key flag restricting the key to discovered public models (combos / `auto/*` / `qtSd/*` routing still allowed).

Hardening applied during integration: migration renumber (089/090/091), `/api/plugins` LOCAL_ONLY route-guard classification (closes the plugin-RCE vector), atomic install/upgrade with path containment, `O_EXCL` tmp-file creation (TOCTOU), rate-limit-map eviction, `validatePluginConfig` on configure, `buildErrorBody` on all plugin error paths. 246/246 tests; typecheck / cycles / docs-sync clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Nicolas Lorin <androw95220@gmail.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…e-banner example (cheaper-inference#3045)

Follow-up to the plugins framework (cheaper-inference#3041): wires the plugin hooks end-to-end and adds full test coverage.

- Wires `onRequest` / `onResponse` / `onError` hooks into the chat pipeline (`chatCore` now imports from the unified `hooks` registry).
- Loads active plugins on server startup (`pluginManager.loadAll()` in `server-init`) so they survive restarts.
- Ships a `welcome-banner` example plugin (`examples/plugins/`) + test fixture.
- Adds a comprehensive plugin test suite (manifest, db, config, hooks, manager lifecycle, loader IPC, permissions, scanner, welcome-banner e2e).

Integration fixes applied during review:
- `manager.install` now removes an orphaned `destDir` (DB row gone but files left on disk) before the atomic rename, guarded by path containment — it previously failed with `ENOTEMPTY` (a regression surfaced by the new lifecycle test).
- `plugins-db` / `plugins-manager-lifecycle` tests now initialize the DB via the real migration `076` (`getDbInstance`) rather than relying on ambient state, so a missing/renumbered migration fails loudly instead of being masked.

348/348 plugin tests pass; typecheck / cycles clean.

Co-authored-by: oyi77 <14921983+oyi77@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…env-doc fixes

- bump package.json / open-sse / electron / openapi / llm.txt to 3.8.8
- restructure CHANGELOG: Unreleased -> [3.8.8], dedup broken Notion/MCP block (was 12x)
- add every PR since v3.8.7 that was missing: Quota Share Engine (cheaper-inference#2859/cheaper-inference#3022/cheaper-inference#3032),
  page redesigns (cheaper-inference#2827/cheaper-inference#2839/cheaper-inference#2847/cheaper-inference#2849/cheaper-inference#2869/cheaper-inference#2873), and fixes cheaper-inference#2960/cheaper-inference#2973/cheaper-inference#2984/
  cheaper-inference#3021/cheaper-inference#3029/cheaper-inference#3031/cheaper-inference#3035/cheaper-inference#3036/cheaper-inference#3037/cheaper-inference#3039/cheaper-inference#3043/cheaper-inference#3028; folded cheaper-inference#2978/cheaper-inference#2988/cheaper-inference#3041
- insert [3.8.8] section into all 41 i18n CHANGELOGs + sync llm.txt mirrors
- document OMNIROUTE_PLUGINS_ALLOW_EXEC in .env.example + ENVIRONMENT.md (env-doc-sync gap)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.