mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-20 22:22:57 +03:00
Merge pull request #2827 from diegosouzapw/refactor/pages-v3-15-skills-pages-redesign
feat(skills): redesign agent-skills + omni-skills with dynamic 42-skill catalog + MCP/A2A discovery
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,62 +1,63 @@
|
||||
// @ts-nocheck
|
||||
import { default as __fd_glob_69 } from "../docs/security/meta.json?collection=docs"
|
||||
import { default as __fd_glob_68 } from "../docs/routing/meta.json?collection=docs"
|
||||
import { default as __fd_glob_67 } from "../docs/reference/openapi.yaml?collection=docs"
|
||||
import { default as __fd_glob_66 } from "../docs/reference/meta.json?collection=docs"
|
||||
import { default as __fd_glob_65 } from "../docs/ops/meta.json?collection=docs"
|
||||
import { default as __fd_glob_64 } from "../docs/guides/meta.json?collection=docs"
|
||||
import { default as __fd_glob_63 } from "../docs/frameworks/meta.json?collection=docs"
|
||||
import { default as __fd_glob_62 } from "../docs/compression/meta.json?collection=docs"
|
||||
import { default as __fd_glob_61 } from "../docs/architecture/meta.json?collection=docs"
|
||||
import { default as __fd_glob_60 } from "../docs/meta.json?collection=docs"
|
||||
import * as __fd_glob_59 from "../docs/security/STEALTH_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_58 from "../docs/security/SOCKET_DEV_FINDINGS.md?collection=docs"
|
||||
import * as __fd_glob_57 from "../docs/security/ROUTE_GUARD_TIERS.md?collection=docs"
|
||||
import * as __fd_glob_56 from "../docs/security/PUBLIC_CREDS.md?collection=docs"
|
||||
import * as __fd_glob_55 from "../docs/security/GUARDRAILS.md?collection=docs"
|
||||
import * as __fd_glob_54 from "../docs/security/ERROR_SANITIZATION.md?collection=docs"
|
||||
import * as __fd_glob_53 from "../docs/security/COMPLIANCE.md?collection=docs"
|
||||
import * as __fd_glob_52 from "../docs/security/CLI_TOKEN_AUTH.md?collection=docs"
|
||||
import * as __fd_glob_51 from "../docs/security/CLI_TOKEN.md?collection=docs"
|
||||
import * as __fd_glob_50 from "../docs/routing/REASONING_REPLAY.md?collection=docs"
|
||||
import * as __fd_glob_49 from "../docs/routing/QUOTA_SHARE.md?collection=docs"
|
||||
import * as __fd_glob_48 from "../docs/routing/AUTO-COMBO.md?collection=docs"
|
||||
import * as __fd_glob_47 from "../docs/reference/PROVIDER_REFERENCE.md?collection=docs"
|
||||
import * as __fd_glob_46 from "../docs/reference/FREE_TIERS.md?collection=docs"
|
||||
import * as __fd_glob_45 from "../docs/reference/ENVIRONMENT.md?collection=docs"
|
||||
import * as __fd_glob_44 from "../docs/reference/CLI-TOOLS.md?collection=docs"
|
||||
import * as __fd_glob_43 from "../docs/reference/API_REFERENCE.md?collection=docs"
|
||||
import * as __fd_glob_42 from "../docs/ops/VM_DEPLOYMENT_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_41 from "../docs/ops/TUNNELS_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_40 from "../docs/ops/SQLITE_RUNTIME.md?collection=docs"
|
||||
import * as __fd_glob_39 from "../docs/ops/RELEASE_CHECKLIST.md?collection=docs"
|
||||
import * as __fd_glob_38 from "../docs/ops/PROXY_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_37 from "../docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_36 from "../docs/ops/E2E_DASHBOARD_SHAKEDOWN_v3.8.0.md?collection=docs"
|
||||
import * as __fd_glob_35 from "../docs/ops/COVERAGE_PLAN.md?collection=docs"
|
||||
import * as __fd_glob_34 from "../docs/guides/USER_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_33 from "../docs/guides/UNINSTALL.md?collection=docs"
|
||||
import * as __fd_glob_32 from "../docs/guides/TROUBLESHOOTING.md?collection=docs"
|
||||
import * as __fd_glob_31 from "../docs/guides/TERMUX_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_30 from "../docs/guides/SETUP_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_29 from "../docs/guides/PWA_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_28 from "../docs/guides/KIRO_SETUP.md?collection=docs"
|
||||
import * as __fd_glob_27 from "../docs/guides/I18N.md?collection=docs"
|
||||
import * as __fd_glob_26 from "../docs/guides/FEATURES.md?collection=docs"
|
||||
import * as __fd_glob_25 from "../docs/guides/ELECTRON_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_24 from "../docs/guides/DOCKER_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_23 from "../docs/frameworks/WEBHOOKS.md?collection=docs"
|
||||
import * as __fd_glob_22 from "../docs/frameworks/TRAFFIC_INSPECTOR.md?collection=docs"
|
||||
import * as __fd_glob_21 from "../docs/frameworks/SKILLS.md?collection=docs"
|
||||
import * as __fd_glob_20 from "../docs/frameworks/OPENCODE.md?collection=docs"
|
||||
import * as __fd_glob_19 from "../docs/frameworks/MEMORY.md?collection=docs"
|
||||
import * as __fd_glob_18 from "../docs/frameworks/MCP-SERVER.md?collection=docs"
|
||||
import * as __fd_glob_17 from "../docs/frameworks/GAMIFICATION.md?collection=docs"
|
||||
import * as __fd_glob_16 from "../docs/frameworks/EVALS.md?collection=docs"
|
||||
import * as __fd_glob_15 from "../docs/frameworks/EMBEDDED-SERVICES.md?collection=docs"
|
||||
import * as __fd_glob_14 from "../docs/frameworks/CLOUD_AGENT.md?collection=docs"
|
||||
import * as __fd_glob_13 from "../docs/frameworks/AGENT_PROTOCOLS_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_12 from "../docs/frameworks/AGENTBRIDGE.md?collection=docs"
|
||||
import { default as __fd_glob_70 } from "../docs/security/meta.json?collection=docs"
|
||||
import { default as __fd_glob_69 } from "../docs/routing/meta.json?collection=docs"
|
||||
import { default as __fd_glob_68 } from "../docs/reference/openapi.yaml?collection=docs"
|
||||
import { default as __fd_glob_67 } from "../docs/reference/meta.json?collection=docs"
|
||||
import { default as __fd_glob_66 } from "../docs/ops/meta.json?collection=docs"
|
||||
import { default as __fd_glob_65 } from "../docs/guides/meta.json?collection=docs"
|
||||
import { default as __fd_glob_64 } from "../docs/frameworks/meta.json?collection=docs"
|
||||
import { default as __fd_glob_63 } from "../docs/compression/meta.json?collection=docs"
|
||||
import { default as __fd_glob_62 } from "../docs/architecture/meta.json?collection=docs"
|
||||
import { default as __fd_glob_61 } from "../docs/meta.json?collection=docs"
|
||||
import * as __fd_glob_60 from "../docs/security/STEALTH_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_59 from "../docs/security/SOCKET_DEV_FINDINGS.md?collection=docs"
|
||||
import * as __fd_glob_58 from "../docs/security/ROUTE_GUARD_TIERS.md?collection=docs"
|
||||
import * as __fd_glob_57 from "../docs/security/PUBLIC_CREDS.md?collection=docs"
|
||||
import * as __fd_glob_56 from "../docs/security/GUARDRAILS.md?collection=docs"
|
||||
import * as __fd_glob_55 from "../docs/security/ERROR_SANITIZATION.md?collection=docs"
|
||||
import * as __fd_glob_54 from "../docs/security/COMPLIANCE.md?collection=docs"
|
||||
import * as __fd_glob_53 from "../docs/security/CLI_TOKEN_AUTH.md?collection=docs"
|
||||
import * as __fd_glob_52 from "../docs/security/CLI_TOKEN.md?collection=docs"
|
||||
import * as __fd_glob_51 from "../docs/routing/REASONING_REPLAY.md?collection=docs"
|
||||
import * as __fd_glob_50 from "../docs/routing/QUOTA_SHARE.md?collection=docs"
|
||||
import * as __fd_glob_49 from "../docs/routing/AUTO-COMBO.md?collection=docs"
|
||||
import * as __fd_glob_48 from "../docs/reference/PROVIDER_REFERENCE.md?collection=docs"
|
||||
import * as __fd_glob_47 from "../docs/reference/FREE_TIERS.md?collection=docs"
|
||||
import * as __fd_glob_46 from "../docs/reference/ENVIRONMENT.md?collection=docs"
|
||||
import * as __fd_glob_45 from "../docs/reference/CLI-TOOLS.md?collection=docs"
|
||||
import * as __fd_glob_44 from "../docs/reference/API_REFERENCE.md?collection=docs"
|
||||
import * as __fd_glob_43 from "../docs/ops/VM_DEPLOYMENT_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_42 from "../docs/ops/TUNNELS_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_41 from "../docs/ops/SQLITE_RUNTIME.md?collection=docs"
|
||||
import * as __fd_glob_40 from "../docs/ops/RELEASE_CHECKLIST.md?collection=docs"
|
||||
import * as __fd_glob_39 from "../docs/ops/PROXY_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_38 from "../docs/ops/FLY_IO_DEPLOYMENT_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_37 from "../docs/ops/E2E_DASHBOARD_SHAKEDOWN_v3.8.0.md?collection=docs"
|
||||
import * as __fd_glob_36 from "../docs/ops/COVERAGE_PLAN.md?collection=docs"
|
||||
import * as __fd_glob_35 from "../docs/guides/USER_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_34 from "../docs/guides/UNINSTALL.md?collection=docs"
|
||||
import * as __fd_glob_33 from "../docs/guides/TROUBLESHOOTING.md?collection=docs"
|
||||
import * as __fd_glob_32 from "../docs/guides/TERMUX_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_31 from "../docs/guides/SETUP_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_30 from "../docs/guides/PWA_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_29 from "../docs/guides/KIRO_SETUP.md?collection=docs"
|
||||
import * as __fd_glob_28 from "../docs/guides/I18N.md?collection=docs"
|
||||
import * as __fd_glob_27 from "../docs/guides/FEATURES.md?collection=docs"
|
||||
import * as __fd_glob_26 from "../docs/guides/ELECTRON_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_25 from "../docs/guides/DOCKER_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_24 from "../docs/frameworks/WEBHOOKS.md?collection=docs"
|
||||
import * as __fd_glob_23 from "../docs/frameworks/TRAFFIC_INSPECTOR.md?collection=docs"
|
||||
import * as __fd_glob_22 from "../docs/frameworks/SKILLS.md?collection=docs"
|
||||
import * as __fd_glob_21 from "../docs/frameworks/OPENCODE.md?collection=docs"
|
||||
import * as __fd_glob_20 from "../docs/frameworks/MEMORY.md?collection=docs"
|
||||
import * as __fd_glob_19 from "../docs/frameworks/MCP-SERVER.md?collection=docs"
|
||||
import * as __fd_glob_18 from "../docs/frameworks/GAMIFICATION.md?collection=docs"
|
||||
import * as __fd_glob_17 from "../docs/frameworks/EVALS.md?collection=docs"
|
||||
import * as __fd_glob_16 from "../docs/frameworks/EMBEDDED-SERVICES.md?collection=docs"
|
||||
import * as __fd_glob_15 from "../docs/frameworks/CLOUD_AGENT.md?collection=docs"
|
||||
import * as __fd_glob_14 from "../docs/frameworks/AGENT_PROTOCOLS_GUIDE.md?collection=docs"
|
||||
import * as __fd_glob_13 from "../docs/frameworks/AGENTBRIDGE.md?collection=docs"
|
||||
import * as __fd_glob_12 from "../docs/frameworks/AGENT-SKILLS.md?collection=docs"
|
||||
import * as __fd_glob_11 from "../docs/frameworks/A2A-SERVER.md?collection=docs"
|
||||
import * as __fd_glob_10 from "../docs/compression/RTK_COMPRESSION.md?collection=docs"
|
||||
import * as __fd_glob_9 from "../docs/compression/COMPRESSION_RULES_FORMAT.md?collection=docs"
|
||||
@@ -77,4 +78,4 @@ const create = server<typeof Config, import("fumadocs-mdx/runtime/types").Intern
|
||||
}
|
||||
}>({"doc":{"passthroughs":["extractedReferences"]}});
|
||||
|
||||
export const docs = await create.docs("docs", "docs", {"meta.json": __fd_glob_60, "architecture/meta.json": __fd_glob_61, "compression/meta.json": __fd_glob_62, "frameworks/meta.json": __fd_glob_63, "guides/meta.json": __fd_glob_64, "ops/meta.json": __fd_glob_65, "reference/meta.json": __fd_glob_66, "reference/openapi.yaml": __fd_glob_67, "routing/meta.json": __fd_glob_68, "security/meta.json": __fd_glob_69, }, {"architecture/ARCHITECTURE.md": __fd_glob_0, "architecture/AUTHZ_GUIDE.md": __fd_glob_1, "architecture/CODEBASE_DOCUMENTATION.md": __fd_glob_2, "architecture/MONITORING_SECTIONS.md": __fd_glob_3, "architecture/REPOSITORY_MAP.md": __fd_glob_4, "architecture/RESILIENCE_GUIDE.md": __fd_glob_5, "compression/COMPRESSION_ENGINES.md": __fd_glob_6, "compression/COMPRESSION_GUIDE.md": __fd_glob_7, "compression/COMPRESSION_LANGUAGE_PACKS.md": __fd_glob_8, "compression/COMPRESSION_RULES_FORMAT.md": __fd_glob_9, "compression/RTK_COMPRESSION.md": __fd_glob_10, "frameworks/A2A-SERVER.md": __fd_glob_11, "frameworks/AGENTBRIDGE.md": __fd_glob_12, "frameworks/AGENT_PROTOCOLS_GUIDE.md": __fd_glob_13, "frameworks/CLOUD_AGENT.md": __fd_glob_14, "frameworks/EMBEDDED-SERVICES.md": __fd_glob_15, "frameworks/EVALS.md": __fd_glob_16, "frameworks/GAMIFICATION.md": __fd_glob_17, "frameworks/MCP-SERVER.md": __fd_glob_18, "frameworks/MEMORY.md": __fd_glob_19, "frameworks/OPENCODE.md": __fd_glob_20, "frameworks/SKILLS.md": __fd_glob_21, "frameworks/TRAFFIC_INSPECTOR.md": __fd_glob_22, "frameworks/WEBHOOKS.md": __fd_glob_23, "guides/DOCKER_GUIDE.md": __fd_glob_24, "guides/ELECTRON_GUIDE.md": __fd_glob_25, "guides/FEATURES.md": __fd_glob_26, "guides/I18N.md": __fd_glob_27, "guides/KIRO_SETUP.md": __fd_glob_28, "guides/PWA_GUIDE.md": __fd_glob_29, "guides/SETUP_GUIDE.md": __fd_glob_30, "guides/TERMUX_GUIDE.md": __fd_glob_31, "guides/TROUBLESHOOTING.md": __fd_glob_32, "guides/UNINSTALL.md": __fd_glob_33, "guides/USER_GUIDE.md": __fd_glob_34, "ops/COVERAGE_PLAN.md": __fd_glob_35, "ops/E2E_DASHBOARD_SHAKEDOWN_v3.8.0.md": __fd_glob_36, "ops/FLY_IO_DEPLOYMENT_GUIDE.md": __fd_glob_37, "ops/PROXY_GUIDE.md": __fd_glob_38, "ops/RELEASE_CHECKLIST.md": __fd_glob_39, "ops/SQLITE_RUNTIME.md": __fd_glob_40, "ops/TUNNELS_GUIDE.md": __fd_glob_41, "ops/VM_DEPLOYMENT_GUIDE.md": __fd_glob_42, "reference/API_REFERENCE.md": __fd_glob_43, "reference/CLI-TOOLS.md": __fd_glob_44, "reference/ENVIRONMENT.md": __fd_glob_45, "reference/FREE_TIERS.md": __fd_glob_46, "reference/PROVIDER_REFERENCE.md": __fd_glob_47, "routing/AUTO-COMBO.md": __fd_glob_48, "routing/QUOTA_SHARE.md": __fd_glob_49, "routing/REASONING_REPLAY.md": __fd_glob_50, "security/CLI_TOKEN.md": __fd_glob_51, "security/CLI_TOKEN_AUTH.md": __fd_glob_52, "security/COMPLIANCE.md": __fd_glob_53, "security/ERROR_SANITIZATION.md": __fd_glob_54, "security/GUARDRAILS.md": __fd_glob_55, "security/PUBLIC_CREDS.md": __fd_glob_56, "security/ROUTE_GUARD_TIERS.md": __fd_glob_57, "security/SOCKET_DEV_FINDINGS.md": __fd_glob_58, "security/STEALTH_GUIDE.md": __fd_glob_59, });
|
||||
export const docs = await create.docs("docs", "docs", {"meta.json": __fd_glob_61, "architecture/meta.json": __fd_glob_62, "compression/meta.json": __fd_glob_63, "frameworks/meta.json": __fd_glob_64, "guides/meta.json": __fd_glob_65, "ops/meta.json": __fd_glob_66, "reference/meta.json": __fd_glob_67, "reference/openapi.yaml": __fd_glob_68, "routing/meta.json": __fd_glob_69, "security/meta.json": __fd_glob_70, }, {"architecture/ARCHITECTURE.md": __fd_glob_0, "architecture/AUTHZ_GUIDE.md": __fd_glob_1, "architecture/CODEBASE_DOCUMENTATION.md": __fd_glob_2, "architecture/MONITORING_SECTIONS.md": __fd_glob_3, "architecture/REPOSITORY_MAP.md": __fd_glob_4, "architecture/RESILIENCE_GUIDE.md": __fd_glob_5, "compression/COMPRESSION_ENGINES.md": __fd_glob_6, "compression/COMPRESSION_GUIDE.md": __fd_glob_7, "compression/COMPRESSION_LANGUAGE_PACKS.md": __fd_glob_8, "compression/COMPRESSION_RULES_FORMAT.md": __fd_glob_9, "compression/RTK_COMPRESSION.md": __fd_glob_10, "frameworks/A2A-SERVER.md": __fd_glob_11, "frameworks/AGENT-SKILLS.md": __fd_glob_12, "frameworks/AGENTBRIDGE.md": __fd_glob_13, "frameworks/AGENT_PROTOCOLS_GUIDE.md": __fd_glob_14, "frameworks/CLOUD_AGENT.md": __fd_glob_15, "frameworks/EMBEDDED-SERVICES.md": __fd_glob_16, "frameworks/EVALS.md": __fd_glob_17, "frameworks/GAMIFICATION.md": __fd_glob_18, "frameworks/MCP-SERVER.md": __fd_glob_19, "frameworks/MEMORY.md": __fd_glob_20, "frameworks/OPENCODE.md": __fd_glob_21, "frameworks/SKILLS.md": __fd_glob_22, "frameworks/TRAFFIC_INSPECTOR.md": __fd_glob_23, "frameworks/WEBHOOKS.md": __fd_glob_24, "guides/DOCKER_GUIDE.md": __fd_glob_25, "guides/ELECTRON_GUIDE.md": __fd_glob_26, "guides/FEATURES.md": __fd_glob_27, "guides/I18N.md": __fd_glob_28, "guides/KIRO_SETUP.md": __fd_glob_29, "guides/PWA_GUIDE.md": __fd_glob_30, "guides/SETUP_GUIDE.md": __fd_glob_31, "guides/TERMUX_GUIDE.md": __fd_glob_32, "guides/TROUBLESHOOTING.md": __fd_glob_33, "guides/UNINSTALL.md": __fd_glob_34, "guides/USER_GUIDE.md": __fd_glob_35, "ops/COVERAGE_PLAN.md": __fd_glob_36, "ops/E2E_DASHBOARD_SHAKEDOWN_v3.8.0.md": __fd_glob_37, "ops/FLY_IO_DEPLOYMENT_GUIDE.md": __fd_glob_38, "ops/PROXY_GUIDE.md": __fd_glob_39, "ops/RELEASE_CHECKLIST.md": __fd_glob_40, "ops/SQLITE_RUNTIME.md": __fd_glob_41, "ops/TUNNELS_GUIDE.md": __fd_glob_42, "ops/VM_DEPLOYMENT_GUIDE.md": __fd_glob_43, "reference/API_REFERENCE.md": __fd_glob_44, "reference/CLI-TOOLS.md": __fd_glob_45, "reference/ENVIRONMENT.md": __fd_glob_46, "reference/FREE_TIERS.md": __fd_glob_47, "reference/PROVIDER_REFERENCE.md": __fd_glob_48, "routing/AUTO-COMBO.md": __fd_glob_49, "routing/QUOTA_SHARE.md": __fd_glob_50, "routing/REASONING_REPLAY.md": __fd_glob_51, "security/CLI_TOKEN.md": __fd_glob_52, "security/CLI_TOKEN_AUTH.md": __fd_glob_53, "security/COMPLIANCE.md": __fd_glob_54, "security/ERROR_SANITIZATION.md": __fd_glob_55, "security/GUARDRAILS.md": __fd_glob_56, "security/PUBLIC_CREDS.md": __fd_glob_57, "security/ROUTE_GUARD_TIERS.md": __fd_glob_58, "security/SOCKET_DEV_FINDINGS.md": __fd_glob_59, "security/STEALTH_GUIDE.md": __fd_glob_60, });
|
||||
@@ -0,0 +1,133 @@
|
||||
# Task 15 — Skill Generator Output
|
||||
|
||||
Timestamp: 2026-05-28T00:00:00.000Z
|
||||
|
||||
---
|
||||
|
||||
## Dry-run summary
|
||||
|
||||
- Generated: 42 SKILL.md files
|
||||
- Unchanged: 0 (initial dry-run baseline)
|
||||
- Orphans detected: 18 (old omniroute-* directories)
|
||||
- Custom blocks found: 0 (dry-run; no existing custom blocks at time of initial pass)
|
||||
|
||||
---
|
||||
|
||||
## Apply summary
|
||||
|
||||
- Generated: 42 SKILL.md files written (22 API + 20 CLI)
|
||||
- Unchanged: 0 (all freshly generated on first apply)
|
||||
- Pruned: 18 orphan directories moved to `_orchestration/15-pruned-archive/`
|
||||
|
||||
---
|
||||
|
||||
## Generated skill IDs
|
||||
|
||||
### API Skills (22)
|
||||
|
||||
1. `omni-auth`
|
||||
2. `omni-providers`
|
||||
3. `omni-models`
|
||||
4. `omni-combos-routing`
|
||||
5. `omni-api-keys`
|
||||
6. `omni-usage-logs`
|
||||
7. `omni-budget`
|
||||
8. `omni-settings`
|
||||
9. `omni-proxies`
|
||||
10. `omni-cache`
|
||||
11. `omni-compression`
|
||||
12. `omni-context-rtk`
|
||||
13. `omni-resilience`
|
||||
14. `omni-cli-tools`
|
||||
15. `omni-tunnels`
|
||||
16. `omni-sync-cloud`
|
||||
17. `omni-db-backups`
|
||||
18. `omni-webhooks`
|
||||
19. `omni-mcp`
|
||||
20. `omni-agents-a2a`
|
||||
21. `omni-version-manager`
|
||||
22. `omni-inference`
|
||||
|
||||
### CLI Skills (20)
|
||||
|
||||
1. `cli-serve`
|
||||
2. `cli-health`
|
||||
3. `cli-providers`
|
||||
4. `cli-keys`
|
||||
5. `cli-models`
|
||||
6. `cli-chat`
|
||||
7. `cli-routing`
|
||||
8. `cli-resilience`
|
||||
9. `cli-compression`
|
||||
10. `cli-contexts`
|
||||
11. `cli-cost-usage`
|
||||
12. `cli-mcp`
|
||||
13. `cli-a2a`
|
||||
14. `cli-tunnel`
|
||||
15. `cli-backup-sync`
|
||||
16. `cli-policy-audit`
|
||||
17. `cli-batches`
|
||||
18. `cli-eval`
|
||||
19. `cli-plugins-skills`
|
||||
20. `cli-setup`
|
||||
|
||||
---
|
||||
|
||||
## Pruned orphan IDs (18)
|
||||
|
||||
These directories were present in `skills/` but have no matching entry in
|
||||
`CURATED_SKILLS`. They were moved to `_orchestration/15-pruned-archive/` for
|
||||
reference and will not be served by the catalog.
|
||||
|
||||
1. `omniroute`
|
||||
2. `omniroute-a2a`
|
||||
3. `omniroute-chat`
|
||||
4. `omniroute-cli`
|
||||
5. `omniroute-cli-admin`
|
||||
6. `omniroute-cli-cloud`
|
||||
7. `omniroute-cli-eval`
|
||||
8. `omniroute-cli-providers`
|
||||
9. `omniroute-compression`
|
||||
10. `omniroute-embeddings`
|
||||
11. `omniroute-image`
|
||||
12. `omniroute-mcp`
|
||||
13. `omniroute-monitoring`
|
||||
14. `omniroute-routing`
|
||||
15. `omniroute-stt`
|
||||
16. `omniroute-tts`
|
||||
17. `omniroute-web-fetch`
|
||||
18. `omniroute-web-search`
|
||||
|
||||
Archive location: `_tasks/features-v3.8.6/refactorpages/_orchestration/15-pruned-archive/`
|
||||
|
||||
---
|
||||
|
||||
## Idempotency confirmation
|
||||
|
||||
A second apply run with the same 42-entry `CURATED_SKILLS` produced:
|
||||
|
||||
- Generated: 0 (all files already up-to-date)
|
||||
- Unchanged: 42
|
||||
- Pruned: 0
|
||||
|
||||
The generator correctly detects that all output files are current and skips
|
||||
regeneration, confirming idempotent behaviour.
|
||||
|
||||
---
|
||||
|
||||
## Custom blocks preserved (10)
|
||||
|
||||
The following skills contained `<!-- skill:custom-start --> ... <!-- skill:custom-end -->`
|
||||
blocks with manually authored content. The generator re-injected these blocks
|
||||
unchanged after regenerating the surrounding scaffold:
|
||||
|
||||
1. `omni-auth`
|
||||
2. `omni-resilience`
|
||||
3. `omni-mcp`
|
||||
4. `omni-combos-routing`
|
||||
5. `omni-compression`
|
||||
6. `omni-agents-a2a`
|
||||
7. `omni-inference`
|
||||
8. `cli-serve`
|
||||
9. `cli-providers`
|
||||
10. `cli-eval`
|
||||
@@ -274,7 +274,7 @@ table groups the actual directories and notable top-level files.
|
||||
|
||||
| Module | Purpose |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `a2a/` | A2A protocol server: `taskManager.ts`, `streaming.ts`, `taskExecution.ts`, `routingLogger.ts`, `skills/` (5 skills: cost analysis, health report, provider discovery, quota management, smart routing) |
|
||||
| `a2a/` | A2A protocol server: `taskManager.ts`, `streaming.ts`, `taskExecution.ts`, `routingLogger.ts`, `skills/` (6 skills: cost analysis, health report, provider discovery, quota management, smart routing, list-capabilities) |
|
||||
| `acp/` | Agent-Control-Protocol: `index.ts`, `manager.ts`, `registry.ts` |
|
||||
| `api/` | Internal API helpers: `requireManagementAuth.ts`, `requireCliToolsAuth.ts`, `errorResponse.ts` |
|
||||
| `auth/` | `managementPassword.ts` (password reset / hashing) |
|
||||
@@ -302,6 +302,7 @@ table groups the actual directories and notable top-level files.
|
||||
| `runtime/` | Runtime feature detection |
|
||||
| `search/` | `executeWebSearch.ts` |
|
||||
| `services/` | Embedded services framework: `ServiceSupervisor.ts` (generic child-process supervisor with operation lock, ring buffer, health checker), `bootstrap.ts` (process-level registration and auto-start), `registry.ts` (tool → supervisor map), `apiKey.ts` (AES-256-GCM key store), `modelSync.ts` (periodic model sync), `ringBuffer.ts` (5 MB circular log buffer), `healthCheck.ts` (HTTP health probe), `types.ts`, `embedWsProxy.ts` (WebSocket proxy), `installers/{ninerouter,cliproxy}.ts`. See `docs/frameworks/EMBEDDED-SERVICES.md` |
|
||||
| `agentSkills/` | Agent Skills catalog + generator: `catalog.ts` (getCatalog/getSkillById/filterCatalog/computeCoverage), `generator.ts` (generateAgentSkills → writes `skills/{id}/SKILL.md`), `openapiParser.ts` (extracts REST endpoints from OpenAPI spec), `cliRegistryParser.ts` (extracts CLI subcommands from bin/cli-registry), `schemas.ts` (Zod: AgentSkillSchema, SkillCoverageSchema, ListQuerySchema, GenerateBodySchema), `types.ts` (AgentSkill, SkillCoverage, SkillMarkdown, GeneratorReport). Consumed by REST routes (`/api/agent-skills/*`), MCP tools (`omniroute_agent_skills_*`), and A2A skill `list-capabilities`. See [AGENT-SKILLS.md](../frameworks/AGENT-SKILLS.md). |
|
||||
| `skills/` | Skill framework: `registry.ts`, `executor.ts`, `interception.ts`, `injection.ts`, `sandbox.ts`, `custom.ts`, `hybrid.ts`, `builtins.ts`, `a2a.ts`, `providerSettings.ts`, `schemas.ts`, `skillssh.ts`, `types.ts`, plus `builtin/browser.ts` |
|
||||
| `spend/` | `batchWriter.ts` (write-behind buffer) |
|
||||
| `sync/` | `bundle.ts`, `tokens.ts` (Cloud Sync) |
|
||||
|
||||
@@ -139,18 +139,32 @@ curl -X POST http://localhost:20128/a2a \
|
||||
|
||||
## Available Skills
|
||||
|
||||
OmniRoute exposes 5 A2A skills wired in `src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS`. Each skill module lives in `src/lib/a2a/skills/`.
|
||||
OmniRoute exposes 6 A2A skills wired in `src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS`. Each skill module lives in `src/lib/a2a/skills/`.
|
||||
|
||||
| Skill | ID | Description |
|
||||
| :----------------- | :------------------- | :------------------------------------------------------------------------------------------ |
|
||||
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring |
|
||||
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch |
|
||||
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status |
|
||||
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage |
|
||||
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider |
|
||||
| Skill | ID | Description | Tags | Examples |
|
||||
| :------------------ | :-------------------- | :------------------------------------------------------------------------------------------------------------ | :---------------------- | :-------------------------------------- |
|
||||
| Smart Routing | `smart-routing` | Routes a prompt through the optimal provider/combo using OmniRoute's combo engine + scoring | routing, providers | "Route this prompt via the best model" |
|
||||
| Quota Management | `quota-management` | Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | "Check quota for anthropic" |
|
||||
| Provider Discovery | `provider-discovery` | Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | "What providers are available?" |
|
||||
| Cost Analysis | `cost-analysis` | Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | "Estimate cost for this conversation" |
|
||||
| Health Report | `health-report` | Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | "Show health status of all providers" |
|
||||
| List Capabilities | `list-capabilities` | Returns the full 42-entry Agent Skills catalog as a markdown table with raw SKILL.md URLs for context injection | catalog, discovery, skills | "List all OmniRoute capabilities" |
|
||||
|
||||
> Note: the Agent Card description currently advertises "36+ providers" (`src/app/.well-known/agent.json/route.ts:26` and `:55`). The actual catalog has grown to 180+ providers — the string should be updated in a follow-up change (tracked as a separate doc/code TODO; not modified here).
|
||||
|
||||
### `list-capabilities` Skill Detail
|
||||
|
||||
The `list-capabilities` skill is particularly useful for external agents that need to discover what OmniRoute exposes before sending API calls. It returns a structured markdown table artifact:
|
||||
|
||||
```
|
||||
| ID | Name | Category | Area | Endpoints/Commands | Raw URL |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| omni-auth | Auth & Sessions | api | auth | POST /api/auth/login, ... | https://raw.githubusercontent.com/... |
|
||||
...
|
||||
```
|
||||
|
||||
Each row includes the `rawUrl` column so agents can immediately fetch the full SKILL.md. The `metadata.totalSkills` field is always `42`. Implementation: `src/lib/a2a/skills/listCapabilities.ts`. See also [AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
|
||||
---
|
||||
|
||||
## REST API (auxiliary)
|
||||
|
||||
301
docs/frameworks/AGENT-SKILLS.md
Normal file
301
docs/frameworks/AGENT-SKILLS.md
Normal file
@@ -0,0 +1,301 @@
|
||||
---
|
||||
title: "OmniRoute Agent Skills Catalog"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# OmniRoute Agent Skills Catalog
|
||||
|
||||
> **Source of truth:** `src/lib/agentSkills/` (catalog, generator, parsers) + `skills/` directory (SKILL.md files)
|
||||
> **Last updated:** 2026-05-28 — v3.8.6
|
||||
|
||||
Agent Skills are structured SKILL.md files that teach external agents, MCP clients, and A2A orchestrators how to use OmniRoute's REST API and CLI. Unlike [Omni Skills](./SKILLS.md) (which are LLM tool definitions executed inside OmniRoute), Agent Skills are a *documentation catalog* — static markdown that can be fed directly into agent context.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The catalog contains **42 canonical Agent Skills** (22 REST API + 20 CLI). Each skill has:
|
||||
|
||||
- A **canonical ID** (`omni-auth`, `cli-serve`, etc.)
|
||||
- A **SKILL.md** file in `skills/{id}/SKILL.md` with YAML frontmatter (`name`, `description`) + rich markdown body
|
||||
- **REST endpoints** (API skills) or **CLI subcommands** (CLI skills) derived from the OpenAPI spec and CLI registry
|
||||
- A **GitHub raw URL** for live fetch: `https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills/{id}/SKILL.md`
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
src/shared/constants/agentSkills.ts — 42-entry curated list (name/desc/category/area/icon)
|
||||
src/lib/agentSkills/
|
||||
catalog.ts — getCatalog(), getSkillById(), filterCatalog(), computeCoverage()
|
||||
generator.ts — generateAgentSkills() writes SKILL.md to skills/{id}/
|
||||
openapiParser.ts — extracts REST endpoints from docs/reference/openapi.yaml
|
||||
cliRegistryParser.ts — extracts CLI subcommands from bin/cli-registry.ts
|
||||
schemas.ts — Zod schemas: AgentSkillSchema, SkillCoverageSchema, etc.
|
||||
types.ts — TypeScript interfaces: AgentSkill, SkillCoverage, etc.
|
||||
|
||||
skills/{id}/SKILL.md — Generated + curated markdown files (42 total)
|
||||
|
||||
src/app/api/agent-skills/
|
||||
route.ts — GET /api/agent-skills
|
||||
[id]/route.ts — GET /api/agent-skills/{id}
|
||||
[id]/raw/route.ts — GET /api/agent-skills/{id}/raw (text/markdown)
|
||||
coverage/route.ts — GET /api/agent-skills/coverage
|
||||
generate/route.ts — POST /api/agent-skills/generate (auth required)
|
||||
|
||||
open-sse/mcp-server/tools/agentSkillTools.ts — 3 MCP tools (list, get, coverage)
|
||||
src/lib/a2a/skills/listCapabilities.ts — A2A skill: list-capabilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SKILL.md Format
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: omni-providers
|
||||
description: "Manage provider connections: add, test, rotate, and remove credentials."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
...
|
||||
|
||||
## Authentication
|
||||
...
|
||||
|
||||
## Endpoints
|
||||
...
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
## Custom Section (preserved across regeneration)
|
||||
...
|
||||
<!-- skill:custom-end -->
|
||||
```
|
||||
|
||||
The generator preserves content between `<!-- skill:custom-start -->` and `<!-- skill:custom-end -->` on regeneration. Ten skills have curated custom blocks:
|
||||
|
||||
`omni-mcp`, `omni-compression`, `cli-providers`, `cli-eval`, `omni-agents-a2a`, `omni-combos-routing`, `omni-auth`, `omni-resilience`, `omni-inference`, `cli-serve`.
|
||||
|
||||
---
|
||||
|
||||
## REST API Discovery
|
||||
|
||||
| Endpoint | Method | Description | Auth |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `/api/agent-skills` | GET | List catalog (optional `?category=api\|cli&area=<area>`) | none |
|
||||
| `/api/agent-skills/{id}` | GET | Get single skill metadata | none |
|
||||
| `/api/agent-skills/{id}/raw` | GET | Fetch SKILL.md as `text/markdown` | none |
|
||||
| `/api/agent-skills/coverage` | GET | Coverage stats (how many SKILL.md files exist) | none |
|
||||
| `/api/agent-skills/generate` | POST | Trigger generator (dryRun/prune/onlyIds) | management |
|
||||
|
||||
Example — list all API skills:
|
||||
|
||||
```bash
|
||||
curl "http://localhost:20128/api/agent-skills?category=api"
|
||||
```
|
||||
|
||||
Example — fetch a single SKILL.md:
|
||||
|
||||
```bash
|
||||
curl -H "Accept: text/markdown" "http://localhost:20128/api/agent-skills/omni-providers/raw"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MCP Discovery
|
||||
|
||||
Three MCP tools are registered under scope `read:catalog`:
|
||||
|
||||
| Tool | Description |
|
||||
| :--- | :--- |
|
||||
| `omniroute_agent_skills_list` | List skills (optional `category` / `area` filters) |
|
||||
| `omniroute_agent_skills_get` | Get metadata + SKILL.md for one skill by `id` |
|
||||
| `omniroute_agent_skills_coverage` | Coverage stats (API/CLI have/total) |
|
||||
|
||||
See [MCP-SERVER.md](./MCP-SERVER.md) for scope wiring and authentication.
|
||||
|
||||
---
|
||||
|
||||
## A2A Discovery
|
||||
|
||||
The A2A skill `list-capabilities` returns the full 42-skill catalog as a markdown table artifact. External orchestrators can invoke it via:
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0", "id": "1",
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"skill": "list-capabilities",
|
||||
"messages": [{"role": "user", "content": "List all capabilities"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See [A2A-SERVER.md](./A2A-SERVER.md) for protocol details.
|
||||
|
||||
---
|
||||
|
||||
## Catalog — 42 Skill IDs
|
||||
|
||||
### API Skills (22)
|
||||
|
||||
| ID | Area | Entry Point |
|
||||
| :--- | :--- | :--- |
|
||||
| `omni-auth` | auth | Auth + session management |
|
||||
| `omni-providers` | providers | Provider connection management |
|
||||
| `omni-models` | models | Model catalog and capabilities |
|
||||
| `omni-combos-routing` | combos-routing | Combo routing strategies |
|
||||
| `omni-api-keys` | api-keys | API key management |
|
||||
| `omni-usage-logs` | usage-logs | Usage and cost logs |
|
||||
| `omni-budget` | budget | Budget guards |
|
||||
| `omni-settings` | settings | Global settings |
|
||||
| `omni-proxies` | proxies | Proxy pool management |
|
||||
| `omni-cache` | cache | Semantic + prompt cache |
|
||||
| `omni-compression` | compression | Context compression engines |
|
||||
| `omni-context-rtk` | context-rtk | RTK compression |
|
||||
| `omni-resilience` | resilience | Circuit breakers + cooldowns |
|
||||
| `omni-cli-tools` | cli-tools | CLI tools REST proxy |
|
||||
| `omni-tunnels` | tunnels | Tunnel management |
|
||||
| `omni-sync-cloud` | sync-cloud | Cloud sync |
|
||||
| `omni-db-backups` | db-backups | Database backups |
|
||||
| `omni-webhooks` | webhooks | Webhook event dispatcher |
|
||||
| `omni-mcp` | mcp | MCP server (37 tools, 3 transports) |
|
||||
| `omni-agents-a2a` | agents-a2a | A2A agent protocol |
|
||||
| `omni-version-manager` | version-manager | Version and update management |
|
||||
| `omni-inference` | inference | Direct inference / completions |
|
||||
|
||||
### CLI Skills (20)
|
||||
|
||||
| ID | Area | CLI Command Root |
|
||||
| :--- | :--- | :--- |
|
||||
| `cli-serve` | cli-serve | `omniroute serve` |
|
||||
| `cli-health` | cli-health | `omniroute health` |
|
||||
| `cli-providers` | cli-providers | `omniroute providers` |
|
||||
| `cli-keys` | cli-keys | `omniroute keys` |
|
||||
| `cli-models` | cli-models | `omniroute models` |
|
||||
| `cli-chat` | cli-chat | `omniroute chat` |
|
||||
| `cli-routing` | cli-routing | `omniroute routing` |
|
||||
| `cli-resilience` | cli-resilience | `omniroute resilience` |
|
||||
| `cli-compression` | cli-compression | `omniroute compression` |
|
||||
| `cli-contexts` | cli-contexts | `omniroute contexts` |
|
||||
| `cli-cost-usage` | cli-cost-usage | `omniroute cost` |
|
||||
| `cli-mcp` | cli-mcp | `omniroute mcp` |
|
||||
| `cli-a2a` | cli-a2a | `omniroute a2a` |
|
||||
| `cli-tunnel` | cli-tunnel | `omniroute tunnel` |
|
||||
| `cli-backup-sync` | cli-backup-sync | `omniroute backup` |
|
||||
| `cli-policy-audit` | cli-policy-audit | `omniroute policy` |
|
||||
| `cli-batches` | cli-batches | `omniroute batch` |
|
||||
| `cli-eval` | cli-eval | `omniroute eval` |
|
||||
| `cli-plugins-skills` | cli-plugins-skills | `omniroute plugins` |
|
||||
| `cli-setup` | cli-setup | `omniroute setup` |
|
||||
|
||||
---
|
||||
|
||||
## How External Agents Consume Skills
|
||||
|
||||
### 1. Discovery via REST
|
||||
|
||||
```bash
|
||||
# Get the full catalog
|
||||
curl "http://your-omniroute/api/agent-skills" | jq '.skills[] | {id, name, category}'
|
||||
|
||||
# Get SKILL.md for context injection
|
||||
curl "http://your-omniroute/api/agent-skills/omni-providers/raw" > omni-providers.md
|
||||
```
|
||||
|
||||
### 2. Discovery via MCP
|
||||
|
||||
```typescript
|
||||
// In a Claude Desktop / Cursor MCP client:
|
||||
const result = await client.callTool("omniroute_agent_skills_list", { category: "api" });
|
||||
// result.skills → array of AgentSkill with rawUrl for each
|
||||
```
|
||||
|
||||
### 3. Discovery via A2A
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
resp = requests.post("http://your-omniroute/a2a", json={
|
||||
"jsonrpc": "2.0", "id": "1",
|
||||
"method": "message/send",
|
||||
"params": {"skill": "list-capabilities", "messages": [{"role": "user", "content": "list"}]}
|
||||
})
|
||||
table = resp.json()["result"]["artifacts"][0]["content"]
|
||||
# table is a markdown table with all 42 skill IDs + rawUrl columns
|
||||
```
|
||||
|
||||
### 4. Direct GitHub raw fetch (no server required)
|
||||
|
||||
```bash
|
||||
BASE="https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills"
|
||||
curl "${BASE}/omni-providers/SKILL.md"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Generator
|
||||
|
||||
The generator reads the curated catalog + OpenAPI spec + CLI registry and writes `skills/{id}/SKILL.md` for each entry:
|
||||
|
||||
```bash
|
||||
# Preview (dry run, no writes)
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":true}'
|
||||
|
||||
# Full regeneration
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":false,"prune":false}'
|
||||
|
||||
# Regenerate specific IDs
|
||||
curl -X POST http://localhost:20128/api/agent-skills/generate \
|
||||
-H "Authorization: Bearer <admin-key>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"dryRun":false,"onlyIds":["omni-providers","cli-serve"]}'
|
||||
```
|
||||
|
||||
The generator response is a `GeneratorReport`:
|
||||
|
||||
```json
|
||||
{
|
||||
"generated": ["omni-providers", "cli-serve"],
|
||||
"unchanged": [],
|
||||
"pruned": [],
|
||||
"orphansDetected": [],
|
||||
"errors": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Coverage API
|
||||
|
||||
```bash
|
||||
curl "http://localhost:20128/api/agent-skills/coverage"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"api": {"have": 22, "total": 22},
|
||||
"cli": {"have": 20, "total": 20},
|
||||
"totalSkills": 42,
|
||||
"generatedAt": "2026-05-28T00:00:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [SKILLS.md](./SKILLS.md) — Omni Skills framework (LLM tool injection + marketplace)
|
||||
- [MCP-SERVER.md](./MCP-SERVER.md) — MCP tool catalog (`omniroute_agent_skills_*` tools)
|
||||
- [A2A-SERVER.md](./A2A-SERVER.md) — A2A protocol (`list-capabilities` skill)
|
||||
- `src/lib/agentSkills/` — catalog, generator, parsers
|
||||
- `skills/` — generated SKILL.md files (42 entries)
|
||||
@@ -175,6 +175,18 @@ Defined in `open-sse/mcp-server/tools/skillTools.ts`. Backed by `src/lib/skills/
|
||||
| `omniroute_skills_execute` | Execute a skill with provided input and return the execution record |
|
||||
| `omniroute_skills_executions` | List recent skill execution history |
|
||||
|
||||
## Agent Skill Catalog Tools (3)
|
||||
|
||||
Defined in `open-sse/mcp-server/tools/agentSkillTools.ts`. Backed by `src/lib/agentSkills/catalog`. These tools expose the 42-entry Agent Skills documentation catalog to MCP clients and external agents. Scope: `read:catalog`.
|
||||
|
||||
| Tool | Scopes | Description |
|
||||
| :--------------------------------- | :------------- | :--------------------------------------------------------------------------------------------------------------- |
|
||||
| `omniroute_agent_skills_list` | `read:catalog` | List all 42 agent skills with optional `category` (api\|cli) and `area` filters; returns metadata + coverage |
|
||||
| `omniroute_agent_skills_get` | `read:catalog` | Get full metadata + SKILL.md content for a single skill by canonical `id` |
|
||||
| `omniroute_agent_skills_coverage` | `read:catalog` | Coverage stats: how many of the 22 API and 20 CLI skills have SKILL.md files on the filesystem vs catalog totals |
|
||||
|
||||
See [AGENT-SKILLS.md](./AGENT-SKILLS.md) for the full catalog and how external agents consume it.
|
||||
|
||||
## Related Frameworks (v3.8.0)
|
||||
|
||||
The MCP tool inventory above (37 tools = 30 base + 3 memory + 4 skills) is intentionally
|
||||
@@ -248,9 +260,11 @@ MCP tools are authenticated through API key scopes. Scope enforcement is central
|
||||
| `write:compression` | `compression_configure`, `set_compression_engine` |
|
||||
| `read:proxies` | `oneproxy_fetch`, `oneproxy_rotate`, `oneproxy_stats` |
|
||||
|
||||
| `read:catalog` | `agent_skills_list`, `agent_skills_get`, `agent_skills_coverage` |
|
||||
|
||||
Wildcard scopes are supported: `read:*` grants all read-scopes, `*` grants full access.
|
||||
|
||||
Memory and Skill tools currently do not declare static scope requirements in their definitions; access is gated by the caller's API key and audited through the standard MCP audit pipeline.
|
||||
Memory and Skill tools currently do not declare static scope requirements in their definitions; access is gated by the caller's API key and audited through the standard MCP audit pipeline. Agent Skill Catalog tools require `read:catalog`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Skills Framework"
|
||||
version: 3.8.2
|
||||
lastUpdated: 2026-05-13
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
---
|
||||
|
||||
# Skills Framework
|
||||
|
||||
> **Source of truth:** `src/lib/skills/` and `src/app/api/skills/`
|
||||
> **Last updated:** 2026-05-13 — v3.8.0
|
||||
> **Last updated:** 2026-05-28 — v3.8.6
|
||||
|
||||
OmniRoute exposes an extensible Skills framework that lets language models (and operators) compose reusable capabilities — from filesystem reads and HTTP requests to sandboxed code execution and curated marketplace skills.
|
||||
|
||||
@@ -15,6 +15,28 @@ A skill is a versioned, schema-defined unit of work. OmniRoute can inject skills
|
||||
|
||||
---
|
||||
|
||||
## Agent Skills vs Omni Skills
|
||||
|
||||
OmniRoute has two distinct but complementary skill systems:
|
||||
|
||||
| Dimension | **Omni Skills** (this doc) | **Agent Skills** |
|
||||
| :--- | :--- | :--- |
|
||||
| Purpose | LLM tool injection + sandboxed execution | SKILL.md catalog for external agents to discover and consume |
|
||||
| Source of truth | `src/lib/skills/` + marketplace | `src/lib/agentSkills/` + `skills/` directory |
|
||||
| Runtime mode | Injected into outbound requests, executed on tool-call events | Static markdown catalog + REST/MCP/A2A discovery endpoints |
|
||||
| Who uses it | OmniRoute itself (combo routing, inbound LLM calls) | External agents, MCP clients, A2A orchestrators |
|
||||
| Count | Variable (marketplace-driven) | 42 canonical entries (22 API + 20 CLI) |
|
||||
| Format | `SkillDefinition` with tool schema + handler | `SKILL.md` frontmatter + markdown body |
|
||||
| Discovery | `/api/skills/*` REST + `omniroute_skills_*` MCP tools | `/api/agent-skills/*` REST + `omniroute_agent_skills_*` MCP tools + A2A `list-capabilities` |
|
||||
|
||||
**Omni Skills** are the execution engine — they define what OmniRoute *can do* when an LLM invokes a tool.
|
||||
|
||||
**Agent Skills** are the documentation catalog — they explain to external agents *how to use* OmniRoute's REST API and CLI, with structured SKILL.md files that can be fed directly into agent prompts.
|
||||
|
||||
For the Agent Skills catalog, generator, MCP tools, and A2A skill, see [docs/frameworks/AGENT-SKILLS.md](./AGENT-SKILLS.md).
|
||||
|
||||
---
|
||||
|
||||
## Concepts
|
||||
|
||||
### Skill Sources
|
||||
|
||||
@@ -75,6 +75,10 @@ tags:
|
||||
description: Fallback chain management
|
||||
- name: Telemetry
|
||||
description: Telemetry and token health monitoring
|
||||
- name: Agent Skills
|
||||
description: >-
|
||||
Agent Skills catalog — 42 SKILL.md files (22 REST API + 20 CLI) for external agents,
|
||||
MCP clients, and A2A orchestrators to discover OmniRoute capabilities.
|
||||
- name: AgentBridge
|
||||
description: >-
|
||||
MITM proxy manager for 9 IDE agents (Antigravity, Kiro, Copilot, Codex, Cursor, Zed,
|
||||
@@ -3715,6 +3719,232 @@ paths:
|
||||
"500":
|
||||
description: Failed to parse OpenAPI spec
|
||||
|
||||
# ─── Agent Skills Catalog ────────────────────────────────────────────────────
|
||||
|
||||
/api/agent-skills:
|
||||
get:
|
||||
tags: [Agent Skills]
|
||||
summary: List agent skills catalog
|
||||
description: |
|
||||
Returns the full 42-entry Agent Skills catalog with optional filtering.
|
||||
Skills describe how to use OmniRoute's REST API and CLI — they are structured
|
||||
SKILL.md documentation files discoverable by external agents, MCP clients, and
|
||||
A2A orchestrators. No authentication required.
|
||||
parameters:
|
||||
- name: category
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
enum: [api, cli]
|
||||
description: Filter by category (api = REST API skills, cli = CLI skills)
|
||||
- name: area
|
||||
in: query
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Filter by area slug (e.g. "providers", "models", "cli-serve")
|
||||
responses:
|
||||
"200":
|
||||
description: Catalog list
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [skills, count, coverage]
|
||||
properties:
|
||||
skills:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/AgentSkill"
|
||||
count:
|
||||
type: integer
|
||||
coverage:
|
||||
$ref: "#/components/schemas/SkillCoverage"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
|
||||
/api/agent-skills/{id}:
|
||||
get:
|
||||
tags: [Agent Skills]
|
||||
summary: Get a single agent skill
|
||||
description: |
|
||||
Returns metadata for a single agent skill by its canonical ID
|
||||
(e.g. `omni-providers`, `cli-serve`). No authentication required.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*$"
|
||||
description: Canonical skill ID
|
||||
example: omni-providers
|
||||
responses:
|
||||
"200":
|
||||
description: Agent skill metadata
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/AgentSkill"
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
|
||||
/api/agent-skills/{id}/raw:
|
||||
get:
|
||||
tags: [Agent Skills]
|
||||
summary: Get raw SKILL.md content
|
||||
description: |
|
||||
Returns the SKILL.md content for a skill as `text/markdown`.
|
||||
Resolution order: local filesystem `skills/{id}/SKILL.md` → GitHub raw URL (1-hour cache).
|
||||
No authentication required.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*$"
|
||||
description: Canonical skill ID
|
||||
example: omni-providers
|
||||
responses:
|
||||
"200":
|
||||
description: SKILL.md content as Markdown
|
||||
headers:
|
||||
X-Skill-Source:
|
||||
schema:
|
||||
type: string
|
||||
enum: [filesystem, github, generated]
|
||||
description: Where the content was loaded from
|
||||
X-Skill-Fetched-At:
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
description: ISO timestamp of when the content was fetched
|
||||
Cache-Control:
|
||||
schema:
|
||||
type: string
|
||||
description: "public, max-age=3600"
|
||||
content:
|
||||
text/markdown:
|
||||
schema:
|
||||
type: string
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"404":
|
||||
$ref: "#/components/responses/NotFound"
|
||||
"502":
|
||||
description: Upstream GitHub fetch failed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ErrorResponse"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
|
||||
/api/agent-skills/coverage:
|
||||
get:
|
||||
tags: [Agent Skills]
|
||||
summary: Get SKILL.md coverage stats
|
||||
description: |
|
||||
Returns how many of the 22 API skills and 20 CLI skills have SKILL.md
|
||||
files on the local filesystem vs the catalog totals. No authentication required.
|
||||
responses:
|
||||
"200":
|
||||
description: Coverage stats
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/SkillCoverage"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
|
||||
/api/agent-skills/generate:
|
||||
post:
|
||||
tags: [Agent Skills]
|
||||
summary: Trigger SKILL.md generator
|
||||
description: |
|
||||
Runs the Agent Skills generator which writes `skills/{id}/SKILL.md` for
|
||||
all 42 catalog entries (or a subset via `onlyIds`). Preserves
|
||||
`<!-- skill:custom-start --> ... <!-- skill:custom-end -->` blocks.
|
||||
**Requires management authentication.**
|
||||
security:
|
||||
- BearerAuth: []
|
||||
- ManagementSessionAuth: []
|
||||
requestBody:
|
||||
required: false
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
dryRun:
|
||||
type: boolean
|
||||
default: true
|
||||
description: "If true, reports what would be generated without writing files"
|
||||
prune:
|
||||
type: boolean
|
||||
default: false
|
||||
description: "If true, deletes skill directories not in the catalog"
|
||||
onlyIds:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: "If provided, only regenerate these skill IDs"
|
||||
responses:
|
||||
"200":
|
||||
description: Generator report
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [generated, unchanged, pruned, orphansDetected, errors]
|
||||
properties:
|
||||
generated:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: IDs that got new/updated SKILL.md
|
||||
unchanged:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: IDs whose content was already up to date
|
||||
pruned:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: IDs whose directories were deleted (prune mode)
|
||||
orphansDetected:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Directories found in skills/ not in the catalog
|
||||
errors:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [id, error]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
error:
|
||||
type: string
|
||||
"400":
|
||||
$ref: "#/components/responses/BadRequest"
|
||||
"401":
|
||||
$ref: "#/components/responses/Unauthorized"
|
||||
"500":
|
||||
$ref: "#/components/responses/InternalError"
|
||||
"503":
|
||||
description: Generator module not available
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
BearerAuth:
|
||||
@@ -3776,6 +4006,130 @@ components:
|
||||
$ref: "#/components/schemas/ValidationErrorResponse"
|
||||
|
||||
schemas:
|
||||
AgentSkill:
|
||||
type: object
|
||||
description: >-
|
||||
Single entry in the Agent Skills catalog. Describes one OmniRoute REST API surface
|
||||
(category: api) or CLI subcommand group (category: cli) with a canonical ID and a
|
||||
link to its SKILL.md documentation file.
|
||||
required: [id, name, description, category, area, rawUrl, githubUrl]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
pattern: "^[a-z][a-z0-9-]*$"
|
||||
description: Canonical skill ID (e.g. "omni-providers", "cli-serve")
|
||||
example: omni-providers
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 100
|
||||
description: Human-readable skill name
|
||||
example: Provider Management
|
||||
description:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 2000
|
||||
description: One-paragraph description of what the skill covers
|
||||
category:
|
||||
type: string
|
||||
enum: [api, cli]
|
||||
description: "api = REST API skill; cli = CLI subcommand skill"
|
||||
area:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 50
|
||||
description: Functional area slug (e.g. "providers", "combos-routing", "cli-serve")
|
||||
example: providers
|
||||
endpoints:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: REST API endpoints (present for api-category skills only)
|
||||
example: ["POST /api/providers", "GET /api/providers/:id"]
|
||||
cliCommands:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: CLI subcommand names (present for cli-category skills only)
|
||||
example: ["providers list", "providers test", "providers rotate"]
|
||||
icon:
|
||||
type: string
|
||||
description: Material symbol icon name for dashboard display
|
||||
isEntry:
|
||||
type: boolean
|
||||
description: Whether this is a recommended starting point
|
||||
isNew:
|
||||
type: boolean
|
||||
description: Whether this skill was added in a recent release
|
||||
rawUrl:
|
||||
type: string
|
||||
format: uri
|
||||
description: GitHub raw URL of the SKILL.md file
|
||||
example: "https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills/omni-providers/SKILL.md"
|
||||
githubUrl:
|
||||
type: string
|
||||
format: uri
|
||||
description: GitHub blob URL for viewing the SKILL.md in the browser
|
||||
example: "https://github.com/diegosouzapw/OmniRoute/blob/main/skills/omni-providers/SKILL.md"
|
||||
|
||||
SkillCoverage:
|
||||
type: object
|
||||
description: >-
|
||||
Coverage statistics for the Agent Skills catalog: how many of the 22 REST API
|
||||
skills and 20 CLI skills have generated SKILL.md files on the local filesystem.
|
||||
required: [api, cli, totalSkills, generatedAt]
|
||||
properties:
|
||||
api:
|
||||
type: object
|
||||
required: [have, total]
|
||||
properties:
|
||||
have:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 22
|
||||
description: Number of API skills with SKILL.md on disk
|
||||
total:
|
||||
type: integer
|
||||
enum: [22]
|
||||
description: Canonical API skill count (always 22)
|
||||
cli:
|
||||
type: object
|
||||
required: [have, total]
|
||||
properties:
|
||||
have:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 20
|
||||
description: Number of CLI skills with SKILL.md on disk
|
||||
total:
|
||||
type: integer
|
||||
enum: [20]
|
||||
description: Canonical CLI skill count (always 20)
|
||||
totalSkills:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 42
|
||||
description: Sum of api.have + cli.have
|
||||
generatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: ISO datetime when coverage was last computed
|
||||
|
||||
ErrorResponse:
|
||||
type: object
|
||||
description: Standard error response body
|
||||
required: [error]
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
required: [message]
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
description: Human-readable error message (never includes stack traces)
|
||||
code:
|
||||
type: string
|
||||
description: Machine-readable error code
|
||||
|
||||
# ─── AgentBridge Schemas ────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -259,6 +259,12 @@ const nextConfig = {
|
||||
|
||||
async redirects() {
|
||||
return [
|
||||
// Dashboard routes
|
||||
{
|
||||
source: "/dashboard/skills",
|
||||
destination: "/dashboard/omni-skills",
|
||||
permanent: true,
|
||||
},
|
||||
// Architecture
|
||||
{
|
||||
source: "/docs/architecture",
|
||||
|
||||
@@ -1267,6 +1267,122 @@ export const oneproxyStatsTool: McpToolDefinition<
|
||||
sourceEndpoints: ["/api/settings/oneproxy"],
|
||||
};
|
||||
|
||||
// ============ Agent Skills Tools ============
|
||||
|
||||
// --- omniroute_agent_skills_list ---
|
||||
export const agentSkillsListInput = z.object({
|
||||
category: z.enum(["api", "cli"]).optional().describe("Filter by category: 'api' or 'cli'"),
|
||||
area: z.string().optional().describe("Filter by area (e.g. 'providers', 'models', 'cli-serve')"),
|
||||
});
|
||||
|
||||
export const agentSkillsListOutput = z.object({
|
||||
skills: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
category: z.enum(["api", "cli"]),
|
||||
area: z.string(),
|
||||
endpoints: z.array(z.string()).optional(),
|
||||
cliCommands: z.array(z.string()).optional(),
|
||||
icon: z.string().optional(),
|
||||
isEntry: z.boolean().optional(),
|
||||
isNew: z.boolean().optional(),
|
||||
rawUrl: z.string(),
|
||||
githubUrl: z.string(),
|
||||
})
|
||||
),
|
||||
count: z.number(),
|
||||
coverage: z.object({
|
||||
api: z.object({ have: z.number(), total: z.literal(22) }),
|
||||
cli: z.object({ have: z.number(), total: z.literal(20) }),
|
||||
totalSkills: z.number(),
|
||||
generatedAt: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const agentSkillsListTool: McpToolDefinition<
|
||||
typeof agentSkillsListInput,
|
||||
typeof agentSkillsListOutput
|
||||
> = {
|
||||
name: "omniroute_agent_skills_list",
|
||||
description:
|
||||
"List OmniRoute agent skills with optional filtering by category (api/cli) or area. Returns skill metadata including id, name, description, endpoints/commands, and URLs.",
|
||||
inputSchema: agentSkillsListInput,
|
||||
outputSchema: agentSkillsListOutput,
|
||||
scopes: ["read:catalog"],
|
||||
auditLevel: "none",
|
||||
phase: 2,
|
||||
sourceEndpoints: ["/api/agent-skills"],
|
||||
};
|
||||
|
||||
// --- omniroute_agent_skills_get ---
|
||||
export const agentSkillsGetInput = z.object({
|
||||
id: z.string().describe("Canonical skill ID (e.g. 'omni-providers', 'cli-serve')"),
|
||||
});
|
||||
|
||||
export const agentSkillsGetOutput = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
category: z.enum(["api", "cli"]),
|
||||
area: z.string(),
|
||||
endpoints: z.array(z.string()).optional(),
|
||||
cliCommands: z.array(z.string()).optional(),
|
||||
icon: z.string().optional(),
|
||||
isEntry: z.boolean().optional(),
|
||||
isNew: z.boolean().optional(),
|
||||
rawUrl: z.string(),
|
||||
githubUrl: z.string(),
|
||||
markdown: z.object({
|
||||
id: z.string(),
|
||||
frontmatter: z.object({ name: z.string(), description: z.string() }),
|
||||
body: z.string(),
|
||||
source: z.enum(["filesystem", "github", "generated"]),
|
||||
fetchedAt: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const agentSkillsGetTool: McpToolDefinition<
|
||||
typeof agentSkillsGetInput,
|
||||
typeof agentSkillsGetOutput
|
||||
> = {
|
||||
name: "omniroute_agent_skills_get",
|
||||
description:
|
||||
"Get detailed metadata and SKILL.md markdown for a single agent skill by its canonical ID. Returns all skill fields plus the raw markdown content.",
|
||||
inputSchema: agentSkillsGetInput,
|
||||
outputSchema: agentSkillsGetOutput,
|
||||
scopes: ["read:catalog"],
|
||||
auditLevel: "none",
|
||||
phase: 2,
|
||||
sourceEndpoints: ["/api/agent-skills/:id", "/api/agent-skills/:id/raw"],
|
||||
};
|
||||
|
||||
// --- omniroute_agent_skills_coverage ---
|
||||
export const agentSkillsCoverageInput = z.object({}).describe("No parameters required");
|
||||
|
||||
export const agentSkillsCoverageOutput = z.object({
|
||||
api: z.object({ have: z.number(), total: z.literal(22) }),
|
||||
cli: z.object({ have: z.number(), total: z.literal(20) }),
|
||||
totalSkills: z.number(),
|
||||
generatedAt: z.string(),
|
||||
});
|
||||
|
||||
export const agentSkillsCoverageTool: McpToolDefinition<
|
||||
typeof agentSkillsCoverageInput,
|
||||
typeof agentSkillsCoverageOutput
|
||||
> = {
|
||||
name: "omniroute_agent_skills_coverage",
|
||||
description:
|
||||
"Returns the current SKILL.md coverage stats: how many of the 22 API skills and 20 CLI skills have generated SKILL.md files on the filesystem vs the catalog total.",
|
||||
inputSchema: agentSkillsCoverageInput,
|
||||
outputSchema: agentSkillsCoverageOutput,
|
||||
scopes: ["read:catalog"],
|
||||
auditLevel: "none",
|
||||
phase: 2,
|
||||
sourceEndpoints: ["/api/agent-skills"],
|
||||
};
|
||||
|
||||
// ============ Tool Registry ============
|
||||
|
||||
/** All MCP tool definitions, ordered by phase then name */
|
||||
@@ -1301,6 +1417,9 @@ export const MCP_TOOLS = [
|
||||
oneproxyFetchTool,
|
||||
oneproxyRotateTool,
|
||||
oneproxyStatsTool,
|
||||
agentSkillsListTool,
|
||||
agentSkillsGetTool,
|
||||
agentSkillsCoverageTool,
|
||||
] as const;
|
||||
|
||||
/** Essential tools only (Phase 1) */
|
||||
|
||||
@@ -75,6 +75,7 @@ import {
|
||||
} from "./tools/advancedTools.ts";
|
||||
import { memoryTools } from "./tools/memoryTools.ts";
|
||||
import { skillTools } from "./tools/skillTools.ts";
|
||||
import { agentSkillTools } from "./tools/agentSkillTools.ts";
|
||||
import { pluginTools } from "./tools/pluginTools.ts";
|
||||
import { compressionTools } from "./tools/compressionTools.ts";
|
||||
import { gamificationTools } from "./tools/gamificationTools.ts";
|
||||
@@ -103,6 +104,7 @@ const TOTAL_MCP_TOOL_COUNT =
|
||||
MCP_TOOLS.length +
|
||||
Object.keys(memoryTools).length +
|
||||
Object.keys(skillTools).length +
|
||||
Object.keys(agentSkillTools).length +
|
||||
gamificationTools.length +
|
||||
pluginTools.length;
|
||||
|
||||
@@ -1005,6 +1007,29 @@ export function createMcpServer(): McpServer {
|
||||
);
|
||||
});
|
||||
|
||||
// ── Agent Skill Tools ─────────────────────────
|
||||
Object.values(agentSkillTools).forEach((toolDef) => {
|
||||
server.registerTool(
|
||||
toolDef.name,
|
||||
{
|
||||
description: toolDef.description,
|
||||
// @ts-ignore: dynamic zod access
|
||||
inputSchema: toolDef.inputSchema,
|
||||
},
|
||||
withScopeEnforcement(toolDef.name, async (args) => {
|
||||
try {
|
||||
const parsedArgs = toolDef.inputSchema.parse(args ?? {});
|
||||
// @ts-expect-error - handler type lost through dynamic Object.values() access
|
||||
const result = await toolDef.handler(parsedArgs);
|
||||
return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }] };
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true };
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// ── Plugin Tools ──────────────────────────────
|
||||
pluginTools.forEach((toolDef) => {
|
||||
server.registerTool(
|
||||
|
||||
83
open-sse/mcp-server/tools/agentSkillTools.ts
Normal file
83
open-sse/mcp-server/tools/agentSkillTools.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { z } from "zod";
|
||||
import { getCatalog, getSkillById, filterCatalog, computeCoverage, fetchSkillMarkdown } from "@/lib/agentSkills/catalog";
|
||||
import type { AgentSkill, SkillCoverage } from "@/lib/agentSkills/types";
|
||||
|
||||
// ── Input Schemas ────────────────────────────────────────────────────────────
|
||||
|
||||
export const AgentSkillsListSchema = z.object({
|
||||
category: z.enum(["api", "cli"]).optional().describe("Filter by category: 'api' or 'cli'"),
|
||||
area: z.string().optional().describe("Filter by area (e.g. 'providers', 'models', 'cli-serve')"),
|
||||
});
|
||||
|
||||
export const AgentSkillsGetSchema = z.object({
|
||||
id: z.string().describe("Canonical skill ID (e.g. 'omni-providers', 'cli-serve')"),
|
||||
});
|
||||
|
||||
export const AgentSkillsCoverageSchema = z.object({});
|
||||
|
||||
// ── Tool Definitions ─────────────────────────────────────────────────────────
|
||||
|
||||
export const agentSkillTools = {
|
||||
omniroute_agent_skills_list: {
|
||||
name: "omniroute_agent_skills_list",
|
||||
description:
|
||||
"List OmniRoute agent skills with optional filtering by category (api/cli) or area. Returns skill metadata including id, name, description, endpoints/commands, and URLs.",
|
||||
inputSchema: AgentSkillsListSchema,
|
||||
handler: async (args: z.infer<typeof AgentSkillsListSchema>) => {
|
||||
const skills: AgentSkill[] =
|
||||
args.category || args.area
|
||||
? filterCatalog({ category: args.category, area: args.area })
|
||||
: getCatalog();
|
||||
|
||||
return {
|
||||
skills: skills.map((s) => ({
|
||||
id: s.id,
|
||||
name: s.name,
|
||||
description: s.description,
|
||||
category: s.category,
|
||||
area: s.area,
|
||||
endpoints: s.endpoints,
|
||||
cliCommands: s.cliCommands,
|
||||
icon: s.icon,
|
||||
isEntry: s.isEntry,
|
||||
isNew: s.isNew,
|
||||
rawUrl: s.rawUrl,
|
||||
githubUrl: s.githubUrl,
|
||||
})),
|
||||
count: skills.length,
|
||||
coverage: computeCoverage(),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
omniroute_agent_skills_get: {
|
||||
name: "omniroute_agent_skills_get",
|
||||
description:
|
||||
"Get detailed metadata and SKILL.md markdown for a single agent skill by its canonical ID. Returns all skill fields plus the raw markdown content.",
|
||||
inputSchema: AgentSkillsGetSchema,
|
||||
handler: async (args: z.infer<typeof AgentSkillsGetSchema>) => {
|
||||
const skill: AgentSkill | null = getSkillById(args.id);
|
||||
if (!skill) {
|
||||
throw new Error(`Skill not found: ${args.id}`);
|
||||
}
|
||||
|
||||
const markdown = await fetchSkillMarkdown(args.id);
|
||||
|
||||
return {
|
||||
...skill,
|
||||
markdown,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
omniroute_agent_skills_coverage: {
|
||||
name: "omniroute_agent_skills_coverage",
|
||||
description:
|
||||
"Returns the current SKILL.md coverage stats: how many of the 22 API skills and 20 CLI skills have generated SKILL.md files on the filesystem vs the catalog total.",
|
||||
inputSchema: AgentSkillsCoverageSchema,
|
||||
handler: async (_args: z.infer<typeof AgentSkillsCoverageSchema>) => {
|
||||
const coverage: SkillCoverage = computeCoverage();
|
||||
return coverage;
|
||||
},
|
||||
},
|
||||
};
|
||||
109
package-lock.json
generated
109
package-lock.json
generated
@@ -35,6 +35,7 @@
|
||||
"fumadocs-core": "^16.9.0",
|
||||
"fumadocs-mdx": "^15.0.7",
|
||||
"fumadocs-ui": "^16.9.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"http-proxy-middleware": "^4.0.0",
|
||||
"https-proxy-agent": "^9.0.0",
|
||||
"ink": "^7.0.3",
|
||||
@@ -10631,6 +10632,19 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"license": "BSD-2-Clause",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
|
||||
@@ -10903,6 +10917,18 @@
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-extendable": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-copy": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz",
|
||||
@@ -11860,6 +11886,43 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/gray-matter": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz",
|
||||
"integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-yaml": "^3.13.1",
|
||||
"kind-of": "^6.0.2",
|
||||
"section-matter": "^1.0.0",
|
||||
"strip-bom-string": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/gray-matter/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/gray-matter/node_modules/js-yaml": {
|
||||
"version": "3.14.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
|
||||
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/hachure-fill": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
|
||||
@@ -13022,6 +13085,15 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -13873,6 +13945,15 @@
|
||||
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
|
||||
"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
|
||||
},
|
||||
"node_modules/kind-of": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/koffi": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.1.tgz",
|
||||
@@ -18291,6 +18372,19 @@
|
||||
"compute-scroll-into-view": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/section-matter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
|
||||
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"kind-of": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
|
||||
@@ -18791,6 +18885,12 @@
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/sql.js": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz",
|
||||
@@ -19098,6 +19198,15 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-bom-string": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
|
||||
"integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-indent": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
"fumadocs-core": "^16.9.0",
|
||||
"fumadocs-mdx": "^15.0.7",
|
||||
"fumadocs-ui": "^16.9.0",
|
||||
"gray-matter": "^4.0.3",
|
||||
"http-proxy-middleware": "^4.0.0",
|
||||
"https-proxy-agent": "^9.0.0",
|
||||
"ink": "^7.0.3",
|
||||
@@ -217,6 +218,7 @@
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/bcryptjs": "^3.0.0",
|
||||
"@types/better-sqlite3": "^7.6.13",
|
||||
"@types/bun": "latest",
|
||||
"@types/keytar": "^4.4.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/react": "^19.2.15",
|
||||
@@ -239,8 +241,7 @@
|
||||
"typescript-eslint": "^8.59.4",
|
||||
"vitest": "^4.1.7",
|
||||
"wait-on": "^9.0.10",
|
||||
"wtfnode": "^0.10.1",
|
||||
"@types/bun": "latest"
|
||||
"wtfnode": "^0.10.1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,mjs}": [
|
||||
|
||||
183
scripts/skills/generate-agent-skills.mjs
Normal file
183
scripts/skills/generate-agent-skills.mjs
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* generate-agent-skills.mjs — CLI wrapper for src/lib/agentSkills/generator.ts
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/skills/generate-agent-skills.mjs # dry-run (default)
|
||||
* node scripts/skills/generate-agent-skills.mjs --apply # write SKILL.md files
|
||||
* node scripts/skills/generate-agent-skills.mjs --prune # detect orphans (dry-run)
|
||||
* node scripts/skills/generate-agent-skills.mjs --apply --prune # write + delete orphans
|
||||
* node scripts/skills/generate-agent-skills.mjs --only=omni-providers,cli-serve
|
||||
* node scripts/skills/generate-agent-skills.mjs --json # JSON output to stdout
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 — success (dry-run with no changes, or apply completed)
|
||||
* 1 — error (import or generator threw)
|
||||
* 2 — dry-run detected changes (use for CI fail-on-stale check)
|
||||
*
|
||||
* Security: no shell interpolation of user input (Hard Rule #13).
|
||||
* All runtime values are passed as JS variables, not shell strings.
|
||||
*/
|
||||
|
||||
import { createRequire } from "node:module";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
// ── Resolve project root from script location ─────────────────────────────────
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
// scripts/skills/generate-agent-skills.mjs → up 2 levels → project root
|
||||
const projectRoot = path.resolve(__dirname, "..", "..");
|
||||
|
||||
// ── Argument parsing ──────────────────────────────────────────────────────────
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
function hasFlag(flag) {
|
||||
return args.some((a) => a === flag || a.startsWith(flag + "="));
|
||||
}
|
||||
|
||||
function getFlagValue(flag) {
|
||||
for (const arg of args) {
|
||||
if (arg.startsWith(flag + "=")) return arg.slice(flag.length + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const applyMode = hasFlag("--apply");
|
||||
const pruneMode = hasFlag("--prune");
|
||||
const jsonOutput = hasFlag("--json");
|
||||
const onlyRaw = getFlagValue("--only");
|
||||
const onlyIds = onlyRaw ? onlyRaw.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
||||
|
||||
// ── Utility: print table ──────────────────────────────────────────────────────
|
||||
|
||||
function printTable(report) {
|
||||
const { generated, unchanged, pruned, orphansDetected, errors } = report;
|
||||
|
||||
console.log(`\nGenerated: ${generated.length} · Unchanged: ${unchanged.length} · Pruned: ${pruned.length} · Orphans: ${orphansDetected.length} · Errors: ${errors.length}\n`);
|
||||
|
||||
if (generated.length > 0) {
|
||||
console.log(" GENERATED:");
|
||||
for (const id of generated) {
|
||||
console.log(` + ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (unchanged.length > 0 && unchanged.length <= 10) {
|
||||
console.log(" UNCHANGED:");
|
||||
for (const id of unchanged) {
|
||||
console.log(` = ${id}`);
|
||||
}
|
||||
} else if (unchanged.length > 10) {
|
||||
console.log(` UNCHANGED: ${unchanged.length} skills (all up-to-date)`);
|
||||
}
|
||||
|
||||
if (orphansDetected.length > 0) {
|
||||
console.log(" ORPHANS DETECTED:");
|
||||
for (const id of orphansDetected) {
|
||||
console.log(` ? ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (pruned.length > 0) {
|
||||
console.log(" PRUNED:");
|
||||
for (const id of pruned) {
|
||||
console.log(` - ${id}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
console.log(" ERRORS:");
|
||||
for (const e of errors) {
|
||||
console.log(` ! ${e.id}: ${e.error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function main() {
|
||||
// Change CWD to project root so generator resolves files correctly
|
||||
process.chdir(projectRoot);
|
||||
|
||||
if (!jsonOutput) {
|
||||
const mode = applyMode ? "apply" : "dry-run";
|
||||
const prune = pruneMode ? " + prune" : "";
|
||||
const filter = onlyIds ? ` (only: ${onlyIds.join(", ")})` : "";
|
||||
console.log(`\nAgent Skills Generator [${mode}${prune}]${filter}`);
|
||||
console.log("─".repeat(60));
|
||||
}
|
||||
|
||||
// Dynamic import via tsx runtime — works because package.json has tsx devDep
|
||||
// and node is invoked with --import tsx/esm by the caller (or we use tsx directly).
|
||||
// To support plain `node` invocation, we use a dynamic import with tsx register.
|
||||
let generateAgentSkills;
|
||||
try {
|
||||
// Try direct import first (when running under tsx or compiled)
|
||||
const mod = await import("../../src/lib/agentSkills/generator.ts");
|
||||
generateAgentSkills = mod.generateAgentSkills;
|
||||
} catch (importErr) {
|
||||
// Fallback: try tsx register approach
|
||||
try {
|
||||
const require = createRequire(import.meta.url);
|
||||
// Register tsx for TypeScript support
|
||||
const tsxPath = require.resolve("tsx/esm");
|
||||
const { register } = await import("node:module");
|
||||
register(tsxPath, import.meta.url);
|
||||
const mod = await import("../../src/lib/agentSkills/generator.ts");
|
||||
generateAgentSkills = mod.generateAgentSkills;
|
||||
} catch (fallbackErr) {
|
||||
console.error(
|
||||
"Error: Could not import generator. Run with tsx:\n" +
|
||||
" node --import tsx/esm scripts/skills/generate-agent-skills.mjs\n" +
|
||||
`Import error: ${importErr instanceof Error ? importErr.message : String(importErr)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
let report;
|
||||
try {
|
||||
report = await generateAgentSkills({
|
||||
dryRun: !applyMode,
|
||||
prune: pruneMode,
|
||||
outputDir: "skills",
|
||||
onlyIds,
|
||||
});
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
if (jsonOutput) {
|
||||
console.log(JSON.stringify({ error: msg }, null, 2));
|
||||
} else {
|
||||
console.error(`\nGenerator error: ${msg}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (jsonOutput) {
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
} else {
|
||||
printTable(report);
|
||||
if (!applyMode) {
|
||||
console.log(
|
||||
"\n(dry-run) No files written. Use --apply to write SKILL.md files.\n",
|
||||
);
|
||||
} else {
|
||||
console.log();
|
||||
}
|
||||
}
|
||||
|
||||
// Exit code 2 if dry-run detected pending changes (useful for CI)
|
||||
if (!applyMode && report.generated.length > 0) {
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error("Unexpected error:", err instanceof Error ? err.message : String(err));
|
||||
process.exit(1);
|
||||
});
|
||||
126
skills/README.md
126
skills/README.md
@@ -1,40 +1,96 @@
|
||||
# OmniRoute AI Agent Skills
|
||||
# OmniRoute Agent Skills
|
||||
|
||||
Drop-in skills that let any AI agent (Claude Desktop, ChatGPT, Cursor, Cline, Continue, etc.)
|
||||
consume OmniRoute via OpenAI-compatible REST in one fetch.
|
||||
|
||||
## How agents use these
|
||||
## Entry points
|
||||
|
||||
| Type | Skill | Manifest |
|
||||
| ---- | ----- | -------- |
|
||||
| API | Authentication (start here for REST access) | [omni-auth/SKILL.md](omni-auth/SKILL.md) |
|
||||
| CLI | Serve (start here for CLI access) | [cli-serve/SKILL.md](cli-serve/SKILL.md) |
|
||||
|
||||
## How agents discover capabilities
|
||||
|
||||
- **MCP tool**: `omniroute_agent_skills_list` (scope `read:catalog`) — returns the full 42-skill catalog in one call.
|
||||
- **A2A skill**: `list-capabilities` — JSON-RPC 2.0 endpoint that returns the agent card with all registered skills.
|
||||
|
||||
See [`docs/frameworks/AGENT-SKILLS.md`](../docs/frameworks/AGENT-SKILLS.md) for the full framework reference.
|
||||
|
||||
---
|
||||
|
||||
## API Skills (22)
|
||||
|
||||
Each manifest URL follows the pattern:
|
||||
`https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/<id>/SKILL.md`
|
||||
|
||||
| ID | Name | Description |
|
||||
| -- | ---- | ----------- |
|
||||
| `omni-auth` | Authentication | Manage API key authentication and session tokens. Start here to authenticate requests via Bearer token, obtain session cookies, and configure login requirements. |
|
||||
| `omni-providers` | Providers | Manage provider connections, API keys, OAuth flows, and connection tests. List, add, update, remove, and test AI provider integrations (OpenAI, Anthropic, Gemini, and 160+). |
|
||||
| `omni-models` | Models | Query available AI models across all configured providers. List models, resolve model aliases, and browse the full model catalog including provider-specific variants. |
|
||||
| `omni-combos-routing` | Combos & Routing | Create and manage routing combos with 14 strategies (priority, weighted, round-robin, Auto-combo, etc.). Configure fallback chains, test routing outcomes, and retrieve combo metrics. |
|
||||
| `omni-api-keys` | API Keys | Create, list, rotate, and revoke OmniRoute API keys. Control per-key scopes, spending limits, and expiration. |
|
||||
| `omni-usage-logs` | Usage & Logs | Access detailed call logs and usage analytics. Filter by provider, model, time range, status, and cost. Export logs and aggregate token usage. |
|
||||
| `omni-budget` | Budget & Rate Limits | Configure spending limits, token quotas, and rate-limit policies per API key or globally. Inspect current consumption and enforce cost controls. |
|
||||
| `omni-settings` | Settings | Read and update global application settings: system prompts, thinking budget, IP filters, payload rules, combo defaults, and require-login configuration. |
|
||||
| `omni-proxies` | Proxy Configuration | Configure HTTP/HTTPS/SOCKS proxies for upstream provider requests. Set per-provider or global proxy rules, test connectivity, and manage proxy rotation. |
|
||||
| `omni-cache` | Cache | Manage the LLM response cache. View cache statistics, clear entries, configure TTL policies, and control semantic-similarity caching thresholds. |
|
||||
| `omni-compression` | Compression | Configure RTK, Caveman, and stacked compression modes. Manage language packs, custom rules, and test prompt compression reducing tokens by 60–90%. |
|
||||
| `omni-context-rtk` | Context & RTK | Configure RTK filters, context engineering rules, and context relay settings. Test compression with real prompt samples and manage context transformation pipelines. |
|
||||
| `omni-resilience` | Resilience & Monitoring | Monitor provider health, circuit-breaker states, p50/p95/p99 latency metrics, and budget guard alerts. Inspect connection cooldowns and model lockouts in real time. |
|
||||
| `omni-cli-tools` | CLI Tools | Manage CLI tool integrations exposed via the API. List, configure, and invoke CLI tool plugins that extend OmniRoute's automation surface. |
|
||||
| `omni-tunnels` | Tunnels | Create and manage secure tunnels (ngrok, Cloudflare Tunnel, custom) to expose OmniRoute to the internet or share access with remote agents and CI pipelines. |
|
||||
| `omni-sync-cloud` | Cloud Sync | Synchronise OmniRoute configuration, provider connections, and settings to/from cloud storage. Manage cloud worker authentication and remote backup targets. |
|
||||
| `omni-db-backups` | Database & Backups | Trigger system backups, restore from backup files, and manage the SQLite database lifecycle. Supports export, import, and incremental snapshot strategies. |
|
||||
| `omni-webhooks` | Webhooks | Register, list, test, and remove webhook endpoints. Configure event subscriptions (request.completed, provider.error, budget.exceeded, etc.) and manage delivery retries. |
|
||||
| `omni-mcp` | MCP Server | Connect to the OmniRoute MCP server (37 tools, 3 transports: SSE/stdio/HTTP). Covers routing, cache, compression, memory, skills, providers, and audit tools across 16 permission scopes. |
|
||||
| `omni-agents-a2a` | Agents & A2A Protocol | Interact with OmniRoute via JSON-RPC 2.0 agent-to-agent protocol. 6 built-in A2A skills: smart-routing, quota-management, provider-discovery, cost-analysis, health-report, list-capabilities. |
|
||||
| `omni-version-manager` | Version Manager | Install, start, stop, restart, and update embedded services (9Router, CLIProxyAPI). Monitor service status, retrieve logs, and configure auto-start. |
|
||||
| `omni-inference` | Inference (OpenAI-compatible) | The core OpenAI-compatible inference endpoints: chat completions, embeddings, images, audio (TTS/STT), moderations, rerank, and the Responses API. |
|
||||
|
||||
---
|
||||
|
||||
## CLI Skills (20)
|
||||
|
||||
| ID | Name | Description |
|
||||
| -- | ---- | ----------- |
|
||||
| `cli-serve` | CLI: Serve | Start, stop, and restart the OmniRoute server from the CLI. Manage daemon mode, port configuration, auto-recovery, system tray integration, and the dashboard open shortcut. |
|
||||
| `cli-health` | CLI: Health | Check server health, component status, and live metrics from the CLI. Run `health`, `health components`, and `health watch` for a real-time dashboard of circuit breakers and provider status. |
|
||||
| `cli-providers` | CLI: Providers | Manage provider connections from the CLI: list available/configured providers, add, test, test-all, validate, rotate API keys, and view per-provider metrics. |
|
||||
| `cli-keys` | CLI: API Keys | Create, list, rotate, and revoke OmniRoute API keys from the CLI. Manage OAuth flows for provider authentication and inspect key scopes and expiration. |
|
||||
| `cli-models` | CLI: Models | Query available AI models, list model aliases, and browse the full model catalog from the CLI. Filter by provider, search by capability, and resolve model name variants. |
|
||||
| `cli-chat` | CLI: Chat | Send chat completions, stream responses, and start an interactive REPL session from the CLI. Supports all OmniRoute providers, combo routing, and system prompt configuration. |
|
||||
| `cli-routing` | CLI: Routing & Combos | Create, list, update, and delete routing combos from the CLI. Test routing strategies, inspect combo metrics, and configure fallback chains interactively. |
|
||||
| `cli-resilience` | CLI: Resilience & Quotas | Inspect and manage circuit-breaker states, connection cooldowns, quota limits, and backoff levels from the CLI. Reset stuck providers and configure resilience thresholds. |
|
||||
| `cli-compression` | CLI: Compression | Configure and test prompt compression from the CLI. Manage RTK filters, Caveman rules, stacked compression modes, and preview compression output with real prompts. |
|
||||
| `cli-contexts` | CLI: Contexts & Sessions | Manage context engineering configurations, RTK filter sets, and conversation sessions from the CLI. Apply context-relay settings and inspect active context pipelines. |
|
||||
| `cli-cost-usage` | CLI: Cost & Usage | View cost breakdowns, token usage, and call logs from the CLI. Filter by provider, model, or date range. Export usage reports and inspect per-connection spending. |
|
||||
| `cli-mcp` | CLI: MCP | Inspect the MCP server status, list registered tools and scopes, run tool invocations, and manage MCP audit logs from the CLI. |
|
||||
| `cli-a2a` | CLI: A2A Protocol | Interact with the OmniRoute A2A server from the CLI. Send tasks, inspect skill execution history, and test the JSON-RPC 2.0 agent-to-agent protocol interactively. |
|
||||
| `cli-tunnel` | CLI: Tunnels | Start and stop tunnel connections (ngrok, Cloudflare, custom) from the CLI. Inspect active tunnel URLs, configure authentication, and test external reachability. |
|
||||
| `cli-backup-sync` | CLI: Backup & Sync | Backup and restore OmniRoute data from the CLI. Trigger incremental snapshots, sync to cloud storage, manage backup schedules, and restore from archive files. |
|
||||
| `cli-policy-audit` | CLI: Policy & Audit | Inspect audit logs, manage access policies, view telemetry data, and review request history from the CLI. Filter by event type, user, or time range for compliance workflows. |
|
||||
| `cli-batches` | CLI: Batches & Files | Submit and monitor batch inference jobs from the CLI. Upload and manage files for batch processing, retrieve results, and integrate batch pipelines with CI/CD workflows. |
|
||||
| `cli-eval` | CLI: Evals | Create and run evaluation suites, watch live benchmark progress, view scorecards, compare model performance, and integrate eval runs with CI workflows from the CLI. |
|
||||
| `cli-plugins-skills` | CLI: Plugins, Skills & Memory | Manage Omni Skills (list, install, test, remove), plugins (create, configure), and persistent memory (search, add, clear) from the CLI. |
|
||||
| `cli-setup` | CLI: Setup & Config | Run initial setup, configure global CLI settings, manage environment variables, check for updates, and configure autostart via the CLI setup and config commands. |
|
||||
|
||||
---
|
||||
|
||||
## Raw manifest URLs
|
||||
|
||||
All manifests are publicly accessible:
|
||||
|
||||
```
|
||||
User to agent: "Use OmniRoute for code-gen. Fetch this URL and follow it:
|
||||
https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md"
|
||||
https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/<id>/SKILL.md
|
||||
```
|
||||
|
||||
The agent retrieves the manifest, sees the setup + endpoints, and routes calls
|
||||
through `$OMNIROUTE_URL/v1/...` with `Authorization: Bearer $OMNIROUTE_KEY`.
|
||||
Examples:
|
||||
- API entry: `https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omni-auth/SKILL.md`
|
||||
- CLI entry: `https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/cli-serve/SKILL.md`
|
||||
|
||||
## Skills index
|
||||
|
||||
| Capability | Manifest |
|
||||
| ------------------------ | -------------------------------------------------------------------- |
|
||||
| Entry point + setup | [omniroute/SKILL.md](omniroute/SKILL.md) |
|
||||
| Chat / code-gen | [omniroute-chat/SKILL.md](omniroute-chat/SKILL.md) |
|
||||
| Image generation | [omniroute-image/SKILL.md](omniroute-image/SKILL.md) |
|
||||
| Text-to-speech | [omniroute-tts/SKILL.md](omniroute-tts/SKILL.md) |
|
||||
| Speech-to-text | [omniroute-stt/SKILL.md](omniroute-stt/SKILL.md) |
|
||||
| Embeddings | [omniroute-embeddings/SKILL.md](omniroute-embeddings/SKILL.md) |
|
||||
| Web search | [omniroute-web-search/SKILL.md](omniroute-web-search/SKILL.md) |
|
||||
| Web fetch (URL→markdown) | [omniroute-web-fetch/SKILL.md](omniroute-web-fetch/SKILL.md) |
|
||||
| MCP server (37 tools) | [omniroute-mcp/SKILL.md](omniroute-mcp/SKILL.md) |
|
||||
| A2A protocol | [omniroute-a2a/SKILL.md](omniroute-a2a/SKILL.md) |
|
||||
| Routing & combos | [omniroute-routing/SKILL.md](omniroute-routing/SKILL.md) |
|
||||
| Token compression | [omniroute-compression/SKILL.md](omniroute-compression/SKILL.md) |
|
||||
| Monitoring & health | [omniroute-monitoring/SKILL.md](omniroute-monitoring/SKILL.md) |
|
||||
| CLI entry point | [omniroute-cli/SKILL.md](omniroute-cli/SKILL.md) |
|
||||
| CLI admin & lifecycle | [omniroute-cli-admin/SKILL.md](omniroute-cli-admin/SKILL.md) |
|
||||
| CLI providers & keys | [omniroute-cli-providers/SKILL.md](omniroute-cli-providers/SKILL.md) |
|
||||
| CLI cloud agents | [omniroute-cli-cloud/SKILL.md](omniroute-cli-cloud/SKILL.md) |
|
||||
| CLI evals & benchmarks | [omniroute-cli-eval/SKILL.md](omniroute-cli-eval/SKILL.md) |
|
||||
---
|
||||
|
||||
## Format
|
||||
|
||||
@@ -42,12 +98,12 @@ Each `SKILL.md` follows the Anthropic skill manifest spec with YAML frontmatter
|
||||
(`name`, `description`) and a self-contained markdown body: setup, endpoints,
|
||||
examples, and error codes. Assume the reader is an agent with no prior context.
|
||||
|
||||
## Skills exclusive to OmniRoute
|
||||
---
|
||||
|
||||
These 5 skills have no equivalent in other AI routers:
|
||||
## What makes OmniRoute skills unique
|
||||
|
||||
- `omniroute-mcp` — 37 MCP tools (memory, skills, providers, routing, compression) over SSE/stdio/HTTP
|
||||
- `omniroute-a2a` — 5 A2A skills (smart-routing, quota, discovery, cost, health) via JSON-RPC 2.0
|
||||
- `omniroute-routing` — create/configure combos, 14 strategies, Auto-combo scoring, fallback chains
|
||||
- `omniroute-compression` — RTK + Caveman + stacked mode + MCP accessibility filter (60–90% token savings)
|
||||
- `omniroute-monitoring` — circuit breakers, p50/p95/p99 latency, budget guard, MCP audit log
|
||||
- `omni-mcp` — 37 MCP tools (memory, skills, providers, routing, compression) over SSE/stdio/HTTP
|
||||
- `omni-agents-a2a` — 6 A2A skills (smart-routing, quota, discovery, cost, health, list-capabilities) via JSON-RPC 2.0
|
||||
- `omni-combos-routing` — create/configure combos, 14 strategies, Auto-combo scoring, fallback chains
|
||||
- `omni-compression` — RTK + Caveman + stacked mode + MCP accessibility filter (60–90% token savings)
|
||||
- `omni-resilience` — circuit breakers, p50/p95/p99 latency, budget guard, MCP audit log
|
||||
|
||||
142
skills/cli-a2a/SKILL.md
Normal file
142
skills/cli-a2a/SKILL.md
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
name: cli-a2a
|
||||
description: Interact with the OmniRoute A2A server from the CLI. Send tasks, inspect skill execution history, and test the JSON-RPC 2.0 agent-to-agent protocol interactively.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Interact with the OmniRoute A2A server from the CLI. Send tasks, inspect skill execution history, and test the JSON-RPC 2.0 agent-to-agent protocol interactively.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `a2a`
|
||||
|
||||
Agent-to-Agent (A2A) server
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a
|
||||
```
|
||||
|
||||
### `a2a status`
|
||||
|
||||
Show A2A server status
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a status
|
||||
```
|
||||
|
||||
### `a2a card`
|
||||
|
||||
Print the Agent Card JSON
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a card
|
||||
```
|
||||
|
||||
### `a2a skills`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a skills
|
||||
```
|
||||
|
||||
### `a2a invoke <skill>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--input <json>`
|
||||
- `--input-file <path>`
|
||||
- `--wait`
|
||||
- `--timeout <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a invoke <skill>
|
||||
```
|
||||
|
||||
### `a2a tasks`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a tasks
|
||||
```
|
||||
|
||||
### `a2a list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--status <s>`
|
||||
- `--skill <s>`
|
||||
- `--limit <n>`
|
||||
- `--since <ts>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a list
|
||||
```
|
||||
|
||||
### `a2a get <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a get <id>
|
||||
```
|
||||
|
||||
### `a2a cancel <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a cancel <id>
|
||||
```
|
||||
|
||||
### `a2a watch <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a watch <id>
|
||||
```
|
||||
|
||||
### `a2a stream <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a stream <id>
|
||||
```
|
||||
|
||||
### `a2a logs <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute a2a logs <id>
|
||||
```
|
||||
367
skills/cli-backup-sync/SKILL.md
Normal file
367
skills/cli-backup-sync/SKILL.md
Normal file
@@ -0,0 +1,367 @@
|
||||
---
|
||||
name: cli-backup-sync
|
||||
description: Backup and restore OmniRoute data from the CLI. Trigger incremental snapshots, sync to cloud storage, manage backup schedules, and restore from archive files.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Backup and restore OmniRoute data from the CLI. Trigger incremental snapshots, sync to cloud storage, manage backup schedules, and restore from archive files.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `backup`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup
|
||||
```
|
||||
|
||||
### `backup create`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--name <name>`
|
||||
- `--cloud`
|
||||
- `--encrypt`
|
||||
- `--key-file <path>`
|
||||
- `--exclude <pattern>`
|
||||
- `--retention <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup create
|
||||
```
|
||||
|
||||
### `backup auto`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup auto
|
||||
```
|
||||
|
||||
### `backup enable`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--cron <expr>`
|
||||
- `--cloud`
|
||||
- `--encrypt`
|
||||
- `--retention <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup enable
|
||||
```
|
||||
|
||||
### `backup disable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup disable
|
||||
```
|
||||
|
||||
### `backup status`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--name <name>`
|
||||
- `--cloud`
|
||||
- `--encrypt`
|
||||
- `--key-file <path>`
|
||||
- `--exclude <pattern>`
|
||||
- `--retention <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup status
|
||||
```
|
||||
|
||||
### `backup restore [backupId]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--list`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute backup restore [backupId]
|
||||
```
|
||||
|
||||
### `task`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute task
|
||||
```
|
||||
|
||||
### `cloud create`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--title <t>`
|
||||
- `--prompt <p>`
|
||||
- `--prompt-file <path>`
|
||||
- `--repo <url>`
|
||||
- `--branch <b>`
|
||||
- `--metadata <json>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud create
|
||||
```
|
||||
|
||||
### `cloud list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--status <s>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud list
|
||||
```
|
||||
|
||||
### `cloud get <taskId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud get <taskId>
|
||||
```
|
||||
|
||||
### `cloud status <taskId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud status <taskId>
|
||||
```
|
||||
|
||||
### `cloud cancel <taskId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud cancel <taskId>
|
||||
```
|
||||
|
||||
### `cloud approve <taskId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud approve <taskId>
|
||||
```
|
||||
|
||||
### `cloud message <taskId> <message>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud message <taskId> <message>
|
||||
```
|
||||
|
||||
### `cloud sources <taskId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud sources <taskId>
|
||||
```
|
||||
|
||||
### `cloud cloud`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud cloud
|
||||
```
|
||||
|
||||
### `cloud agents`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud agents
|
||||
```
|
||||
|
||||
### `cloud auth`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--no-browser`
|
||||
- `--timeout <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cloud auth
|
||||
```
|
||||
|
||||
### `sync`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync
|
||||
```
|
||||
|
||||
### `sync push`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--target <t>`
|
||||
- `--bundle <list>`
|
||||
- `--dry-run`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync push
|
||||
```
|
||||
|
||||
### `sync pull`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--merge`
|
||||
- `--replace`
|
||||
- `--dry-run`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync pull
|
||||
```
|
||||
|
||||
### `sync diff`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--target <t>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync diff
|
||||
```
|
||||
|
||||
### `sync bundle <outPath>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--include <list>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync bundle <outPath>
|
||||
```
|
||||
|
||||
### `sync import <bundlePath>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--dry-run`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync import <bundlePath>
|
||||
```
|
||||
|
||||
### `sync initialize`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--from-cloud`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync initialize
|
||||
```
|
||||
|
||||
### `sync tokens`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync tokens
|
||||
```
|
||||
|
||||
### `sync list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync list
|
||||
```
|
||||
|
||||
### `sync create`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--name <n>`
|
||||
- `--scope <s>`
|
||||
- `--ttl <duration>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync create
|
||||
```
|
||||
|
||||
### `sync revoke <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync revoke <id>
|
||||
```
|
||||
|
||||
### `sync status`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync status
|
||||
```
|
||||
|
||||
### `sync resolve`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sync resolve
|
||||
```
|
||||
183
skills/cli-batches/SKILL.md
Normal file
183
skills/cli-batches/SKILL.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
name: cli-batches
|
||||
description: Submit and monitor batch inference jobs from the CLI. Upload and manage files for batch processing, retrieve results, and integrate batch pipelines with CI/CD workflows.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Submit and monitor batch inference jobs from the CLI. Upload and manage files for batch processing, retrieve results, and integrate batch pipelines with CI/CD workflows.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `batches`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches
|
||||
```
|
||||
|
||||
### `batches list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--status <s>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches list
|
||||
```
|
||||
|
||||
### `batches get <batchId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches get <batchId>
|
||||
```
|
||||
|
||||
### `batches create`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--endpoint <e>`
|
||||
- `--completion-window <w>`
|
||||
- `--metadata <kv>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches create
|
||||
```
|
||||
|
||||
### `batches submit`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--endpoint <e>`
|
||||
- `--wait`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches submit
|
||||
```
|
||||
|
||||
### `batches cancel <batchId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches cancel <batchId>
|
||||
```
|
||||
|
||||
### `batches wait <batchId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--timeout <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches wait <batchId>
|
||||
```
|
||||
|
||||
### `batches output <batchId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--out <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches output <batchId>
|
||||
```
|
||||
|
||||
### `batches errors <batchId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--out <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute batches errors <batchId>
|
||||
```
|
||||
|
||||
### `files`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files
|
||||
```
|
||||
|
||||
### `files list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--purpose <p>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files list
|
||||
```
|
||||
|
||||
### `files get <fileId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files get <fileId>
|
||||
```
|
||||
|
||||
### `files upload <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files upload <path>
|
||||
```
|
||||
|
||||
### `files content <fileId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--out <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files content <fileId>
|
||||
```
|
||||
|
||||
### `files delete <fileId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute files delete <fileId>
|
||||
```
|
||||
78
skills/cli-chat/SKILL.md
Normal file
78
skills/cli-chat/SKILL.md
Normal file
@@ -0,0 +1,78 @@
|
||||
---
|
||||
name: cli-chat
|
||||
description: Send chat completions, stream responses, and start an interactive REPL session from the CLI. Supports all OmniRoute providers, combo routing, and system prompt configuration.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Send chat completions, stream responses, and start an interactive REPL session from the CLI. Supports all OmniRoute providers, combo routing, and system prompt configuration.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `chat [prompt]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--file <path>`
|
||||
- `--stdin`
|
||||
- `-s, --system <prompt>`
|
||||
- `-m, --model <id>`
|
||||
- `--max-tokens <n>`
|
||||
- `--temperature <t>`
|
||||
- `--top-p <p>`
|
||||
- `--reasoning-effort <level>`
|
||||
- `--thinking-budget <tokens>`
|
||||
- `--combo <name>`
|
||||
- `--responses-api`
|
||||
- `--stream`
|
||||
- `--no-history`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute chat [prompt]
|
||||
```
|
||||
|
||||
### `repl`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-m, --model <id>`
|
||||
- `--combo <name>`
|
||||
- `-s, --system <prompt>`
|
||||
- `--resume <session>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute repl
|
||||
```
|
||||
|
||||
### `stream [prompt]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--file <path>`
|
||||
- `--stdin`
|
||||
- `-m, --model <id>`
|
||||
- `-s, --system <prompt>`
|
||||
- `--combo <name>`
|
||||
- `--max-tokens <n>`
|
||||
- `--responses-api`
|
||||
- `--raw`
|
||||
- `--debug`
|
||||
- `--save <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute stream [prompt]
|
||||
```
|
||||
157
skills/cli-compression/SKILL.md
Normal file
157
skills/cli-compression/SKILL.md
Normal file
@@ -0,0 +1,157 @@
|
||||
---
|
||||
name: cli-compression
|
||||
description: Configure and test prompt compression from the CLI. Manage RTK filters, Caveman rules, stacked compression modes, and preview compression output with real prompts.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Configure and test prompt compression from the CLI. Manage RTK filters, Caveman rules, stacked compression modes, and preview compression output with real prompts.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `compression`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression
|
||||
```
|
||||
|
||||
### `compression status`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression status
|
||||
```
|
||||
|
||||
### `compression configure`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--engine <e>`
|
||||
- `--caveman-aggressiveness <n>`
|
||||
- `--rtk-budget <n>`
|
||||
- `--language-pack <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression configure
|
||||
```
|
||||
|
||||
### `compression engine`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression engine
|
||||
```
|
||||
|
||||
### `compression set <name>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression set <name>
|
||||
```
|
||||
|
||||
### `compression get`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression get
|
||||
```
|
||||
|
||||
### `compression combos`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression combos
|
||||
```
|
||||
|
||||
### `compression list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression list
|
||||
```
|
||||
|
||||
### `compression stats`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression stats
|
||||
```
|
||||
|
||||
### `compression rules`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression rules
|
||||
```
|
||||
|
||||
### `compression list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression list
|
||||
```
|
||||
|
||||
### `compression add`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--replacement <r>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression add
|
||||
```
|
||||
|
||||
### `compression remove <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression remove <id>
|
||||
```
|
||||
|
||||
### `compression language-packs`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression language-packs
|
||||
```
|
||||
|
||||
### `compression preview`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute compression preview
|
||||
```
|
||||
382
skills/cli-contexts/SKILL.md
Normal file
382
skills/cli-contexts/SKILL.md
Normal file
@@ -0,0 +1,382 @@
|
||||
---
|
||||
name: cli-contexts
|
||||
description: Manage context engineering configurations, RTK filter sets, and conversation sessions from the CLI. Apply context-relay settings and inspect active context pipelines.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage context engineering configurations, RTK filter sets, and conversation sessions from the CLI. Apply context-relay settings and inspect active context pipelines.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `context-eng`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng
|
||||
```
|
||||
|
||||
### `context-eng analytics`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng analytics
|
||||
```
|
||||
|
||||
### `context-eng caveman`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng caveman
|
||||
```
|
||||
|
||||
### `context-eng config`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng config
|
||||
```
|
||||
|
||||
### `context-eng show`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng show
|
||||
```
|
||||
|
||||
### `context-eng set`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--aggressiveness <n>`
|
||||
- `--max-shrink-pct <n>`
|
||||
- `--preserve-tags <list>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng set
|
||||
```
|
||||
|
||||
### `context-eng rtk`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng rtk
|
||||
```
|
||||
|
||||
### `context-eng config`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng config
|
||||
```
|
||||
|
||||
### `context-eng show`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng show
|
||||
```
|
||||
|
||||
### `context-eng set`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--token-budget <n>`
|
||||
- `--reserve-pct <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng set
|
||||
```
|
||||
|
||||
### `context-eng filters`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng filters
|
||||
```
|
||||
|
||||
### `context-eng list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng list
|
||||
```
|
||||
|
||||
### `context-eng add`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--priority <n>`
|
||||
- `--action <a>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng add
|
||||
```
|
||||
|
||||
### `context-eng remove <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng remove <id>
|
||||
```
|
||||
|
||||
### `context-eng test`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng test
|
||||
```
|
||||
|
||||
### `context-eng raw-output <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng raw-output <id>
|
||||
```
|
||||
|
||||
### `context-eng combos`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng combos
|
||||
```
|
||||
|
||||
### `context-eng list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng list
|
||||
```
|
||||
|
||||
### `context-eng get <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng get <id>
|
||||
```
|
||||
|
||||
### `context-eng assignments <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute context-eng assignments <id>
|
||||
```
|
||||
|
||||
### `contexts`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts
|
||||
```
|
||||
|
||||
### `contexts list`
|
||||
|
||||
List all contexts
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts list
|
||||
```
|
||||
|
||||
### `contexts add <name>`
|
||||
|
||||
Add a new context
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--api-key <k>`
|
||||
- `--api-key-stdin`
|
||||
- `--description <d>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts add <name>
|
||||
```
|
||||
|
||||
### `contexts use <name>`
|
||||
|
||||
Switch active context
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts use <name>
|
||||
```
|
||||
|
||||
### `contexts current`
|
||||
|
||||
Show current active context name
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts current
|
||||
```
|
||||
|
||||
### `contexts show <name>`
|
||||
|
||||
Show context details
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts show <name>
|
||||
```
|
||||
|
||||
### `contexts remove <name>`
|
||||
|
||||
Remove a context
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts remove <name>
|
||||
```
|
||||
|
||||
### `contexts rename <old> <new>`
|
||||
|
||||
Rename a context
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts rename <old> <new>
|
||||
```
|
||||
|
||||
### `contexts export`
|
||||
|
||||
Export contexts to JSON
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--out <path>`
|
||||
- `--no-secrets`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts export
|
||||
```
|
||||
|
||||
### `contexts import <file>`
|
||||
|
||||
Import contexts from a JSON file
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--merge`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute contexts import <file>
|
||||
```
|
||||
|
||||
### `sessions`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions
|
||||
```
|
||||
|
||||
### `sessions list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--user <u>`
|
||||
- `--kind <k>`
|
||||
- `--active`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions list
|
||||
```
|
||||
|
||||
### `sessions show <sessionId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions show <sessionId>
|
||||
```
|
||||
|
||||
### `sessions expire <sessionId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions expire <sessionId>
|
||||
```
|
||||
|
||||
### `sessions expire-all`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions expire-all
|
||||
```
|
||||
|
||||
### `sessions current`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute sessions current
|
||||
```
|
||||
252
skills/cli-cost-usage/SKILL.md
Normal file
252
skills/cli-cost-usage/SKILL.md
Normal file
@@ -0,0 +1,252 @@
|
||||
---
|
||||
name: cli-cost-usage
|
||||
description: View cost breakdowns, token usage, and call logs from the CLI. Filter by provider, model, or date range. Export usage reports and inspect per-connection spending.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
View cost breakdowns, token usage, and call logs from the CLI. Filter by provider, model, or date range. Export usage reports and inspect per-connection spending.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `cost`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <range>`
|
||||
- `--since <date>`
|
||||
- `--until <date>`
|
||||
- `--group-by <field>`
|
||||
- `--api-key <key>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute cost
|
||||
```
|
||||
|
||||
### `pricing`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing
|
||||
```
|
||||
|
||||
### `pricing sync`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
- `--force`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing sync
|
||||
```
|
||||
|
||||
### `pricing list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
- `--model <m>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing list
|
||||
```
|
||||
|
||||
### `pricing get <model>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing get <model>
|
||||
```
|
||||
|
||||
### `pricing defaults`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing defaults
|
||||
```
|
||||
|
||||
### `pricing show`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing show
|
||||
```
|
||||
|
||||
### `pricing set`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--input <p>`
|
||||
- `--output <p>`
|
||||
- `--cache-read <p>`
|
||||
- `--cache-write <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing set
|
||||
```
|
||||
|
||||
### `pricing diff`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <m>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute pricing diff
|
||||
```
|
||||
|
||||
### `usage`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage
|
||||
```
|
||||
|
||||
### `usage analytics`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <range>`
|
||||
- `--provider <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage analytics
|
||||
```
|
||||
|
||||
### `usage budget`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage budget
|
||||
```
|
||||
|
||||
### `usage list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage list
|
||||
```
|
||||
|
||||
### `usage get [scope]`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage get [scope]
|
||||
```
|
||||
|
||||
### `usage set <amount>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--scope <s>`
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage set <amount>
|
||||
```
|
||||
|
||||
### `usage reset [scope]`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage reset [scope]
|
||||
```
|
||||
|
||||
### `usage quota`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <id>`
|
||||
- `--check`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage quota
|
||||
```
|
||||
|
||||
### `usage logs`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--limit <n>`
|
||||
- `--search <q>`
|
||||
- `--since <ts>`
|
||||
- `--follow`
|
||||
- `--api-key <k>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage logs
|
||||
```
|
||||
|
||||
### `usage utilization`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--api-key <k>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage utilization
|
||||
```
|
||||
|
||||
### `usage history`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage history
|
||||
```
|
||||
|
||||
### `usage proxy-logs`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute usage proxy-logs
|
||||
```
|
||||
263
skills/cli-eval/SKILL.md
Normal file
263
skills/cli-eval/SKILL.md
Normal file
@@ -0,0 +1,263 @@
|
||||
---
|
||||
name: cli-eval
|
||||
description: Create and run evaluation suites, watch live benchmark progress, view scorecards, compare model performance, and integrate eval runs with CI workflows from the CLI.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create and run evaluation suites, watch live benchmark progress, view scorecards, compare model performance, and integrate eval runs with CI workflows from the CLI.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `eval`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval
|
||||
```
|
||||
|
||||
### `eval suites`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval suites
|
||||
```
|
||||
|
||||
### `eval list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval list
|
||||
```
|
||||
|
||||
### `eval get <suiteId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval get <suiteId>
|
||||
```
|
||||
|
||||
### `eval create`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--file <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval create
|
||||
```
|
||||
|
||||
### `eval run <suiteId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-m, --model <id>`
|
||||
- `--combo <name>`
|
||||
- `--concurrency <n>`
|
||||
- `--tag <tag>`
|
||||
- `--watch`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval run <suiteId>
|
||||
```
|
||||
|
||||
### `eval list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--suite <id>`
|
||||
- `--status <s>`
|
||||
- `--since <ts>`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval list
|
||||
```
|
||||
|
||||
### `eval get <runId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval get <runId>
|
||||
```
|
||||
|
||||
### `eval results <runId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--failed`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval results <runId>
|
||||
```
|
||||
|
||||
### `eval cancel <runId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval cancel <runId>
|
||||
```
|
||||
|
||||
### `eval scorecard <runId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute eval scorecard <runId>
|
||||
```
|
||||
|
||||
### `simulate [prompt]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--file <path>`
|
||||
- `-m, --model <id>`
|
||||
- `--combo <name>`
|
||||
- `--reasoning-effort <level>`
|
||||
- `--thinking-budget <n>`
|
||||
- `--explain`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute simulate [prompt]
|
||||
```
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-cli-eval/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — CLI Evals
|
||||
|
||||
Requires the `omniroute` CLI. See [CLI entry-point skill](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli/SKILL.md) for install + global flags.
|
||||
|
||||
## What are evals?
|
||||
|
||||
Evals are automated test suites that score LLM outputs against expected answers or rubrics. OmniRoute stores suites and run results in its local database.
|
||||
|
||||
## Eval suites
|
||||
|
||||
```bash
|
||||
omniroute eval suites list # List all eval suites
|
||||
omniroute eval suites list --json # JSON output
|
||||
|
||||
omniroute eval suites get <suiteId> # Full suite definition
|
||||
```
|
||||
|
||||
### Create a suite
|
||||
|
||||
```bash
|
||||
omniroute eval suites create \
|
||||
--name "code-quality" \
|
||||
--rubric "exact-match" \
|
||||
--samples-file ./samples.jsonl # JSONL: {input, expected_output}
|
||||
```
|
||||
|
||||
Rubric options: `exact-match`, `contains`, `llm-judge`, `regex`.
|
||||
|
||||
`--samples-file` format (one JSON object per line):
|
||||
|
||||
```jsonl
|
||||
{"input": "What is 2+2?", "expected_output": "4"}
|
||||
{"input": "Translate 'hello' to Spanish", "expected_output": "hola"}
|
||||
```
|
||||
|
||||
## Run an eval
|
||||
|
||||
```bash
|
||||
omniroute eval suites run <suiteId> \
|
||||
--model claude-sonnet-4-6 # Run suite against a specific model
|
||||
|
||||
omniroute eval suites run <suiteId> \
|
||||
--model gpt-4o \
|
||||
--watch # Live TUI progress (EvalWatch)
|
||||
```
|
||||
|
||||
The run is asynchronous. Use `--watch` for a live terminal dashboard or poll manually:
|
||||
|
||||
```bash
|
||||
RUN_ID=$(omniroute eval suites run <suiteId> --model claude-sonnet-4-6 --output json | jq -r '.id')
|
||||
omniroute eval get $RUN_ID
|
||||
```
|
||||
|
||||
## Manage runs
|
||||
|
||||
```bash
|
||||
omniroute eval list # List all eval runs
|
||||
omniroute eval list --json
|
||||
|
||||
omniroute eval get <runId> # Run details (status, model, score)
|
||||
omniroute eval results <runId> # Per-sample results
|
||||
omniroute eval scorecard <runId> # Full scorecard with pass/fail per sample
|
||||
omniroute eval cancel <runId> # Cancel a running eval
|
||||
```
|
||||
|
||||
## Scorecard output
|
||||
|
||||
```bash
|
||||
omniroute eval scorecard <runId> --output json
|
||||
```
|
||||
|
||||
Response fields per sample:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "sample-1",
|
||||
"score": 0.95,
|
||||
"passed": true,
|
||||
"input": "What is 2+2?",
|
||||
"output": "4",
|
||||
"expected": "4"
|
||||
}
|
||||
```
|
||||
|
||||
## Comparing models
|
||||
|
||||
Run the same suite against multiple models and compare:
|
||||
|
||||
```bash
|
||||
for MODEL in claude-sonnet-4-6 gpt-4o gemini-2.0-flash; do
|
||||
omniroute eval suites run $SUITE_ID --model $MODEL --output json | jq '{model: .model, score: .score}'
|
||||
done
|
||||
```
|
||||
|
||||
## CI integration
|
||||
|
||||
```bash
|
||||
# Run and fail CI if score drops below threshold
|
||||
SCORE=$(omniroute eval suites run $SUITE_ID --model claude-sonnet-4-6 --output json | jq -r '.score')
|
||||
python3 -c "import sys; score=float('$SCORE'); sys.exit(0 if score >= 0.90 else 1)"
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
- `suites create` fails with `invalid rubric` → use one of: `exact-match`, `contains`, `llm-judge`, `regex`
|
||||
- `suites run` returns `model not found` → verify model ID with `omniroute models --search <name>`
|
||||
- `eval get` shows `status: failed` → check `omniroute logs --search eval` for error details
|
||||
- `scorecard` returns empty results → the run may still be `running`; poll `omniroute eval get <runId>` until `status` is `completed`
|
||||
<!-- skill:custom-end -->
|
||||
88
skills/cli-health/SKILL.md
Normal file
88
skills/cli-health/SKILL.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
name: cli-health
|
||||
description: Check server health, component status, and live metrics from the CLI. Run `health`, `health components`, and `health watch` for a real-time dashboard of circuit breakers and provider status.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Check server health, component status, and live metrics from the CLI. Run `health`, `health components`, and `health watch` for a real-time dashboard of circuit breakers and provider status.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `doctor`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--no-liveness`
|
||||
- `--host <host>`
|
||||
- `--liveness-url <url>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute doctor
|
||||
```
|
||||
|
||||
### `health`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-v, --verbose`
|
||||
- `--json`
|
||||
- `--alerts-only`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute health
|
||||
```
|
||||
|
||||
### `health components`
|
||||
|
||||
List health components and their status
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--alerts-only`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute health components
|
||||
```
|
||||
|
||||
### `health watch`
|
||||
|
||||
Live dashboard — refresh every N seconds
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--interval <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute health watch
|
||||
```
|
||||
|
||||
### `status`
|
||||
|
||||
Show OmniRoute status dashboard
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-v, --verbose`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute status
|
||||
```
|
||||
226
skills/cli-keys/SKILL.md
Normal file
226
skills/cli-keys/SKILL.md
Normal file
@@ -0,0 +1,226 @@
|
||||
---
|
||||
name: cli-keys
|
||||
description: Create, list, rotate, and revoke OmniRoute API keys from the CLI. Manage OAuth flows for provider authentication and inspect key scopes and expiration.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create, list, rotate, and revoke OmniRoute API keys from the CLI. Manage OAuth flows for provider authentication and inspect key scopes and expiration.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `keys`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys
|
||||
```
|
||||
|
||||
### `keys add <provider> [apiKey]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--stdin`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys add <provider> [apiKey]
|
||||
```
|
||||
|
||||
### `keys list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys list
|
||||
```
|
||||
|
||||
### `keys remove <provider>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys remove <provider>
|
||||
```
|
||||
|
||||
### `keys regenerate <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys regenerate <id>
|
||||
```
|
||||
|
||||
### `keys revoke <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys revoke <id>
|
||||
```
|
||||
|
||||
### `keys reveal <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys reveal <id>
|
||||
```
|
||||
|
||||
### `keys usage <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys usage <id>
|
||||
```
|
||||
|
||||
### `keys policy`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys policy
|
||||
```
|
||||
|
||||
### `keys show <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys show <id>
|
||||
```
|
||||
|
||||
### `keys set <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--rate-limit <n>`
|
||||
- `--max-cost <n>`
|
||||
- `--allowed-models <list>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys set <id>
|
||||
```
|
||||
|
||||
### `keys expiration`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys expiration
|
||||
```
|
||||
|
||||
### `keys list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--days <n>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys list
|
||||
```
|
||||
|
||||
### `keys rotate <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--grace-period <ms>`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute keys rotate <id>
|
||||
```
|
||||
|
||||
### `oauth`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute oauth
|
||||
```
|
||||
|
||||
### `oauth providers`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute oauth providers
|
||||
```
|
||||
|
||||
### `oauth start`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--no-browser`
|
||||
- `--import-from-system`
|
||||
- `--social <s>`
|
||||
- `--timeout <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute oauth start
|
||||
```
|
||||
|
||||
### `oauth status`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute oauth status
|
||||
```
|
||||
|
||||
### `oauth revoke`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--connection-id <id>`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute oauth revoke
|
||||
```
|
||||
150
skills/cli-mcp/SKILL.md
Normal file
150
skills/cli-mcp/SKILL.md
Normal file
@@ -0,0 +1,150 @@
|
||||
---
|
||||
name: cli-mcp
|
||||
description: Inspect the MCP server status, list registered tools and scopes, run tool invocations, and manage MCP audit logs from the CLI.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Inspect the MCP server status, list registered tools and scopes, run tool invocations, and manage MCP audit logs from the CLI.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `mcp`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp
|
||||
```
|
||||
|
||||
### `mcp status`
|
||||
|
||||
Show MCP server status
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp status
|
||||
```
|
||||
|
||||
### `mcp restart`
|
||||
|
||||
Restart the MCP server
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp restart
|
||||
```
|
||||
|
||||
### `mcp call <tool> [argsJson]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--args <json>`
|
||||
- `--args-file <path>`
|
||||
- `--stream`
|
||||
- `--scope <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp call <tool> [argsJson]
|
||||
```
|
||||
|
||||
### `mcp scopes`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--tool <name>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp scopes
|
||||
```
|
||||
|
||||
### `mcp tools`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp tools
|
||||
```
|
||||
|
||||
### `mcp list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--scope <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp list
|
||||
```
|
||||
|
||||
### `mcp info <name>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp info <name>
|
||||
```
|
||||
|
||||
### `mcp schema <name>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--io <kind>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp schema <name>
|
||||
```
|
||||
|
||||
### `mcp audit`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp audit
|
||||
```
|
||||
|
||||
### `mcp tail`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--follow`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp tail
|
||||
```
|
||||
|
||||
### `mcp stats`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute mcp stats
|
||||
```
|
||||
31
skills/cli-models/SKILL.md
Normal file
31
skills/cli-models/SKILL.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: cli-models
|
||||
description: Query available AI models, list model aliases, and browse the full model catalog from the CLI. Filter by provider, search by capability, and resolve model name variants.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Query available AI models, list model aliases, and browse the full model catalog from the CLI. Filter by provider, search by capability, and resolve model name variants.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `models [provider]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--search <query>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute models [provider]
|
||||
```
|
||||
385
skills/cli-plugins-skills/SKILL.md
Normal file
385
skills/cli-plugins-skills/SKILL.md
Normal file
@@ -0,0 +1,385 @@
|
||||
---
|
||||
name: cli-plugins-skills
|
||||
description: Manage Omni Skills (list, install, test, remove), plugins (create, configure), and persistent memory (search, add, clear) from the CLI.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage Omni Skills (list, install, test, remove), plugins (create, configure), and persistent memory (search, add, clear) from the CLI.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `memory`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory
|
||||
```
|
||||
|
||||
### `memory search <query>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--type <type>`
|
||||
- `--limit <n>`
|
||||
- `--api-key <key>`
|
||||
- `--token-budget <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory search <query>
|
||||
```
|
||||
|
||||
### `memory add`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--content <text>`
|
||||
- `--file <path>`
|
||||
- `--type <type>`
|
||||
- `--metadata <json>`
|
||||
- `--api-key <key>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory add
|
||||
```
|
||||
|
||||
### `memory clear`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--type <type>`
|
||||
- `--older-than <duration>`
|
||||
- `--api-key <key>`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory clear
|
||||
```
|
||||
|
||||
### `memory list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--type <type>`
|
||||
- `--limit <n>`
|
||||
- `--api-key <key>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory list
|
||||
```
|
||||
|
||||
### `memory get <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory get <id>
|
||||
```
|
||||
|
||||
### `memory delete <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory delete <id>
|
||||
```
|
||||
|
||||
### `memory health`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute memory health
|
||||
```
|
||||
|
||||
### `PLUGIN_NAME`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute PLUGIN_NAME
|
||||
```
|
||||
|
||||
### `plugin plugin`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin plugin
|
||||
```
|
||||
|
||||
### `plugin list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin list
|
||||
```
|
||||
|
||||
### `plugin install <name>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-y, --yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin install <name>
|
||||
```
|
||||
|
||||
### `plugin remove <name>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `-y, --yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin remove <name>
|
||||
```
|
||||
|
||||
### `plugin info <name>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin info <name>
|
||||
```
|
||||
|
||||
### `plugin search [query]`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin search [query]
|
||||
```
|
||||
|
||||
### `plugin update [name]`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin update [name]
|
||||
```
|
||||
|
||||
### `plugin scaffold <name>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute plugin scaffold <name>
|
||||
```
|
||||
|
||||
### `marketplace`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute marketplace
|
||||
```
|
||||
|
||||
### `skills search [query]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--category <c>`
|
||||
- `--tag <t>`
|
||||
- `--limit <n>`
|
||||
- `--sort <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills search [query]
|
||||
```
|
||||
|
||||
### `skills info <packageId>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills info <packageId>
|
||||
```
|
||||
|
||||
### `skills install <packageId>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--version <v>`
|
||||
- `--enable`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills install <packageId>
|
||||
```
|
||||
|
||||
### `skills categories`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills categories
|
||||
```
|
||||
|
||||
### `skills featured`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills featured
|
||||
```
|
||||
|
||||
### `skills skills`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills skills
|
||||
```
|
||||
|
||||
### `skills list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--type <type>`
|
||||
- `--enabled`
|
||||
- `--disabled`
|
||||
- `--api-key <key>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills list
|
||||
```
|
||||
|
||||
### `skills get <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills get <id>
|
||||
```
|
||||
|
||||
### `skills install`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--from-file <path>`
|
||||
- `--from-url <url>`
|
||||
- `--type <type>`
|
||||
- `--enable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills install
|
||||
```
|
||||
|
||||
### `skills enable <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills enable <id>
|
||||
```
|
||||
|
||||
### `skills disable <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills disable <id>
|
||||
```
|
||||
|
||||
### `skills delete <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills delete <id>
|
||||
```
|
||||
|
||||
### `skills execute <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--input <json>`
|
||||
- `--input-file <path>`
|
||||
- `--timeout <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills execute <id>
|
||||
```
|
||||
|
||||
### `skills executions`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--skill <id>`
|
||||
- `--limit <n>`
|
||||
- `--status <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills executions
|
||||
```
|
||||
|
||||
### `skills skillssh`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills skillssh
|
||||
```
|
||||
|
||||
### `skills list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills list
|
||||
```
|
||||
|
||||
### `skills install <url>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute skills install <url>
|
||||
```
|
||||
244
skills/cli-policy-audit/SKILL.md
Normal file
244
skills/cli-policy-audit/SKILL.md
Normal file
@@ -0,0 +1,244 @@
|
||||
---
|
||||
name: cli-policy-audit
|
||||
description: Inspect audit logs, manage access policies, view telemetry data, and review request history from the CLI. Filter by event type, user, or time range for compliance workflows.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Inspect audit logs, manage access policies, view telemetry data, and review request history from the CLI. Filter by event type, user, or time range for compliance workflows.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `audit`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit
|
||||
```
|
||||
|
||||
### `audit tail`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--follow`
|
||||
- `--limit <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit tail
|
||||
```
|
||||
|
||||
### `audit search <query>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--since <ts>`
|
||||
- `--until <ts>`
|
||||
- `--limit <n>`
|
||||
- `--actor <id>`
|
||||
- `--action <a>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit search <query>
|
||||
```
|
||||
|
||||
### `audit export <file>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--format <f>`
|
||||
- `--since <ts>`
|
||||
- `--until <ts>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit export <file>
|
||||
```
|
||||
|
||||
### `audit stats`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit stats
|
||||
```
|
||||
|
||||
### `audit get <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--source <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute audit get <id>
|
||||
```
|
||||
|
||||
### `logs`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--follow`
|
||||
- `--filter <level>`
|
||||
- `--lines <n>`
|
||||
- `--timeout <ms>`
|
||||
- `--base-url <url>`
|
||||
- `--request-id <id>`
|
||||
- `--api-key <key>`
|
||||
- `--combo <name>`
|
||||
- `--status <code>`
|
||||
- `--duration-min <ms>`
|
||||
- `--duration-max <ms>`
|
||||
- `--export <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute logs
|
||||
```
|
||||
|
||||
### `policy`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy
|
||||
```
|
||||
|
||||
### `policy list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--kind <k>`
|
||||
- `--scope <s>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy list
|
||||
```
|
||||
|
||||
### `policy get <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy get <id>
|
||||
```
|
||||
|
||||
### `policy create`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy create
|
||||
```
|
||||
|
||||
### `policy update <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy update <id>
|
||||
```
|
||||
|
||||
### `policy delete <id>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy delete <id>
|
||||
```
|
||||
|
||||
### `policy evaluate`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--resource <r>`
|
||||
- `--context <json>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy evaluate
|
||||
```
|
||||
|
||||
### `policy export <file>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy export <file>
|
||||
```
|
||||
|
||||
### `policy import <file>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--overwrite`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute policy import <file>
|
||||
```
|
||||
|
||||
### `telemetry`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute telemetry
|
||||
```
|
||||
|
||||
### `telemetry summary`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <p>`
|
||||
- `--compare-to <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute telemetry summary
|
||||
```
|
||||
|
||||
### `telemetry export`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--out <path>`
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute telemetry export
|
||||
```
|
||||
332
skills/cli-providers/SKILL.md
Normal file
332
skills/cli-providers/SKILL.md
Normal file
@@ -0,0 +1,332 @@
|
||||
---
|
||||
name: cli-providers
|
||||
description: "Manage provider connections from the CLI: list available/configured providers, add, test, test-all, validate, rotate API keys, and view per-provider metrics."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage provider connections from the CLI: list available/configured providers, add, test, test-all, validate, rotate API keys, and view per-provider metrics.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `provider-cmd provider [subcommand]`
|
||||
|
||||
Manage provider connections (use
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute provider-cmd provider [subcommand]
|
||||
```
|
||||
|
||||
### `providers`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers
|
||||
```
|
||||
|
||||
### `providers available`
|
||||
|
||||
Show available providers in the OmniRoute catalog
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
- `--search <query>`
|
||||
- `-q, --q <query>`
|
||||
- `--category <category>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers available
|
||||
```
|
||||
|
||||
### `providers list`
|
||||
|
||||
List configured provider connections
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers list
|
||||
```
|
||||
|
||||
### `providers test <idOrName>`
|
||||
|
||||
Test a configured provider connection
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers test <idOrName>
|
||||
```
|
||||
|
||||
### `providers test-all`
|
||||
|
||||
Test all active provider connections
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers test-all
|
||||
```
|
||||
|
||||
### `providers validate`
|
||||
|
||||
Validate local provider configuration without calling upstream
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers validate
|
||||
```
|
||||
|
||||
### `providers rotate <idOrName>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--new-key <key>`
|
||||
- `--from-env <VAR>`
|
||||
- `--oauth`
|
||||
- `--yes`
|
||||
- `--skip-test`
|
||||
- `--dry-run`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers rotate <idOrName>
|
||||
```
|
||||
|
||||
### `providers status`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <name>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers status
|
||||
```
|
||||
|
||||
### `providers metrics`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <id>`
|
||||
- `--connection-id <id>`
|
||||
- `--period <p>`
|
||||
- `--metric <m>`
|
||||
- `--sort-by <field>`
|
||||
- `--limit <n>`
|
||||
- `--watch`
|
||||
- `--compare <list>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers metrics
|
||||
```
|
||||
|
||||
### `providers metric <connectionId> <metric>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--period <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute providers metric <connectionId> <metric>
|
||||
```
|
||||
|
||||
### `test-provider test [provider] [model]`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--all-providers`
|
||||
- `--json`
|
||||
- `--latency`
|
||||
- `--repeat <n>`
|
||||
- `--compare <models>`
|
||||
- `--save <path>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute test-provider test [provider] [model]
|
||||
```
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-cli-providers/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — CLI Providers & Keys
|
||||
|
||||
Requires the `omniroute` CLI. See [CLI entry-point skill](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli/SKILL.md) for install + global flags.
|
||||
|
||||
## Provider catalog (available providers)
|
||||
|
||||
```bash
|
||||
omniroute providers available # Full OmniRoute provider catalog
|
||||
omniroute providers available --search openai # Filter by id, name, alias
|
||||
omniroute providers available --category api-key # Filter by category
|
||||
omniroute providers available --json # Machine-readable JSON
|
||||
```
|
||||
|
||||
Categories: `api-key`, `oauth`, `free`, `local`, `combo`.
|
||||
|
||||
## Configured provider connections
|
||||
|
||||
```bash
|
||||
omniroute providers list # Connections in your DB
|
||||
omniroute providers list --json
|
||||
```
|
||||
|
||||
## Testing connections
|
||||
|
||||
```bash
|
||||
omniroute providers test <id|name> # Test one configured connection
|
||||
omniroute providers test-all # Test every active connection (TUI progress)
|
||||
omniroute providers validate # Local-only structural validation (no HTTP)
|
||||
```
|
||||
|
||||
`test-all` opens an interactive TUI that shows live pass/fail per connection. Use `--json` to get a machine-readable result:
|
||||
|
||||
```bash
|
||||
omniroute providers test-all --json
|
||||
```
|
||||
|
||||
## API key management (OmniRoute keys)
|
||||
|
||||
These manage the OmniRoute API keys issued under **API Manager** — not provider credentials.
|
||||
|
||||
```bash
|
||||
omniroute keys list # List all OmniRoute API keys
|
||||
omniroute keys add <provider> [apiKey] # Add an API key for a provider
|
||||
omniroute keys remove <provider> # Remove an API key
|
||||
omniroute keys regenerate <id> # Regenerate (rotate) a key
|
||||
omniroute keys revoke <id> # Revoke a key (disables it)
|
||||
omniroute keys reveal <id> # Show the full key value
|
||||
omniroute keys usage <id> # Show usage stats for a key
|
||||
|
||||
omniroute keys rotate <id> # Rotate + revoke old key atomically
|
||||
omniroute keys expiration list # List key expiration times
|
||||
```
|
||||
|
||||
### Key policies
|
||||
|
||||
```bash
|
||||
omniroute keys policy show <id> # Show rate-limit / permission policy
|
||||
omniroute keys policy set <id> \
|
||||
--rate-limit 100 \
|
||||
--rate-window minute \
|
||||
--permissions chat,models # Set policy on a key
|
||||
```
|
||||
|
||||
## Models
|
||||
|
||||
```bash
|
||||
omniroute models # List all models (all providers)
|
||||
omniroute models openai # Filter by provider
|
||||
omniroute models --search gpt # Search by name
|
||||
omniroute models --json # JSON output
|
||||
```
|
||||
|
||||
## OAuth providers
|
||||
|
||||
```bash
|
||||
omniroute oauth list # List OAuth-configured providers
|
||||
omniroute oauth login <provider> # Start browser-based OAuth flow
|
||||
omniroute oauth logout <provider> # Revoke OAuth token
|
||||
omniroute oauth status <provider> # Show token state + expiry
|
||||
omniroute oauth refresh <provider> # Force token refresh
|
||||
```
|
||||
|
||||
For OAuth providers (Gemini, Windsurf, Antigravity, etc.) the `login` command opens the OmniRoute dashboard OAuth flow in your browser.
|
||||
|
||||
## Provider nodes (multi-account routing)
|
||||
|
||||
Provider nodes let you attach multiple API keys / accounts to one logical provider for round-robin or failover.
|
||||
|
||||
```bash
|
||||
omniroute nodes list <provider> # List nodes for a provider
|
||||
omniroute nodes add <provider> --api-key <key> # Add a node
|
||||
omniroute nodes remove <provider> <nodeId> # Remove a node
|
||||
omniroute nodes test <provider> <nodeId> # Test one node
|
||||
```
|
||||
|
||||
## Routing combos (CLI)
|
||||
|
||||
Create and manage routing combos from the terminal:
|
||||
|
||||
```bash
|
||||
omniroute combo list # List all combos
|
||||
omniroute combo create <name> \
|
||||
--strategy priority \
|
||||
--targets anthropic/claude-opus-4-7,openai/gpt-4o # Create combo
|
||||
omniroute combo switch <name> # Activate a combo as default
|
||||
omniroute combo delete <name> # Delete a combo
|
||||
omniroute combo suggest --task "code review" # Ask OmniRoute to recommend a combo
|
||||
```
|
||||
|
||||
For the full REST API for combos see [omniroute-routing skill](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-routing/SKILL.md).
|
||||
|
||||
## Quota & usage
|
||||
|
||||
```bash
|
||||
omniroute quota # Provider quota usage + reset times
|
||||
omniroute usage # Request + token usage summary
|
||||
omniroute cost # Cost breakdown (by provider/model)
|
||||
```
|
||||
|
||||
## Compression (CLI)
|
||||
|
||||
```bash
|
||||
omniroute compression status # Current compression mode + savings stats
|
||||
omniroute compression set --mode rtk # Enable RTK compression
|
||||
omniroute compression set --mode stacked # Enable stacked (RTK + Caveman)
|
||||
omniroute compression set --mode off # Disable compression
|
||||
omniroute compression preview --mode rtk --text "..." # Preview savings for sample text
|
||||
```
|
||||
|
||||
## Health
|
||||
|
||||
```bash
|
||||
omniroute health # Detailed health: circuit breakers, cache, memory
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
- `providers test <id>` fails with 401 → API key stored for that provider is wrong; use `keys remove` + `keys add` to reset it
|
||||
- `oauth login` opens but doesn't complete → the OAuth token endpoint may be firewalled; use API key auth instead
|
||||
- `combo create` fails with `strategy unknown` → use one of: `priority`, `weighted`, `round-robin`, `fill-first`, `least-used`, `cost-optimized`, `auto`, `random`, `strict-random`, `p2c`, `reset-aware`, `lkgp`, `context-optimized`, `context-relay`
|
||||
<!-- skill:custom-end -->
|
||||
158
skills/cli-resilience/SKILL.md
Normal file
158
skills/cli-resilience/SKILL.md
Normal file
@@ -0,0 +1,158 @@
|
||||
---
|
||||
name: cli-resilience
|
||||
description: Inspect and manage circuit-breaker states, connection cooldowns, quota limits, and backoff levels from the CLI. Reset stuck providers and configure resilience thresholds.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Inspect and manage circuit-breaker states, connection cooldowns, quota limits, and backoff levels from the CLI. Reset stuck providers and configure resilience thresholds.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `quota`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <id>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute quota
|
||||
```
|
||||
|
||||
### `resilience`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience
|
||||
```
|
||||
|
||||
### `resilience status`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience status
|
||||
```
|
||||
|
||||
### `resilience breakers`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience breakers
|
||||
```
|
||||
|
||||
### `resilience cooldowns`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
- `--connection-id <id>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience cooldowns
|
||||
```
|
||||
|
||||
### `resilience lockouts`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--provider <p>`
|
||||
- `--model <m>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience lockouts
|
||||
```
|
||||
|
||||
### `resilience reset`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--connection-id <id>`
|
||||
- `--model <m>`
|
||||
- `--all-cooldowns`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience reset
|
||||
```
|
||||
|
||||
### `resilience profile`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience profile
|
||||
```
|
||||
|
||||
### `resilience show`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience show
|
||||
```
|
||||
|
||||
### `resilience set`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience set
|
||||
```
|
||||
|
||||
### `resilience config`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience config
|
||||
```
|
||||
|
||||
### `resilience show`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience show
|
||||
```
|
||||
|
||||
### `resilience set`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--threshold <n>`
|
||||
- `--reset-timeout <ms>`
|
||||
- `--base-cooldown <ms>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute resilience set
|
||||
```
|
||||
91
skills/cli-routing/SKILL.md
Normal file
91
skills/cli-routing/SKILL.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: cli-routing
|
||||
description: Create, list, update, and delete routing combos from the CLI. Test routing strategies, inspect combo metrics, and configure fallback chains interactively.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create, list, update, and delete routing combos from the CLI. Test routing strategies, inspect combo metrics, and configure fallback chains interactively.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `suggest`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--max-cost <usd>`
|
||||
- `--max-latency-ms <ms>`
|
||||
- `--weights <json>`
|
||||
- `--top <n>`
|
||||
- `--explain`
|
||||
- `--switch`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute suggest
|
||||
```
|
||||
|
||||
### `combo combo`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute combo combo
|
||||
```
|
||||
|
||||
### `combo list`
|
||||
|
||||
List configured routing combos
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute combo list
|
||||
```
|
||||
|
||||
### `combo switch <name>`
|
||||
|
||||
Activate a routing combo
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute combo switch <name>
|
||||
```
|
||||
|
||||
### `combo create <name>`
|
||||
|
||||
Create a new routing combo
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute combo create <name>
|
||||
```
|
||||
|
||||
### `combo delete <name>`
|
||||
|
||||
Delete a routing combo
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute combo delete <name>
|
||||
```
|
||||
312
skills/cli-serve/SKILL.md
Normal file
312
skills/cli-serve/SKILL.md
Normal file
@@ -0,0 +1,312 @@
|
||||
---
|
||||
name: cli-serve
|
||||
description: Start, stop, and restart the OmniRoute server from the CLI. Manage daemon mode, port configuration, auto-recovery, system tray integration, and the dashboard open shortcut.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Start, stop, and restart the OmniRoute server from the CLI. Manage daemon mode, port configuration, auto-recovery, system tray integration, and the dashboard open shortcut.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `dashboard`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--url`
|
||||
- `--port <port>`
|
||||
- `--tui`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute dashboard
|
||||
```
|
||||
|
||||
### `restart`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--port <port>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute restart
|
||||
```
|
||||
|
||||
### `serve`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--port <port>`
|
||||
- `--no-open`
|
||||
- `--daemon`
|
||||
- `--log`
|
||||
- `--no-recovery`
|
||||
- `--max-restarts <n>`
|
||||
- `--tray`
|
||||
- `--no-tray`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute serve
|
||||
```
|
||||
|
||||
### `stop`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute stop
|
||||
```
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Aggregated from: omniroute-cli (setup + index), omniroute-cli-admin (lifecycle/setup/backup/tunnel) -->
|
||||
|
||||
## Setup
|
||||
|
||||
The `omniroute` binary ships with the OmniRoute server. It is both the server launcher and a full management CLI with 250+ commands across 39 groups.
|
||||
|
||||
### Install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # npm registry
|
||||
# or: use the binary bundled with the desktop app
|
||||
```
|
||||
|
||||
Requires Node.js ≥20.20.2, ≥22.22.2, or ≥24.
|
||||
|
||||
Verify:
|
||||
|
||||
```bash
|
||||
omniroute --version # prints installed version
|
||||
omniroute --help # full command tree
|
||||
```
|
||||
|
||||
### Connection
|
||||
|
||||
Every CLI command that talks to the server reads two values:
|
||||
|
||||
| Source | Variable / Flag |
|
||||
| -------- | ------------------------------------ |
|
||||
| Base URL | `OMNIROUTE_BASE_URL` or `--base-url` |
|
||||
| API key | `OMNIROUTE_API_KEY` or `--api-key` |
|
||||
|
||||
Default base URL: `http://localhost:20128`
|
||||
|
||||
```bash
|
||||
export OMNIROUTE_BASE_URL="http://localhost:20128"
|
||||
export OMNIROUTE_API_KEY="sk-..." # from Dashboard → API Manager
|
||||
```
|
||||
|
||||
For a remote server:
|
||||
|
||||
```bash
|
||||
export OMNIROUTE_BASE_URL="https://your-server.com"
|
||||
```
|
||||
|
||||
### Global flags
|
||||
|
||||
| Flag | Description |
|
||||
| ------------------- | -------------------------------------------------------- |
|
||||
| `--base-url <url>` | Override server URL for this invocation |
|
||||
| `--api-key <key>` | Override API key for this invocation |
|
||||
| `--output <format>` | Output format: `table` (default), `json`, `jsonl`, `csv` |
|
||||
| `--json` | Shorthand for `--output json` |
|
||||
| `--non-interactive` | Disable prompts — for CI / shell scripts |
|
||||
| `--no-open` | Don't auto-open the browser on start |
|
||||
| `--port <n>` | Override default port 20128 |
|
||||
| `--help`, `-h` | Show help for the current command |
|
||||
| `--version`, `-v` | Print the installed version |
|
||||
|
||||
### Output formats
|
||||
|
||||
All listing commands support `--output`:
|
||||
|
||||
```bash
|
||||
omniroute combo list # human-readable table
|
||||
omniroute combo list --output json # JSON array
|
||||
omniroute combo list --output jsonl # one JSON object per line
|
||||
omniroute combo list --output csv # CSV with header row
|
||||
```
|
||||
|
||||
### Quick start: one-shot server + provider setup
|
||||
|
||||
```bash
|
||||
# 1. Start server
|
||||
omniroute
|
||||
|
||||
# 2. (First run) interactive setup wizard
|
||||
omniroute setup
|
||||
|
||||
# 3. Verify everything is healthy
|
||||
omniroute doctor
|
||||
```
|
||||
|
||||
### CLI capability skills
|
||||
|
||||
| Capability | Skill |
|
||||
| ------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
||||
| Server admin + backup | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-admin/SKILL.md |
|
||||
| Provider & key management | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-providers/SKILL.md |
|
||||
| Cloud agents (Codex / Devin / Jules) | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-cloud/SKILL.md |
|
||||
| Evals & benchmarking | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-eval/SKILL.md |
|
||||
|
||||
### Errors
|
||||
|
||||
- `Connection refused` → server not running; run `omniroute` or `omniroute serve`
|
||||
- `401 Unauthorized` → wrong or missing API key
|
||||
- `command not found: omniroute` → not in PATH; check `npm root -g` or re-install
|
||||
- `doctor` reports SQLite incompatible → `npm rebuild better-sqlite3` in the app directory
|
||||
|
||||
## Admin lifecycle
|
||||
|
||||
Requires the `omniroute` CLI. See [CLI entry-point skill](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli/SKILL.md) for install + global flags.
|
||||
|
||||
### Server lifecycle
|
||||
|
||||
```bash
|
||||
omniroute # Start server (default port 20128)
|
||||
omniroute serve # Explicit alias
|
||||
omniroute --port 3000 # Override port
|
||||
omniroute --no-open # Don't auto-open browser
|
||||
omniroute --mcp # Start as MCP server (stdio transport)
|
||||
|
||||
omniroute stop # Stop the running server
|
||||
omniroute restart # Restart the server
|
||||
|
||||
omniroute dashboard # Open dashboard in browser
|
||||
omniroute open # Alias for dashboard
|
||||
omniroute status # Runtime status (uptime, requests, providers)
|
||||
```
|
||||
|
||||
### Setup & provisioning
|
||||
|
||||
#### Interactive wizard
|
||||
|
||||
```bash
|
||||
omniroute setup # Step-by-step interactive setup
|
||||
```
|
||||
|
||||
#### Non-interactive (CI / Docker)
|
||||
|
||||
```bash
|
||||
omniroute setup --non-interactive \
|
||||
--password 'admin-password' \
|
||||
--add-provider \
|
||||
--provider openai \
|
||||
--api-key 'sk-...' \
|
||||
--test-provider
|
||||
```
|
||||
|
||||
Environment variables for non-interactive setup:
|
||||
|
||||
| Variable | Purpose |
|
||||
| ----------------------------- | -------------------------------------------- |
|
||||
| `OMNIROUTE_SETUP_PASSWORD` | Admin password (≥8 chars) |
|
||||
| `OMNIROUTE_PROVIDER` | Provider id (e.g. `openai`, `anthropic`) |
|
||||
| `OMNIROUTE_PROVIDER_NAME` | Display name for the connection |
|
||||
| `OMNIROUTE_PROVIDER_BASE_URL` | Optional OpenAI-compatible base URL override |
|
||||
| `OMNIROUTE_API_KEY` | Provider API key |
|
||||
| `OMNIROUTE_DEFAULT_MODEL` | Optional default model |
|
||||
| `DATA_DIR` | Override OmniRoute data directory |
|
||||
|
||||
### Diagnostics
|
||||
|
||||
```bash
|
||||
omniroute doctor # Full health check
|
||||
omniroute doctor --json # Machine-readable JSON
|
||||
omniroute doctor --no-liveness # Skip HTTP health probe
|
||||
omniroute doctor --host 0.0.0.0 # Override liveness host
|
||||
omniroute doctor --liveness-url <url> # Full URL override
|
||||
```
|
||||
|
||||
Checks performed: Config, Database, Storage/encryption, Port, Node runtime, Native binary (better-sqlite3), Memory, Server liveness.
|
||||
|
||||
Exit code is non-zero if any check fails — useful in CI:
|
||||
|
||||
```bash
|
||||
omniroute doctor --json | jq '.checks[] | select(.status=="fail")'
|
||||
```
|
||||
|
||||
### Backup & restore
|
||||
|
||||
```bash
|
||||
omniroute backup # Snapshot config + SQLite DB to ~/.omniroute/backups/
|
||||
omniroute restore # Restore from a previous snapshot (interactive picker)
|
||||
```
|
||||
|
||||
### Autostart (system tray / startup)
|
||||
|
||||
```bash
|
||||
omniroute autostart enable # Register OmniRoute as a system startup item
|
||||
omniroute autostart disable # Remove startup registration
|
||||
omniroute autostart status # Show current autostart state
|
||||
```
|
||||
|
||||
On Linux: creates a **systemd user service** (`~/.config/systemd/user/omniroute.service`) and enables **linger** so the service can start after reboot without a graphical login; on desktop sessions it also adds an XDG autostart entry with `--tray`. On macOS: LaunchAgent plist. On Windows: registry startup entry.
|
||||
|
||||
### Tunnels (public URL)
|
||||
|
||||
Expose a local OmniRoute instance via a secure tunnel:
|
||||
|
||||
```bash
|
||||
omniroute tunnel list # List active tunnels
|
||||
omniroute tunnel create cloudflare # Start a Cloudflare Tunnel (free)
|
||||
omniroute tunnel create tailscale # Start a Tailscale funnel
|
||||
omniroute tunnel create ngrok # Start an ngrok tunnel
|
||||
omniroute tunnel stop <id> # Stop a running tunnel
|
||||
```
|
||||
|
||||
The tunnel URL is printed and can be used as `OMNIROUTE_BASE_URL` from remote machines.
|
||||
|
||||
### Config & environment
|
||||
|
||||
```bash
|
||||
omniroute config show # Display current effective configuration
|
||||
omniroute env show # List all OmniRoute environment variables
|
||||
omniroute env get <KEY> # Get a single env var value
|
||||
omniroute env set <KEY> <value> # Set an env var (temporary — until restart)
|
||||
```
|
||||
|
||||
### Recovery
|
||||
|
||||
```bash
|
||||
omniroute reset-password # Reset the admin password interactively
|
||||
omniroute reset-encrypted-columns # Dry-run: show encrypted credential columns
|
||||
omniroute reset-encrypted-columns --force # Null out encrypted credentials in SQLite
|
||||
```
|
||||
|
||||
Use `reset-encrypted-columns --force` only if `STORAGE_ENCRYPTION_KEY` was lost and you need to re-enter all provider API keys.
|
||||
|
||||
### Logs
|
||||
|
||||
```bash
|
||||
omniroute logs # Stream live request logs
|
||||
omniroute logs --json # JSON log entries
|
||||
omniroute logs --search <term> # Filter by term
|
||||
omniroute logs --follow # Tail mode (keep streaming)
|
||||
```
|
||||
|
||||
### Update
|
||||
|
||||
```bash
|
||||
omniroute update # Check for a newer version and prompt to update
|
||||
```
|
||||
|
||||
### Errors
|
||||
|
||||
- `doctor` shows `STORAGE_ENCRYPTION_KEY missing` → set the key in `.env` or run `reset-encrypted-columns --force` to wipe and re-enter credentials
|
||||
- `doctor` reports native binary fail → `npm rebuild better-sqlite3` in the OmniRoute app directory
|
||||
- `tunnel create cloudflare` hangs → ensure `cloudflared` is installed: `brew install cloudflare/cloudflare/cloudflared`
|
||||
<!-- skill:custom-end -->
|
||||
261
skills/cli-setup/SKILL.md
Normal file
261
skills/cli-setup/SKILL.md
Normal file
@@ -0,0 +1,261 @@
|
||||
---
|
||||
name: cli-setup
|
||||
description: Run initial setup, configure global CLI settings, manage environment variables, check for updates, and configure autostart via the CLI setup and config commands.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Run initial setup, configure global CLI settings, manage environment variables, check for updates, and configure autostart via the CLI setup and config commands.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `autostart`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart
|
||||
```
|
||||
|
||||
### `autostart enable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart enable
|
||||
```
|
||||
|
||||
### `autostart disable`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart disable
|
||||
```
|
||||
|
||||
### `autostart status`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute autostart status
|
||||
```
|
||||
|
||||
### `config`
|
||||
|
||||
Show or update CLI tool configuration
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config
|
||||
```
|
||||
|
||||
### `config list`
|
||||
|
||||
List all CLI tools and config status
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config list
|
||||
```
|
||||
|
||||
### `config get <tool>`
|
||||
|
||||
Show current config for a tool
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config get <tool>
|
||||
```
|
||||
|
||||
### `config set <tool>`
|
||||
|
||||
Write config for a tool
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--non-interactive`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config set <tool>
|
||||
```
|
||||
|
||||
### `config validate <tool>`
|
||||
|
||||
Validate config format without writing
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config validate <tool>
|
||||
```
|
||||
|
||||
### `config opencode`
|
||||
|
||||
Generate OpenCode config (alias for
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--model <model>`
|
||||
- `--non-interactive`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config opencode
|
||||
```
|
||||
|
||||
### `config lang`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config lang
|
||||
```
|
||||
|
||||
### `config get`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config get
|
||||
```
|
||||
|
||||
### `config set <code>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--force`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config set <code>
|
||||
```
|
||||
|
||||
### `config list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute config list
|
||||
```
|
||||
|
||||
### `env`
|
||||
|
||||
Show and manage environment variables
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env
|
||||
```
|
||||
|
||||
### `env show`
|
||||
|
||||
Show current environment variables
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env show
|
||||
```
|
||||
|
||||
### `env get <key>`
|
||||
|
||||
Get a single environment variable
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env get <key>
|
||||
```
|
||||
|
||||
### `env set <key> <value>`
|
||||
|
||||
Set an environment variable (current session only)
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute env set <key> <value>
|
||||
```
|
||||
|
||||
### `setup`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--password <value>`
|
||||
- `--add-provider`
|
||||
- `--provider <id>`
|
||||
- `--provider-name <name>`
|
||||
- `--api-key <value>`
|
||||
- `--default-model <model>`
|
||||
- `--provider-base-url <url>`
|
||||
- `--test-provider`
|
||||
- `--non-interactive`
|
||||
- `--list`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute setup
|
||||
```
|
||||
|
||||
### `update`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--check`
|
||||
- `--apply`
|
||||
- `--changelog`
|
||||
- `--dry-run`
|
||||
- `--no-backup`
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute update
|
||||
```
|
||||
106
skills/cli-tunnel/SKILL.md
Normal file
106
skills/cli-tunnel/SKILL.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: cli-tunnel
|
||||
description: Start and stop tunnel connections (ngrok, Cloudflare, custom) from the CLI. Inspect active tunnel URLs, configure authentication, and test external reachability.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Start and stop tunnel connections (ngrok, Cloudflare, custom) from the CLI. Inspect active tunnel URLs, configure authentication, and test external reachability.
|
||||
|
||||
## Quick install
|
||||
|
||||
```bash
|
||||
npm install -g omniroute # or: npx omniroute
|
||||
omniroute --version
|
||||
```
|
||||
|
||||
## Subcommands
|
||||
|
||||
### `tunnel`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel
|
||||
```
|
||||
|
||||
### `tunnel list`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel list
|
||||
```
|
||||
|
||||
### `tunnel create [type]`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel create [type]
|
||||
```
|
||||
|
||||
### `tunnel stop <type>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel stop <type>
|
||||
```
|
||||
|
||||
### `tunnel status <type>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel status <type>
|
||||
```
|
||||
|
||||
### `tunnel logs <type>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--tail <n>`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel logs <type>
|
||||
```
|
||||
|
||||
### `tunnel info <type>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--json`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel info <type>
|
||||
```
|
||||
|
||||
### `tunnel rotate <type>`
|
||||
|
||||
**Flags:**
|
||||
|
||||
- `--yes`
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
omniroute tunnel rotate <type>
|
||||
```
|
||||
91
skills/omni-agents-a2a/SKILL.md
Normal file
91
skills/omni-agents-a2a/SKILL.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: omni-agents-a2a
|
||||
description: "Interact with OmniRoute via JSON-RPC 2.0 agent-to-agent protocol. 6 built-in A2A skills: smart-routing, quota-management, provider-discovery, cost-analysis, health-report, list-capabilities."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Interact with OmniRoute via JSON-RPC 2.0 agent-to-agent protocol. 6 built-in A2A skills: smart-routing, quota-management, provider-discovery, cost-analysis, health-report, list-capabilities.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-a2a/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — A2A Protocol
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
OmniRoute publishes an Agent Card at `/.well-known/agent.json` and accepts
|
||||
JSON-RPC 2.0 calls at `/a2a`.
|
||||
|
||||
## Discovery
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/.well-known/agent.json
|
||||
```
|
||||
|
||||
Returns Agent Card with skills, endpoints, auth scheme.
|
||||
|
||||
## Available skills
|
||||
|
||||
| Skill | Purpose |
|
||||
| -------------------- | ----------------------------------------------------------------------------------- |
|
||||
| `smart-routing` | Given a prompt, recommends best provider/model combo |
|
||||
| `quota-management` | Reports quota balance for given provider/account |
|
||||
| `provider-discovery` | Lists providers matching capability filters (vision, JSON mode, tools, max-context) |
|
||||
| `cost-analysis` | Estimates cost for a given request shape |
|
||||
| `health-report` | Returns system health (circuit states, latencies, errors) |
|
||||
|
||||
## Call example (JSON-RPC 2.0)
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/a2a \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "tasks/send",
|
||||
"params": {
|
||||
"skillId": "smart-routing",
|
||||
"input": { "prompt_length": 4000, "tools": true, "vision": false }
|
||||
},
|
||||
"id": 1
|
||||
}'
|
||||
```
|
||||
|
||||
## Response shape
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"taskId": "...",
|
||||
"status": "completed",
|
||||
"output": { "recommended_combo": "...", "reasoning": "..." }
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
- `-32600` → invalid request (bad JSON-RPC envelope)
|
||||
- `-32601` → method not found (check `method` field)
|
||||
- `-32602` → invalid params (check `skillId` against Agent Card)
|
||||
- `401` → missing/invalid `OMNIROUTE_KEY`
|
||||
|
||||
## Reference
|
||||
|
||||
Full docs: https://github.com/diegosouzapw/OmniRoute/blob/main/docs/frameworks/A2A-SERVER.md
|
||||
<!-- skill:custom-end -->
|
||||
48
skills/omni-api-keys/SKILL.md
Normal file
48
skills/omni-api-keys/SKILL.md
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: omni-api-keys
|
||||
description: Create, list, rotate, and revoke OmniRoute API keys. Control per-key scopes, spending limits, and expiration. Keys gate access to all proxy and management endpoints.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create, list, rotate, and revoke OmniRoute API keys. Control per-key scopes, spending limits, and expiration. Keys gate access to all proxy and management endpoints.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/keys
|
||||
|
||||
List API keys
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/keys \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/keys
|
||||
|
||||
Create API key
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/keys \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/keys/{id}
|
||||
|
||||
Delete API key
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/keys/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
117
skills/omni-auth/SKILL.md
Normal file
117
skills/omni-auth/SKILL.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
name: omni-auth
|
||||
description: Manage API key authentication and session tokens. Start here to authenticate requests via Bearer token, obtain session cookies, and configure login requirements for the OmniRoute API.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage API key authentication and session tokens. Start here to authenticate requests via Bearer token, obtain session cookies, and configure login requirements for the OmniRoute API.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/auth/login
|
||||
|
||||
Authenticate user
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/auth/login \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/auth/logout
|
||||
|
||||
Log out
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/auth/logout \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute
|
||||
|
||||
Local/remote AI gateway exposing OpenAI-compatible REST. One key, 207+ providers,
|
||||
auto-fallback, RTK token saver, MCP server, A2A agents.
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
export OMNIROUTE_URL="http://localhost:20128" # or VPS / tunnel URL
|
||||
export OMNIROUTE_KEY="sk-..." # from Dashboard → API Keys
|
||||
```
|
||||
|
||||
All requests: `${OMNIROUTE_URL}/v1/...` with `Authorization: Bearer ${OMNIROUTE_KEY}`.
|
||||
|
||||
Verify: `curl $OMNIROUTE_URL/api/health` → `{"ok":true}`
|
||||
|
||||
## Discover models
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models # chat/LLM (default)
|
||||
curl $OMNIROUTE_URL/v1/models/image # image-gen
|
||||
curl $OMNIROUTE_URL/v1/models/tts # text-to-speech
|
||||
curl $OMNIROUTE_URL/v1/models/embedding # embeddings
|
||||
curl $OMNIROUTE_URL/v1/models/web # web search + fetch
|
||||
curl $OMNIROUTE_URL/v1/models/stt # speech-to-text
|
||||
```
|
||||
|
||||
Use `data[].id` as `model` field in requests. Combos appear with `owned_by:"combo"`.
|
||||
|
||||
## Capability skills
|
||||
|
||||
| Capability | Raw URL |
|
||||
| --------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Chat / code-gen | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-chat/SKILL.md |
|
||||
| Image generation | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-image/SKILL.md |
|
||||
| Text-to-speech | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-tts/SKILL.md |
|
||||
| Speech-to-text | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-stt/SKILL.md |
|
||||
| Embeddings | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-embeddings/SKILL.md |
|
||||
| Web search | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-web-search/SKILL.md |
|
||||
| Web fetch | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-web-fetch/SKILL.md |
|
||||
| MCP server (37 tools) | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-mcp/SKILL.md |
|
||||
| A2A protocol | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-a2a/SKILL.md |
|
||||
| Routing & combos | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-routing/SKILL.md |
|
||||
| Token compression | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-compression/SKILL.md |
|
||||
| Monitoring & health | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-monitoring/SKILL.md |
|
||||
|
||||
## CLI skills (omniroute binary)
|
||||
|
||||
| Capability | Raw URL |
|
||||
| ---------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| CLI entry point | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli/SKILL.md |
|
||||
| CLI admin & lifecycle | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-admin/SKILL.md |
|
||||
| CLI providers & keys | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-providers/SKILL.md |
|
||||
| CLI cloud agents | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-cloud/SKILL.md |
|
||||
| CLI evals & benchmarks | https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute-cli-eval/SKILL.md |
|
||||
|
||||
## Errors
|
||||
|
||||
- `401` → set/refresh `OMNIROUTE_KEY` (Dashboard → API Keys)
|
||||
- `400 Invalid model format` → check `model` exists in `/v1/models/<kind>`
|
||||
- `503 Provider circuit open` → upstream provider down; retry after `Retry-After` seconds
|
||||
- `429` → rate limited; honor `Retry-After`
|
||||
|
||||
## Differentiators vs OpenAI direct
|
||||
|
||||
- **Auto-fallback** combos (14 strategies): never stop coding even if a provider rate-limits
|
||||
- **RTK token saver**: tool_result compressed via 47 specialized filters (git-diff, test-jest, terraform-plan, docker-logs…) — 20-40% token reduction
|
||||
- **Caveman mode**: optional terse system prompt injection (LITE/FULL/ULTRA) — 15-25% completion reduction
|
||||
- **MCP + A2A** servers built-in (this is the only AI router that exposes both protocols)
|
||||
- **Memory** with FTS5 + Qdrant for persistent agent context
|
||||
- **Guardrails** for PII masking, prompt injection detection, vision policies
|
||||
<!-- skill:custom-end -->
|
||||
39
skills/omni-budget/SKILL.md
Normal file
39
skills/omni-budget/SKILL.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: omni-budget
|
||||
description: Configure spending limits, token quotas, and rate-limit policies per API key or globally. Inspect current consumption and enforce cost controls across providers.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Configure spending limits, token quotas, and rate-limit policies per API key or globally. Inspect current consumption and enforce cost controls across providers.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/rate-limit
|
||||
|
||||
Get rate limit configuration
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/rate-limit \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/rate-limit
|
||||
|
||||
Update rate limit configuration
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/rate-limit \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
57
skills/omni-cache/SKILL.md
Normal file
57
skills/omni-cache/SKILL.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: omni-cache
|
||||
description: Manage the LLM response cache. View cache statistics, clear entries, configure TTL policies, and control semantic-similarity caching thresholds.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage the LLM response cache. View cache statistics, clear entries, configure TTL policies, and control semantic-similarity caching thresholds.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/cache
|
||||
|
||||
Get cache statistics
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cache \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### DELETE /api/cache
|
||||
|
||||
Clear all caches
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cache \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cache/stats
|
||||
|
||||
Get detailed cache statistics
|
||||
|
||||
Returns detailed statistics for all cache layers.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cache/stats \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### DELETE /api/cache/stats
|
||||
|
||||
Clear cache statistics
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cache/stats \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
320
skills/omni-cli-tools/SKILL.md
Normal file
320
skills/omni-cli-tools/SKILL.md
Normal file
@@ -0,0 +1,320 @@
|
||||
---
|
||||
name: omni-cli-tools
|
||||
description: "Manage CLI tool integrations exposed via the API. List, configure, and invoke CLI tool plugins that extend OmniRoute's automation surface."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage CLI tool integrations exposed via the API. List, configure, and invoke CLI tool plugins that extend OmniRoute's automation surface.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/cli-tools/backups
|
||||
|
||||
List CLI tool backups
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/backups \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/backups
|
||||
|
||||
Create CLI tool backup
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/backups \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/runtime/{toolId}
|
||||
|
||||
Get runtime status for a CLI tool
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/runtime/{toolId} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/guide-settings/{toolId}
|
||||
|
||||
Get guide settings for a tool
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/guide-settings/{toolId} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/antigravity-mitm
|
||||
|
||||
Get Antigravity MITM proxy settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/antigravity-mitm \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/antigravity-mitm
|
||||
|
||||
Update Antigravity MITM proxy settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/antigravity-mitm \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/antigravity-mitm
|
||||
|
||||
Reset Antigravity MITM proxy settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/antigravity-mitm \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/antigravity-mitm/alias
|
||||
|
||||
Get Antigravity MITM alias configuration
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/antigravity-mitm/alias \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/cli-tools/antigravity-mitm/alias
|
||||
|
||||
Update Antigravity MITM alias configuration
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/cli-tools/antigravity-mitm/alias \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/claude-settings
|
||||
|
||||
Get Claude CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/claude-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/claude-settings
|
||||
|
||||
Apply Claude CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/claude-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/claude-settings
|
||||
|
||||
Reset Claude CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/claude-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/cline-settings
|
||||
|
||||
Get Cline CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/cline-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/cline-settings
|
||||
|
||||
Apply Cline CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/cline-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/cline-settings
|
||||
|
||||
Reset Cline CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/cline-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/codex-profiles
|
||||
|
||||
Get Codex profiles
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/codex-profiles \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/codex-profiles
|
||||
|
||||
Create Codex profile
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/codex-profiles \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### PUT /api/cli-tools/codex-profiles
|
||||
|
||||
Update Codex profile
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/cli-tools/codex-profiles \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/codex-profiles
|
||||
|
||||
Delete Codex profile
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/codex-profiles \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/codex-settings
|
||||
|
||||
Get Codex CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/codex-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/codex-settings
|
||||
|
||||
Apply Codex CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/codex-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/codex-settings
|
||||
|
||||
Reset Codex CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/codex-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/droid-settings
|
||||
|
||||
Get Droid CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/droid-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/droid-settings
|
||||
|
||||
Apply Droid CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/droid-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/droid-settings
|
||||
|
||||
Reset Droid CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/droid-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/kilo-settings
|
||||
|
||||
Get Kilo CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/kilo-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/kilo-settings
|
||||
|
||||
Apply Kilo CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/kilo-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/kilo-settings
|
||||
|
||||
Reset Kilo CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/kilo-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/cli-tools/openclaw-settings
|
||||
|
||||
Get OpenClaw CLI settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cli-tools/openclaw-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/cli-tools/openclaw-settings
|
||||
|
||||
Apply OpenClaw CLI settings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cli-tools/openclaw-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/cli-tools/openclaw-settings
|
||||
|
||||
Reset OpenClaw CLI settings
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/cli-tools/openclaw-settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
241
skills/omni-combos-routing/SKILL.md
Normal file
241
skills/omni-combos-routing/SKILL.md
Normal file
@@ -0,0 +1,241 @@
|
||||
---
|
||||
name: omni-combos-routing
|
||||
description: Create and manage routing combos with 14 strategies (priority, weighted, round-robin, Auto-combo, etc.). Configure fallback chains, test routing outcomes, and retrieve combo metrics.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create and manage routing combos with 14 strategies (priority, weighted, round-robin, Auto-combo, etc.). Configure fallback chains, test routing outcomes, and retrieve combo metrics.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/combos
|
||||
|
||||
List routing combos
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/combos \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/combos
|
||||
|
||||
Create routing combo
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/combos \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### PATCH /api/combos/{id}
|
||||
|
||||
Update combo
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://localhost:20128/api/combos/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/combos/{id}
|
||||
|
||||
Delete combo
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/combos/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/combos/metrics
|
||||
|
||||
Get combo metrics
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/combos/metrics \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/combos/test
|
||||
|
||||
Test a combo configuration
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/combos/test \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/fallback/chains
|
||||
|
||||
List fallback chains
|
||||
|
||||
Returns all registered fallback chains for model routing.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/fallback/chains \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/fallback/chains
|
||||
|
||||
Create fallback chain
|
||||
|
||||
Registers a fallback routing chain for a model.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/fallback/chains \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/fallback/chains
|
||||
|
||||
Delete fallback chain
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/fallback/chains \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-routing/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — Routing & Combos
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
## What is a combo?
|
||||
|
||||
A combo is a named group of providers/models with a routing strategy. All requests through a combo are automatically distributed, failed-over, and load-balanced — the caller uses a single model ID like `my-combo`.
|
||||
|
||||
## List existing combos
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/combos \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Response includes `id`, `name`, `strategy`, `enabled`, and per-target stats.
|
||||
|
||||
## Create a combo
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/api/combos \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "my-combo",
|
||||
"strategy": "priority",
|
||||
"targets": [
|
||||
{ "provider": "anthropic", "model": "claude-opus-4-7", "weight": 1 },
|
||||
{ "provider": "openai", "model": "gpt-4o", "weight": 1 }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## 14 routing strategies
|
||||
|
||||
| Strategy | Description |
|
||||
| ------------------- | -------------------------------------------------- |
|
||||
| `priority` | Always use target[0]; fall back on error |
|
||||
| `weighted` | Distribute by weight percentage |
|
||||
| `round-robin` | Rotate targets in order |
|
||||
| `fill-first` | Fill quota of target[0] before spilling |
|
||||
| `least-used` | Route to target with fewest active requests |
|
||||
| `cost-optimized` | Pick cheapest target for the token estimate |
|
||||
| `auto` | 9-factor scoring: cost + latency + quota + circuit |
|
||||
| `random` | Uniform random selection |
|
||||
| `strict-random` | Random without repeating until all used |
|
||||
| `p2c` | Power-of-2-choices: sample 2, pick better |
|
||||
| `reset-aware` | Prefer targets near quota reset time |
|
||||
| `lkgp` | Last-known-good-provider sticky routing |
|
||||
| `context-optimized` | Pick best model for context length |
|
||||
| `context-relay` | Chain models for very long contexts |
|
||||
|
||||
## Auto-combo (recommended for production)
|
||||
|
||||
Auto-combo scores each candidate on 9 factors every request:
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/api/combos \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "prod-auto",
|
||||
"strategy": "auto",
|
||||
"targets": [
|
||||
{ "provider": "anthropic", "model": "claude-sonnet-4-6" },
|
||||
{ "provider": "openai", "model": "gpt-4o-mini" },
|
||||
{ "provider": "google", "model": "gemini-2.0-flash" }
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
Then call it with:
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/chat/completions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "model": "prod-auto", "messages": [{ "role": "user", "content": "Hello" }] }'
|
||||
```
|
||||
|
||||
## Activate / deactivate a combo
|
||||
|
||||
```bash
|
||||
# Activate
|
||||
curl -X PUT $OMNIROUTE_URL/api/combos/{id}/toggle \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-d '{ "enabled": true }'
|
||||
```
|
||||
|
||||
## Get combo metrics
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/combos/{id}/metrics \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Returns p50/p95/p99 latency, success rate, cost, and per-target breakdown.
|
||||
|
||||
## Simulate routing (dry run)
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/api/routing/simulate \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "comboId": "{id}", "messages": [{ "role": "user", "content": "test" }] }'
|
||||
```
|
||||
|
||||
Returns which provider would be selected and why — no actual API call is made.
|
||||
|
||||
## Via MCP (if OmniRoute is your MCP server)
|
||||
|
||||
```
|
||||
omniroute_list_combos → list all combos
|
||||
omniroute_switch_combo → enable/disable a combo
|
||||
omniroute_set_routing_strategy → change strategy at runtime
|
||||
omniroute_simulate_route → dry-run routing decision
|
||||
omniroute_best_combo_for_task → get recommendation by task type
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
- `404 combo not found` → check `id` from `/api/combos`
|
||||
- `400 invalid strategy` → use one of the 14 strategies above
|
||||
- `409 name conflict` → combo name already exists
|
||||
<!-- skill:custom-end -->
|
||||
179
skills/omni-compression/SKILL.md
Normal file
179
skills/omni-compression/SKILL.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
name: omni-compression
|
||||
description: Configure RTK (command output), Caveman (prose), and stacked compression modes. Manage language packs, custom rules, and test prompt compression reducing tokens by 60–90%.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Configure RTK (command output), Caveman (prose), and stacked compression modes. Manage language packs, custom rules, and test prompt compression reducing tokens by 60–90%.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/compression/preview
|
||||
|
||||
Preview compression for a message payload
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/compression/preview \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/compression/language-packs
|
||||
|
||||
List Caveman compression language packs
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/compression/language-packs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/compression/rules
|
||||
|
||||
List Caveman compression rule metadata
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/compression/rules \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-compression/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — Compression
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
## Overview
|
||||
|
||||
OmniRoute compresses token payloads before forwarding to providers. No code changes required — set it once, it applies to all requests transparently.
|
||||
|
||||
| Engine | Best for | Typical savings |
|
||||
| ------------------------- | ------------------------------------ | --------------- |
|
||||
| RTK | Terminal / build / test / git output | 60–90% |
|
||||
| Caveman | Human prose, chat history | 46% input |
|
||||
| Stacked (`rtk → caveman`) | Mixed coding sessions | 78–95% |
|
||||
| MCP accessibility filter | Browser/accessibility tool results | 60–80% |
|
||||
|
||||
## Get current settings
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
## Enable RTK (best for coding agents)
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "mode": "rtk", "enabled": true }'
|
||||
```
|
||||
|
||||
## Enable stacked mode (maximum savings)
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"mode": "stacked",
|
||||
"enabled": true,
|
||||
"stackedPipeline": ["rtk", "caveman"]
|
||||
}'
|
||||
```
|
||||
|
||||
## Enable Caveman (prose / chat)
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "mode": "standard", "enabled": true }'
|
||||
```
|
||||
|
||||
Caveman intensities: `lite` (safe), `standard` (balanced), `aggressive` (long sessions), `ultra` (context recovery).
|
||||
|
||||
## Preview compression before enabling
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/api/compression/preview \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"mode": "rtk",
|
||||
"text": "$ npm test\n> jest\n\nPASS src/a.test.ts (2.1s)\nPASS src/b.test.ts (1.8s)\n..."
|
||||
}'
|
||||
```
|
||||
|
||||
Response includes `compressed`, `original_length`, `compressed_length`, `savings_pct`.
|
||||
|
||||
## MCP accessibility-tree filter (browser agent use)
|
||||
|
||||
When OmniRoute is used with browser/Playwright MCP tools, it automatically compresses verbose accessibility-tree tool results. Enabled by default; configure thresholds:
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"mcpAccessibility": {
|
||||
"enabled": true,
|
||||
"collapseThreshold": 30,
|
||||
"maxTextChars": 50000
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
`collapseThreshold`: collapse sibling lines when ≥ N repeats (default 30).
|
||||
`maxTextChars`: hard truncate after N chars with navigation hint (default 50000).
|
||||
|
||||
## Language packs (Caveman)
|
||||
|
||||
Caveman supports language-aware rules for pt-BR, es, de, fr, ja:
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"mode": "standard",
|
||||
"cavemanConfig": {
|
||||
"language": "pt-BR",
|
||||
"autoDetectLanguage": true
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
## Via MCP
|
||||
|
||||
```
|
||||
omniroute_compression_status → current settings + savings analytics
|
||||
omniroute_compression_configure → update mode/threshold/language
|
||||
omniroute_set_compression_engine → switch engine at runtime
|
||||
```
|
||||
|
||||
## Disable compression
|
||||
|
||||
```bash
|
||||
curl -X PUT $OMNIROUTE_URL/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-d '{ "enabled": false }'
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
- `400 invalid mode` → use `off`, `lite`, `standard`, `aggressive`, `ultra`, `rtk`, or `stacked`
|
||||
- `400 invalid stackedPipeline` → array must contain valid engine ids (`rtk`, `caveman`)
|
||||
<!-- skill:custom-end -->
|
||||
68
skills/omni-context-rtk/SKILL.md
Normal file
68
skills/omni-context-rtk/SKILL.md
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: omni-context-rtk
|
||||
description: Configure RTK filters, context engineering rules, and context relay settings. Test compression with real prompt samples and manage context transformation pipelines.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Configure RTK filters, context engineering rules, and context relay settings. Test compression with real prompt samples and manage context transformation pipelines.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/context/rtk/config
|
||||
|
||||
Get RTK compression settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/context/rtk/config \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/context/rtk/config
|
||||
|
||||
Update RTK compression settings
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/context/rtk/config \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/context/rtk/filters
|
||||
|
||||
List RTK filters and load diagnostics
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/context/rtk/filters \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/context/rtk/test
|
||||
|
||||
Run RTK compression preview for text
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/context/rtk/test \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/context/rtk/raw-output/{id}
|
||||
|
||||
Read retained redacted RTK raw output
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/context/rtk/raw-output/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
20
skills/omni-db-backups/SKILL.md
Normal file
20
skills/omni-db-backups/SKILL.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: omni-db-backups
|
||||
description: Trigger system backups, restore from backup files, and manage the SQLite database lifecycle. Supports export, import, and incremental snapshot strategies.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Trigger system backups, restore from backup files, and manage the SQLite database lifecycle. Supports export, import, and incremental snapshot strategies.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
530
skills/omni-inference/SKILL.md
Normal file
530
skills/omni-inference/SKILL.md
Normal file
@@ -0,0 +1,530 @@
|
||||
---
|
||||
name: omni-inference
|
||||
description: "The core OpenAI-compatible inference endpoints: chat completions, embeddings, images, audio (TTS/STT), moderations, rerank, and the Responses API. The primary integration surface for AI agents."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
The core OpenAI-compatible inference endpoints: chat completions, embeddings, images, audio (TTS/STT), moderations, rerank, and the Responses API. The primary integration surface for AI agents.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/v1/chat/completions
|
||||
|
||||
Create chat completion
|
||||
|
||||
OpenAI-compatible chat completions endpoint. Routes to configured providers.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/chat/completions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/providers/{provider}/chat/completions
|
||||
|
||||
Create chat completion (provider-specific)
|
||||
|
||||
Routes to a specific provider by name.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/providers/{provider}/chat/completions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/api/chat
|
||||
|
||||
Ollama-compatible chat endpoint
|
||||
|
||||
Provides compatibility with Ollama's /api/chat format.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/api/chat \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/messages
|
||||
|
||||
Create message (Anthropic-compatible)
|
||||
|
||||
Anthropic Messages API endpoint. Routes to Claude providers.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/messages \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/messages/count_tokens
|
||||
|
||||
Count tokens for a message
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/messages/count_tokens \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/responses
|
||||
|
||||
Create response (OpenAI Responses API)
|
||||
|
||||
OpenAI Responses API endpoint.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/responses \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/embeddings
|
||||
|
||||
Create embeddings
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/embeddings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/providers/{provider}/embeddings
|
||||
|
||||
Create embeddings (provider-specific)
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/providers/{provider}/embeddings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/images/generations
|
||||
|
||||
Generate images
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/images/generations \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/providers/{provider}/images/generations
|
||||
|
||||
Generate images (provider-specific)
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/providers/{provider}/images/generations \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/audio/speech
|
||||
|
||||
Generate speech audio
|
||||
|
||||
Text-to-speech endpoint. Routes to configured TTS providers.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/audio/speech \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/audio/transcriptions
|
||||
|
||||
Transcribe audio
|
||||
|
||||
Audio-to-text transcription endpoint.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/moderations
|
||||
|
||||
Create moderation
|
||||
|
||||
Content moderation endpoint. Routes to configured moderation providers.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/moderations \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/v1/rerank
|
||||
|
||||
Rerank documents
|
||||
|
||||
Document reranking endpoint.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/v1/rerank \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/v1
|
||||
|
||||
API v1 root endpoint
|
||||
|
||||
Returns basic API info and status.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/v1 \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/v1/providers/{provider}/models
|
||||
|
||||
List models for a specific provider
|
||||
|
||||
Returns only models for the selected provider with provider prefix removed from each model id.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/v1/providers/{provider}/models \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Aggregated from: omniroute-chat, omniroute-image, omniroute-tts, omniroute-stt, omniroute-embeddings, omniroute-web-search, omniroute-web-fetch -->
|
||||
|
||||
## Chat completions
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoints
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/chat/completions` — OpenAI format
|
||||
- `POST $OMNIROUTE_URL/v1/messages` — Anthropic Messages format
|
||||
- `POST $OMNIROUTE_URL/v1/responses` — OpenAI Responses API
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models | jq '.data[].id'
|
||||
```
|
||||
|
||||
Combos (e.g. `auto`, `cost-optimized`, `subscription`) auto-fallback through multiple providers.
|
||||
|
||||
### OpenAI format example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/chat/completions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "claude-opus-4-7",
|
||||
"messages": [{"role": "user", "content": "Refactor this function"}],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
### Anthropic format example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/messages \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "claude-opus-4-7",
|
||||
"max_tokens": 4096,
|
||||
"messages": [{"role": "user", "content": "Hi"}]
|
||||
}'
|
||||
```
|
||||
|
||||
### Tool use
|
||||
|
||||
Supports OpenAI `tools` array and Anthropic `tools` block. Tool results
|
||||
auto-compressed via RTK (47 filters: git-diff, grep, test-jest, terraform-plan,
|
||||
docker-logs, etc.) — 20-40% token savings. Disable per-request with
|
||||
`X-Omniroute-Rtk: off` header.
|
||||
|
||||
### Reasoning / thinking
|
||||
|
||||
Anthropic extended thinking and OpenAI Responses reasoning blocks are forwarded
|
||||
verbatim. Cached automatically via reasoning cache.
|
||||
|
||||
### Errors
|
||||
|
||||
- `401` → invalid API key
|
||||
- `400 invalid_model` → model not in registry; check `/v1/models`
|
||||
- `503 circuit_open` → provider circuit breaker tripped; retry later or use combo
|
||||
- `429 rate_limited` → honor `Retry-After`; consider using a combo for auto-fallback
|
||||
|
||||
## Image generation
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoints
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/images/generations` — Text-to-image
|
||||
- `POST $OMNIROUTE_URL/v1/images/edits` — Image edit (mask)
|
||||
- `POST $OMNIROUTE_URL/v1/images/variations` — Variations
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/image | jq '.data[]'
|
||||
```
|
||||
|
||||
Returns `{ id, owned_by, sizes:[...], capabilities:[...] }` per model.
|
||||
|
||||
### Generate example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/images/generations \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "dall-e-3",
|
||||
"prompt": "a red bicycle on a wet street, photoreal",
|
||||
"n": 1,
|
||||
"size": "1024x1024",
|
||||
"response_format": "b64_json"
|
||||
}'
|
||||
```
|
||||
|
||||
Response: `{ created, data: [{ url? or b64_json, revised_prompt }] }`
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 invalid_size` → not supported by this model; check `/v1/models/image`
|
||||
- `400 content_policy_violation` → blocked by provider safety
|
||||
- `503` → provider unavailable; try another model in `/v1/models/image`
|
||||
|
||||
## Text-to-speech
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoint
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/audio/speech` — returns binary audio (mp3/opus/wav/flac)
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/tts | jq '.data[]'
|
||||
```
|
||||
|
||||
Each entry includes `voices:[...]` for the available voice names per provider.
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/audio/speech \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "tts-1",
|
||||
"input": "Hello from OmniRoute.",
|
||||
"voice": "alloy",
|
||||
"response_format": "mp3"
|
||||
}' --output speech.mp3
|
||||
```
|
||||
|
||||
### Voices
|
||||
|
||||
Voice names vary by provider. Check `/v1/models/tts` — each entry has `voices:[...]`.
|
||||
Common OpenAI voices: `alloy`, `echo`, `fable`, `onyx`, `nova`, `shimmer`.
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 invalid_voice` → voice not supported by this model
|
||||
- `400 input_too_long` → input exceeds model character limit
|
||||
- `503` → provider unavailable; try another model in `/v1/models/tts`
|
||||
|
||||
## Speech-to-text
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoints
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/audio/transcriptions` — multipart upload, returns text
|
||||
- `POST $OMNIROUTE_URL/v1/audio/translations` — transcribe + translate to English
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/stt | jq '.data[]'
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=whisper-1" \
|
||||
-F "response_format=verbose_json"
|
||||
```
|
||||
|
||||
Response: `{ text, language, duration, segments?:[{ start, end, text }] }`
|
||||
|
||||
### Supported formats
|
||||
|
||||
Audio: `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `wav`, `webm`.
|
||||
Response formats: `json`, `text`, `srt`, `verbose_json`, `vtt`.
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 invalid_file_format` → unsupported audio format
|
||||
- `400 file_too_large` → exceeds provider limit (usually 25MB)
|
||||
- `503` → provider unavailable; try another model in `/v1/models/stt`
|
||||
|
||||
## Embeddings
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoint
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/embeddings`
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/embedding | jq '.data[]'
|
||||
```
|
||||
|
||||
Each entry: `{ id, owned_by, dimensions, max_input_tokens }`.
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/embeddings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "text-embedding-3-large",
|
||||
"input": ["first text", "second text"],
|
||||
"encoding_format": "float"
|
||||
}'
|
||||
```
|
||||
|
||||
Response: `{ data:[{ embedding:[...], index }], usage:{ prompt_tokens, total_tokens } }`
|
||||
|
||||
### Batch input
|
||||
|
||||
`input` accepts a string or array of strings (up to provider batch limit, typically 2048 items).
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 input_too_long` → input exceeds `max_input_tokens` for this model
|
||||
- `400 invalid_encoding_format` → use `float` or `base64`
|
||||
- `503` → provider unavailable; try another model in `/v1/models/embedding`
|
||||
|
||||
## Web search
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoint
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/web/search` — unified search format
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/web | jq '.data[] | select(.kind == "webSearch")'
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/web/search \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "tavily/search",
|
||||
"query": "OmniRoute github latest release",
|
||||
"max_results": 5,
|
||||
"include_answer": true
|
||||
}'
|
||||
```
|
||||
|
||||
Response: `{ answer?, results:[{ url, title, content, score }] }`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Field | Type | Description |
|
||||
| ---------------- | ------- | ------------------------------------ |
|
||||
| `model` | string | Provider model from `/v1/models/web` |
|
||||
| `query` | string | Search query |
|
||||
| `max_results` | number | Max results (default: 5) |
|
||||
| `include_answer` | boolean | Include AI-synthesized answer |
|
||||
| `search_depth` | string | `basic` or `advanced` (Tavily) |
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 query_too_long` → shorten the search query
|
||||
- `503` → provider unavailable; try another model in `/v1/models/web`
|
||||
|
||||
## Web fetch
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
### Endpoint
|
||||
|
||||
- `POST $OMNIROUTE_URL/v1/web/fetch`
|
||||
|
||||
### Discover
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/v1/models/web | jq '.data[] | select(.kind == "webFetch")'
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/v1/web/fetch \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "jina/reader",
|
||||
"url": "https://anthropic.com",
|
||||
"format": "markdown"
|
||||
}'
|
||||
```
|
||||
|
||||
Response: `{ url, title, markdown, links?:[...], images?:[...] }`
|
||||
|
||||
### Parameters
|
||||
|
||||
| Field | Type | Description |
|
||||
| -------- | ------ | ----------------------------------------------------------------------- |
|
||||
| `model` | string | Provider from `/v1/models/web` (e.g. `jina/reader`, `firecrawl/scrape`) |
|
||||
| `url` | string | URL to fetch |
|
||||
| `format` | string | `markdown` (default), `html`, `text` |
|
||||
|
||||
### Errors
|
||||
|
||||
- `400 invalid_url` → URL must be http/https
|
||||
- `403 blocked` → provider blocked by target site; try a different model
|
||||
- `503` → provider unavailable; try another model in `/v1/models/web`
|
||||
<!-- skill:custom-end -->
|
||||
91
skills/omni-mcp/SKILL.md
Normal file
91
skills/omni-mcp/SKILL.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: omni-mcp
|
||||
description: "Connect to the OmniRoute MCP server (37 tools, 3 transports: SSE/stdio/HTTP). Covers routing, cache, compression, memory, skills, providers, and audit tools across 16 permission scopes."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Connect to the OmniRoute MCP server (37 tools, 3 transports: SSE/stdio/HTTP). Covers routing, cache, compression, memory, skills, providers, and audit tools across 16 permission scopes.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-mcp/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — MCP Server
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
## Transports
|
||||
|
||||
- **stdio** — local IPC, for Claude Desktop / VS Code extensions
|
||||
- **SSE** — `GET $OMNIROUTE_URL/api/mcp/sse`
|
||||
- **Streamable HTTP** — `POST $OMNIROUTE_URL/api/mcp/stream`
|
||||
|
||||
## Claude Desktop config
|
||||
|
||||
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"omniroute": {
|
||||
"command": "npx",
|
||||
"args": ["-y", "omniroute", "--mcp"],
|
||||
"env": { "OMNIROUTE_KEY": "sk-..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Cursor / VS Code config
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"omniroute": {
|
||||
"url": "http://localhost:20128/api/mcp/sse",
|
||||
"headers": { "Authorization": "Bearer sk-..." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Available tools (37 total)
|
||||
|
||||
| Scope | Tools |
|
||||
| --------- | -------------------------------------------------------------------------------------------------- |
|
||||
| health | `omniroute_get_health` |
|
||||
| combos | `omniroute_list_combos`, `omniroute_get_combo_metrics`, `omniroute_switch_combo` |
|
||||
| routing | `omniroute_simulate_route`, `omniroute_best_combo_for_task`, `omniroute_explain_route` |
|
||||
| providers | `omniroute_get_provider_metrics`, `omniroute_check_quota`, `omniroute_route_request` |
|
||||
| budget | `omniroute_set_budget_guard`, `omniroute_set_routing_strategy`, `omniroute_set_resilience_profile` |
|
||||
| testing | `omniroute_test_combo` |
|
||||
| memory | `memory_add`, `memory_search`, `memory_delete` |
|
||||
| skills | `skill_invoke`, `skill_list`, `skill_describe`, `skill_register` |
|
||||
| cache | `omniroute_cache_stats`, `omniroute_cache_flush` |
|
||||
| admin | `omniroute_db_health_check`, `omniroute_sync_pricing`, `omniroute_get_session_snapshot` |
|
||||
|
||||
Full list: `GET $OMNIROUTE_URL/api/mcp/tools`
|
||||
|
||||
## Scopes
|
||||
|
||||
Tools are grouped into 13 scopes (chat-only, memory-readonly, full-admin, etc.).
|
||||
Pass scope name as `--scope` arg or via `X-Omniroute-Scope` header.
|
||||
|
||||
## Reference
|
||||
|
||||
Full docs: https://github.com/diegosouzapw/OmniRoute/blob/main/docs/frameworks/MCP-SERVER.md
|
||||
<!-- skill:custom-end -->
|
||||
59
skills/omni-models/SKILL.md
Normal file
59
skills/omni-models/SKILL.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
name: omni-models
|
||||
description: Query available AI models across all configured providers. List models, resolve model aliases, and browse the full model catalog including provider-specific variants.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Query available AI models across all configured providers. List models, resolve model aliases, and browse the full model catalog including provider-specific variants.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/v1/models
|
||||
|
||||
List available models
|
||||
|
||||
Returns all models available across configured providers.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/v1/models \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/models
|
||||
|
||||
List models (management)
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/models \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/models/alias
|
||||
|
||||
Create or update a model alias
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/models/alias \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/models/catalog
|
||||
|
||||
Get full model catalog
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/models/catalog \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
229
skills/omni-providers/SKILL.md
Normal file
229
skills/omni-providers/SKILL.md
Normal file
@@ -0,0 +1,229 @@
|
||||
---
|
||||
name: omni-providers
|
||||
description: Manage provider connections, API keys, OAuth flows, and connection tests via the REST API. List, add, update, remove, and test AI provider integrations (OpenAI, Anthropic, Gemini, and 160+).
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Manage provider connections, API keys, OAuth flows, and connection tests via the REST API. List, add, update, remove, and test AI provider integrations (OpenAI, Anthropic, Gemini, and 160+).
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/providers
|
||||
|
||||
List provider connections
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/providers \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/providers
|
||||
|
||||
Create provider connection
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/providers \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/providers/{id}
|
||||
|
||||
Get provider connection
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/providers/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PATCH /api/providers/{id}
|
||||
|
||||
Update provider connection
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://localhost:20128/api/providers/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/providers/{id}
|
||||
|
||||
Delete provider connection
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/providers/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/providers/{id}/test
|
||||
|
||||
Test provider connection
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/providers/{id}/test \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/providers/{id}/models
|
||||
|
||||
List models for a provider
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/providers/{id}/models \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/providers/test-batch
|
||||
|
||||
Test multiple providers at once
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/providers/test-batch \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/providers/validate
|
||||
|
||||
Validate provider credentials
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/providers/validate \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/providers/client
|
||||
|
||||
Get client-side provider info
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/providers/client \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/provider-nodes
|
||||
|
||||
List provider nodes
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/provider-nodes \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/provider-nodes
|
||||
|
||||
Create provider node
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/provider-nodes \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### PATCH /api/provider-nodes/{id}
|
||||
|
||||
Update provider node
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://localhost:20128/api/provider-nodes/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### DELETE /api/provider-nodes/{id}
|
||||
|
||||
Delete provider node
|
||||
|
||||
```bash
|
||||
curl -X DELETE https://localhost:20128/api/provider-nodes/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/provider-nodes/validate
|
||||
|
||||
Validate a provider node
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/provider-nodes/validate \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/provider-models
|
||||
|
||||
List provider models
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/provider-models \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
## Additional endpoints
|
||||
|
||||
### POST /api/providers/{id}/refresh
|
||||
|
||||
Rotate / refresh OAuth tokens or re-validate credentials for a provider connection. Useful after token expiration or when credentials need to be renewed without deleting the connection.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/providers/{id}/refresh \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/provider-metrics
|
||||
|
||||
Retrieve aggregated usage and performance metrics for all provider connections (request counts, error rates, latency percentiles, token usage). Used by dashboards and Auto-Combo scoring.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/provider-metrics \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
**Response example:**
|
||||
```json
|
||||
{
|
||||
"providers": [
|
||||
{
|
||||
"id": "my-openai",
|
||||
"requestCount": 1240,
|
||||
"errorRate": 0.02,
|
||||
"p50LatencyMs": 340,
|
||||
"p99LatencyMs": 1200,
|
||||
"tokensIn": 500000,
|
||||
"tokensOut": 250000
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/providers/health-matrix
|
||||
|
||||
Returns a health-matrix view of all providers with per-connection status, circuit-breaker state, and cooldown expiry. Useful for debugging routing decisions.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/providers/health-matrix \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
<!-- skill:custom-end -->
|
||||
20
skills/omni-proxies/SKILL.md
Normal file
20
skills/omni-proxies/SKILL.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: omni-proxies
|
||||
description: Configure HTTP/HTTPS/SOCKS proxies for upstream provider requests. Set per-provider or global proxy rules, test connectivity, and manage proxy rotation.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Configure HTTP/HTTPS/SOCKS proxies for upstream provider requests. Set per-provider or global proxy rules, test connectivity, and manage proxy rotation.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
144
skills/omni-resilience/SKILL.md
Normal file
144
skills/omni-resilience/SKILL.md
Normal file
@@ -0,0 +1,144 @@
|
||||
---
|
||||
name: omni-resilience
|
||||
description: Monitor provider health, circuit-breaker states, p50/p95/p99 latency metrics, and budget guard alerts. Inspect connection cooldowns and model lockouts in real time.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Monitor provider health, circuit-breaker states, p50/p95/p99 latency metrics, and budget guard alerts. Inspect connection cooldowns and model lockouts in real time.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/monitoring/health
|
||||
|
||||
System health check
|
||||
|
||||
Returns system health including uptime, memory, circuit breakers, rate limits
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/monitoring/health \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
|
||||
<!-- skill:custom-start -->
|
||||
<!-- Migrated from skills/omniroute-monitoring/SKILL.md (preserved curated content) -->
|
||||
|
||||
# OmniRoute — Monitoring & Health
|
||||
|
||||
Requires `OMNIROUTE_URL` and `OMNIROUTE_KEY`. See [entry-point SKILL](https://raw.githubusercontent.com/diegosouzapw/OmniRoute/main/skills/omniroute/SKILL.md) for setup.
|
||||
|
||||
## System health
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/health \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Returns: uptime, memory, active connections, circuit breaker states, rate limit status, cache stats.
|
||||
|
||||
Unauthenticated quick check:
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/health
|
||||
# → {"ok":true}
|
||||
```
|
||||
|
||||
## Provider circuit breakers
|
||||
|
||||
Circuit breakers prevent traffic from hitting failing providers.
|
||||
|
||||
States: `CLOSED` (normal), `OPEN` (blocked), `HALF_OPEN` (probe mode — auto-recovers).
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/monitoring/health \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Response includes `circuitBreakers` array with per-provider state and `resetAt` timestamp.
|
||||
|
||||
## Per-provider metrics (p50/p95/p99)
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/providers/metrics \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Response shape per provider:
|
||||
|
||||
```json
|
||||
{
|
||||
"provider": "anthropic",
|
||||
"requests": 1247,
|
||||
"successRate": 0.994,
|
||||
"latency": { "p50": 820, "p95": 2100, "p99": 3800 },
|
||||
"circuitState": "CLOSED",
|
||||
"tokensUsed": 2847000
|
||||
}
|
||||
```
|
||||
|
||||
## Via MCP (if OmniRoute is your MCP server)
|
||||
|
||||
```
|
||||
omniroute_get_health → full system health snapshot
|
||||
omniroute_get_provider_metrics → p50/p95/p99 + circuit state per provider
|
||||
omniroute_get_session_snapshot → cost, tokens, errors for current session
|
||||
omniroute_check_quota → quota balance + percent remaining + reset time
|
||||
omniroute_db_health_check → diagnose + auto-repair database drift
|
||||
```
|
||||
|
||||
## Quota check
|
||||
|
||||
```bash
|
||||
curl $OMNIROUTE_URL/api/quota \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Returns used/total tokens and requests per provider/account, with `resetAt` timestamps.
|
||||
|
||||
## Budget guard (spend limit)
|
||||
|
||||
Set a session spending limit that degrades or blocks requests when hit:
|
||||
|
||||
```bash
|
||||
curl -X POST $OMNIROUTE_URL/api/budget/guard \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"limitUsd": 5.00,
|
||||
"action": "degrade",
|
||||
"degradeTo": "openai/gpt-4o-mini"
|
||||
}'
|
||||
```
|
||||
|
||||
`action` options:
|
||||
|
||||
- `degrade` — switch to a cheaper model when limit is hit
|
||||
- `block` — return 429 when limit is hit
|
||||
- `alert` — continue but add `X-Budget-Warning` header
|
||||
|
||||
## MCP audit log
|
||||
|
||||
OmniRoute logs every MCP tool call to `mcp_audit` table. Query via API:
|
||||
|
||||
```bash
|
||||
curl "$OMNIROUTE_URL/api/mcp/status" \
|
||||
-H "Authorization: Bearer $OMNIROUTE_KEY"
|
||||
```
|
||||
|
||||
Returns: server status, heartbeat, recent audit activity summary.
|
||||
|
||||
## Errors
|
||||
|
||||
- `503` on health endpoint → OmniRoute is starting up; retry in 5s
|
||||
- Circuit breaker `OPEN` → provider is temporarily blocked; check `resetAt` to know when it auto-recovers
|
||||
- `429 budget_exceeded` → budget guard limit reached; raise limit or wait for reset
|
||||
<!-- skill:custom-end -->
|
||||
222
skills/omni-settings/SKILL.md
Normal file
222
skills/omni-settings/SKILL.md
Normal file
@@ -0,0 +1,222 @@
|
||||
---
|
||||
name: omni-settings
|
||||
description: "Read and update global application settings: system prompts, thinking budget, IP filters, payload rules, combo defaults, and require-login configuration."
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Read and update global application settings: system prompts, thinking budget, IP filters, payload rules, combo defaults, and require-login configuration.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/settings
|
||||
|
||||
Get application settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PATCH /api/settings
|
||||
|
||||
Update settings
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://localhost:20128/api/settings \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/compression
|
||||
|
||||
Get global compression settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/settings/compression
|
||||
|
||||
Update global compression settings
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/compression \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/payload-rules
|
||||
|
||||
Get payload rules configuration
|
||||
|
||||
Returns the current payload rules used to mutate outgoing request payloads before they
|
||||
are sent upstream.
|
||||
|
||||
Requires a dashboard management session cookie when management auth is enabled.
|
||||
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/payload-rules \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/settings/payload-rules
|
||||
|
||||
Update payload rules configuration
|
||||
|
||||
Persists and hot reloads payload rules. The legacy input field `default-raw` is accepted
|
||||
on writes and normalized to `defaultRaw` in responses/runtime state.
|
||||
|
||||
Requires a dashboard management session cookie when management auth is enabled.
|
||||
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/payload-rules \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/combo-defaults
|
||||
|
||||
Get combo default settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/combo-defaults \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/settings/proxy
|
||||
|
||||
Get proxy settings
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/proxy \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PATCH /api/settings/proxy
|
||||
|
||||
Update proxy settings
|
||||
|
||||
```bash
|
||||
curl -X PATCH https://localhost:20128/api/settings/proxy \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/settings/proxy/test
|
||||
|
||||
Test proxy connection
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/settings/proxy/test \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/settings/require-login
|
||||
|
||||
Toggle login requirement
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/settings/require-login \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/ip-filter
|
||||
|
||||
Get IP filter configuration
|
||||
|
||||
Returns the current IP filter settings including blacklist, whitelist, and temp bans.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/ip-filter \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/settings/ip-filter
|
||||
|
||||
Update IP filter configuration
|
||||
|
||||
Configure IP filtering with blacklist/whitelist modes, add/remove individual IPs, and manage temp bans.
|
||||
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/ip-filter \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/system-prompt
|
||||
|
||||
Get system prompt configuration
|
||||
|
||||
Returns the current system prompt injection settings.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/system-prompt \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/settings/system-prompt
|
||||
|
||||
Update system prompt configuration
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/system-prompt \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/settings/thinking-budget
|
||||
|
||||
Get thinking budget configuration
|
||||
|
||||
Returns the current thinking/reasoning budget settings for AI models.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/settings/thinking-budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/settings/thinking-budget
|
||||
|
||||
Update thinking budget configuration
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/settings/thinking-budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/tags
|
||||
|
||||
List Ollama-compatible model tags
|
||||
|
||||
Returns models in Ollama /api/tags format for Ollama client compatibility
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/tags \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
98
skills/omni-sync-cloud/SKILL.md
Normal file
98
skills/omni-sync-cloud/SKILL.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: omni-sync-cloud
|
||||
description: Synchronise OmniRoute configuration, provider connections, and settings to/from cloud storage. Manage cloud worker authentication and remote backup targets.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Synchronise OmniRoute configuration, provider connections, and settings to/from cloud storage. Manage cloud worker authentication and remote backup targets.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/cloud/auth
|
||||
|
||||
Authenticate with cloud worker
|
||||
|
||||
Authenticates with the OmniRoute cloud worker for remote access.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cloud/auth \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### PUT /api/cloud/credentials/update
|
||||
|
||||
Update cloud worker credentials
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/cloud/credentials/update \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/cloud/model/resolve
|
||||
|
||||
Resolve model via cloud
|
||||
|
||||
Resolves a model request through the cloud worker.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/cloud/model/resolve \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/cloud/models/alias
|
||||
|
||||
Get cloud model aliases
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/cloud/models/alias \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### PUT /api/cloud/models/alias
|
||||
|
||||
Update cloud model alias
|
||||
|
||||
```bash
|
||||
curl -X PUT https://localhost:20128/api/cloud/models/alias \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/sync/cloud
|
||||
|
||||
Sync with cloud
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/sync/cloud \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/sync/initialize
|
||||
|
||||
Initialize cloud sync
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/sync/initialize \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
20
skills/omni-tunnels/SKILL.md
Normal file
20
skills/omni-tunnels/SKILL.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: omni-tunnels
|
||||
description: Create and manage secure tunnels (ngrok, Cloudflare Tunnel, custom) to expose OmniRoute to the internet or share access with remote agents and CI pipelines.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Create and manage secure tunnels (ngrok, Cloudflare Tunnel, custom) to expose OmniRoute to the internet or share access with remote agents and CI pipelines.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
115
skills/omni-usage-logs/SKILL.md
Normal file
115
skills/omni-usage-logs/SKILL.md
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
name: omni-usage-logs
|
||||
description: Access detailed call logs and usage analytics. Filter by provider, model, time range, status, and cost. Export logs and aggregate token usage across all connections.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Access detailed call logs and usage analytics. Filter by provider, model, time range, status, and cost. Export logs and aggregate token usage across all connections.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### GET /api/usage/analytics
|
||||
|
||||
Get usage analytics
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/analytics \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/call-logs
|
||||
|
||||
Get call logs
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/call-logs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/call-logs/{id}
|
||||
|
||||
Get a specific call log
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/call-logs/{id} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/{connectionId}
|
||||
|
||||
Get usage for a specific connection
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/{connectionId} \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/history
|
||||
|
||||
Get usage history
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/history \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/logs
|
||||
|
||||
Get usage logs
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/logs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/proxy-logs
|
||||
|
||||
Get proxy logs
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/proxy-logs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/request-logs
|
||||
|
||||
Get request logs
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/request-logs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### GET /api/usage/budget
|
||||
|
||||
Get usage budget status
|
||||
|
||||
Returns current budget limits and consumption.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/usage/budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/usage/budget
|
||||
|
||||
Configure usage budget
|
||||
|
||||
Set or update budget limits for usage tracking.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/usage/budget \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
221
skills/omni-version-manager/SKILL.md
Normal file
221
skills/omni-version-manager/SKILL.md
Normal file
@@ -0,0 +1,221 @@
|
||||
---
|
||||
name: omni-version-manager
|
||||
description: Install, start, stop, restart, and update embedded services (9Router, CLIProxyAPI). Monitor service status, retrieve logs, and configure auto-start for local-only service endpoints.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Install, start, stop, restart, and update embedded services (9Router, CLIProxyAPI). Monitor service status, retrieve logs, and configure auto-start for local-only service endpoints.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /api/services/9router/install
|
||||
|
||||
Install 9Router from npm
|
||||
|
||||
Installs the `9router` npm package under DATA_DIR/services/9router/. Uses execFile (no shell interpolation — hard rule #13). **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/install \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/start
|
||||
|
||||
Start 9Router
|
||||
|
||||
Spawns the 9Router process. Idempotent if already running. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/stop
|
||||
|
||||
Stop 9Router
|
||||
|
||||
Gracefully stops 9Router (SIGTERM → 15 s → SIGKILL). Idempotent. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/stop \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/restart
|
||||
|
||||
Restart 9Router
|
||||
|
||||
Equivalent to stop() then start() under the operation lock. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/restart \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/update
|
||||
|
||||
Update 9Router to a newer npm version
|
||||
|
||||
Stops the service (if running), installs the newer npm version, then restarts. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/update \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/9router/rotate-key
|
||||
|
||||
Rotate the 9Router API key
|
||||
|
||||
Generates a new API key, encrypts it at-rest, and restarts the service to apply it. The plaintext key is never returned. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/rotate-key \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/9router/status
|
||||
|
||||
Get 9Router status
|
||||
|
||||
Returns combined live supervisor state and DB metadata. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/9router/status \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/services/9router/auto-start
|
||||
|
||||
Toggle 9Router auto-start
|
||||
|
||||
When enabled, 9Router starts automatically on the next OmniRoute boot. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/9router/auto-start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/install
|
||||
|
||||
Install CLIProxyAPI from npm
|
||||
|
||||
Installs the CLIProxyAPI package under DATA_DIR/services/cliproxy/. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/install \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/start
|
||||
|
||||
Start CLIProxyAPI
|
||||
|
||||
Spawns the CLIProxyAPI process. Idempotent if already running. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/stop
|
||||
|
||||
Stop CLIProxyAPI
|
||||
|
||||
Gracefully stops CLIProxyAPI. Idempotent. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/stop \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/restart
|
||||
|
||||
Restart CLIProxyAPI
|
||||
|
||||
stop() then start() under the operation lock. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/restart \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/update
|
||||
|
||||
Update CLIProxyAPI to a newer npm version
|
||||
|
||||
Stops, installs newer version, restarts. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/update \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/cliproxy/status
|
||||
|
||||
Get CLIProxyAPI status
|
||||
|
||||
Returns live supervisor state and DB metadata (no apiKeyMasked — CLIProxyAPI does not use an injected API key). **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/cliproxy/status \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
### POST /api/services/cliproxy/auto-start
|
||||
|
||||
Toggle CLIProxyAPI auto-start
|
||||
|
||||
When enabled, CLIProxyAPI starts automatically on the next OmniRoute boot. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl -X POST https://localhost:20128/api/services/cliproxy/auto-start \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
```
|
||||
|
||||
### GET /api/services/{name}/logs
|
||||
|
||||
Stream service logs via SSE
|
||||
|
||||
Returns a Server-Sent Events stream from the service's in-memory ring buffer (5 MB, circular). Sends a `snapshot` event with historical lines first, then live `log` events, plus a `heartbeat` every 15 s. **LOCAL_ONLY** — loopback only.
|
||||
|
||||
```bash
|
||||
curl https://localhost:20128/api/services/{name}/logs \
|
||||
-H "Authorization: Bearer $OMNIROUTE_TOKEN"
|
||||
```
|
||||
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
20
skills/omni-webhooks/SKILL.md
Normal file
20
skills/omni-webhooks/SKILL.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: omni-webhooks
|
||||
description: Register, list, test, and remove webhook endpoints. Configure event subscriptions (request.completed, provider.error, budget.exceeded, etc.) and manage delivery retries.
|
||||
---
|
||||
<!-- generated by src/lib/agentSkills/generator.ts; manual edits will be overwritten -->
|
||||
|
||||
## Overview
|
||||
|
||||
Register, list, test, and remove webhook endpoints. Configure event subscriptions (request.completed, provider.error, budget.exceeded, etc.) and manage delivery retries.
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests require a valid Bearer token or session cookie. Obtain a token via `POST /api/auth/login` or configure `REQUIRE_API_KEY=false` for local development.
|
||||
|
||||
## Endpoints
|
||||
|
||||
_No endpoints mapped for this area yet._
|
||||
## Payloads
|
||||
|
||||
See the full OpenAPI specification at `GET /api/openapi/spec` or `docs/reference/openapi.yaml` for detailed request/response schemas.
|
||||
@@ -0,0 +1,304 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { SkillsConceptCard } from "@/shared/components/SkillsConceptCard";
|
||||
import { CoverageBar } from "./components/CoverageBar";
|
||||
import { McpA2aLinksBar } from "./components/McpA2aLinksBar";
|
||||
import { SkillCard } from "./components/SkillCard";
|
||||
import { SkillPreviewPane } from "./components/SkillPreviewPane";
|
||||
import type { AgentSkill, SkillCoverage } from "@/lib/agentSkills/types";
|
||||
|
||||
type FilterCategory = "all" | "api" | "cli";
|
||||
|
||||
// ── Skeleton helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
function SkillCardSkeleton(): JSX.Element {
|
||||
return (
|
||||
<div className="flex items-start gap-3 rounded-lg border border-border p-3 animate-pulse">
|
||||
<div className="h-9 w-9 rounded-lg bg-bg-subtle shrink-0" />
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="h-3 w-1/3 rounded bg-bg-subtle" />
|
||||
<div className="h-2 w-full rounded bg-bg-subtle" />
|
||||
<div className="h-2 w-4/5 rounded bg-bg-subtle" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function CoverageBarSkeleton(): JSX.Element {
|
||||
return (
|
||||
<div className="space-y-2 animate-pulse">
|
||||
<div className="flex gap-2">
|
||||
<div className="h-2 w-16 rounded bg-bg-subtle" />
|
||||
<div className="flex-1 h-2 rounded bg-bg-subtle" />
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="h-2 w-16 rounded bg-bg-subtle" />
|
||||
<div className="flex-1 h-2 rounded bg-bg-subtle" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Main component ───────────────────────────────────────────────────────────
|
||||
|
||||
export function AgentSkillsPageClient(): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
|
||||
// State
|
||||
const [catalog, setCatalog] = useState<AgentSkill[]>([]);
|
||||
const [coverage, setCoverage] = useState<SkillCoverage | null>(null);
|
||||
const [loadingCatalog, setLoadingCatalog] = useState(true);
|
||||
const [filter, setFilter] = useState<FilterCategory>("all");
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const [markdownCache, setMarkdownCache] = useState<Map<string, string>>(new Map());
|
||||
const [loadingPreview, setLoadingPreview] = useState(false);
|
||||
const [generatingSkills, setGeneratingSkills] = useState(false);
|
||||
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
|
||||
// ── Fetch catalog + coverage on mount ────────────────────────────────────
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
abortRef.current = controller;
|
||||
|
||||
const load = async () => {
|
||||
try {
|
||||
setLoadingCatalog(true);
|
||||
const res = await fetch("/api/agent-skills", { signal: controller.signal });
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json = (await res.json()) as { skills: AgentSkill[]; coverage: SkillCoverage };
|
||||
setCatalog(json.skills ?? []);
|
||||
setCoverage(json.coverage ?? null);
|
||||
} catch (err) {
|
||||
if ((err as Error).name !== "AbortError") {
|
||||
setCatalog([]);
|
||||
}
|
||||
} finally {
|
||||
setLoadingCatalog(false);
|
||||
}
|
||||
};
|
||||
|
||||
void load();
|
||||
return () => {
|
||||
controller.abort();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// ── Fetch raw markdown when a card is selected ────────────────────────────
|
||||
const loadPreview = useCallback(
|
||||
async (id: string) => {
|
||||
if (markdownCache.has(id)) return;
|
||||
setLoadingPreview(true);
|
||||
try {
|
||||
const res = await fetch(`/api/agent-skills/${id}/raw`);
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const body = await res.text();
|
||||
setMarkdownCache((prev) => new Map(prev).set(id, body));
|
||||
} catch {
|
||||
setMarkdownCache((prev) => {
|
||||
const next = new Map(prev);
|
||||
// Set empty string to signal "failed" so we show error state
|
||||
next.set(id, "");
|
||||
return next;
|
||||
});
|
||||
} finally {
|
||||
setLoadingPreview(false);
|
||||
}
|
||||
},
|
||||
[markdownCache],
|
||||
);
|
||||
|
||||
// ── Debounced preview load (200ms) ───────────────────────────────────────
|
||||
useEffect(() => {
|
||||
if (!selectedId) return;
|
||||
const timer = setTimeout(() => {
|
||||
void loadPreview(selectedId);
|
||||
}, 200);
|
||||
return () => clearTimeout(timer);
|
||||
}, [selectedId, loadPreview]);
|
||||
|
||||
const handleSelectCard = useCallback(
|
||||
(id: string) => {
|
||||
setSelectedId(id);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const handleRefreshPreview = useCallback(() => {
|
||||
if (!selectedId) return;
|
||||
setMarkdownCache((prev) => {
|
||||
const next = new Map(prev);
|
||||
next.delete(selectedId);
|
||||
return next;
|
||||
});
|
||||
void loadPreview(selectedId);
|
||||
}, [selectedId, loadPreview]);
|
||||
|
||||
// ── Generate missing skills ───────────────────────────────────────────────
|
||||
const handleGenerate = useCallback(async () => {
|
||||
const confirmed = window.confirm(t("regenerateConfirm"));
|
||||
if (!confirmed) return;
|
||||
setGeneratingSkills(true);
|
||||
try {
|
||||
const res = await fetch("/api/agent-skills/generate", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ dryRun: false, prune: false }),
|
||||
});
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
// Refresh catalog + coverage after generation
|
||||
const res2 = await fetch("/api/agent-skills");
|
||||
if (res2.ok) {
|
||||
const json = (await res2.json()) as { skills: AgentSkill[]; coverage: SkillCoverage };
|
||||
setCatalog(json.skills ?? []);
|
||||
setCoverage(json.coverage ?? null);
|
||||
}
|
||||
} catch {
|
||||
// Error handled silently — could extend with toast notification
|
||||
} finally {
|
||||
setGeneratingSkills(false);
|
||||
}
|
||||
}, [t]);
|
||||
|
||||
// ── Filtering + search ────────────────────────────────────────────────────
|
||||
const filteredSkills = catalog.filter((s) => {
|
||||
if (filter !== "all" && s.category !== filter) return false;
|
||||
if (searchTerm.trim()) {
|
||||
const q = searchTerm.toLowerCase();
|
||||
return (
|
||||
s.name.toLowerCase().includes(q) ||
|
||||
s.description.toLowerCase().includes(q) ||
|
||||
s.id.toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const selectedMarkdown = selectedId ? markdownCache.get(selectedId) ?? null : null;
|
||||
const coverageTotal =
|
||||
coverage !== null ? coverage.api.have + coverage.cli.have : null;
|
||||
const showGenerateButton = coverageTotal !== null && coverageTotal < 42;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{/* Concept card — full width */}
|
||||
<SkillsConceptCard variant="agent" />
|
||||
|
||||
{/* Header: coverage + MCP/A2A bar + generate button */}
|
||||
<div className="flex flex-col gap-3 rounded-xl border border-border bg-bg p-4">
|
||||
{/* Coverage */}
|
||||
<div>
|
||||
<div className="mb-2 flex items-center justify-between gap-2">
|
||||
<span className="text-xs font-semibold text-text-muted uppercase tracking-wide">
|
||||
{t("coverageLabel")}
|
||||
</span>
|
||||
{showGenerateButton && (
|
||||
<button
|
||||
onClick={() => void handleGenerate()}
|
||||
disabled={generatingSkills}
|
||||
className="flex items-center gap-1.5 rounded-lg border border-primary/30 bg-primary/5 px-3 py-1.5 text-xs font-medium text-primary hover:bg-primary/10 transition-colors disabled:opacity-60"
|
||||
data-testid="generate-button"
|
||||
>
|
||||
<span
|
||||
className={`material-symbols-outlined text-[14px] ${generatingSkills ? "animate-spin" : ""}`}
|
||||
>
|
||||
{generatingSkills ? "refresh" : "auto_fix_high"}
|
||||
</span>
|
||||
{generatingSkills ? t("regenerateRunning") : t("generateButton")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{coverage ? (
|
||||
<CoverageBar coverage={coverage} />
|
||||
) : (
|
||||
<CoverageBarSkeleton />
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* MCP + A2A links */}
|
||||
<McpA2aLinksBar />
|
||||
</div>
|
||||
|
||||
{/* Search + filters */}
|
||||
<div className="flex flex-col sm:flex-row gap-2">
|
||||
<div className="relative flex-1">
|
||||
<span className="absolute left-3 top-1/2 -translate-y-1/2 material-symbols-outlined text-[16px] text-text-muted pointer-events-none">
|
||||
search
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
placeholder={t("filters.searchPlaceholder")}
|
||||
className="w-full rounded-lg border border-border bg-bg py-2 pl-9 pr-3 text-sm text-text-main placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-primary/40"
|
||||
data-testid="search-input"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-1" role="group" aria-label={t("filters.category")}>
|
||||
{(["all", "api", "cli"] as FilterCategory[]).map((cat) => (
|
||||
<button
|
||||
key={cat}
|
||||
onClick={() => setFilter(cat)}
|
||||
data-testid={`filter-${cat}`}
|
||||
className={`rounded-lg px-3 py-2 text-xs font-medium transition-colors border ${
|
||||
filter === cat
|
||||
? "bg-primary/10 text-primary border-primary/30"
|
||||
: "bg-bg text-text-muted border-border hover:bg-bg-subtle hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{cat === "all"
|
||||
? t("filterAll")
|
||||
: cat === "api"
|
||||
? t("categoryApi")
|
||||
: t("categoryCli")}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Two-column grid: left = skill cards, right = preview */}
|
||||
<div className="grid grid-cols-12 gap-4" data-testid="skills-grid">
|
||||
{/* Left: skill cards list (col-span 7) */}
|
||||
<div
|
||||
className="col-span-12 lg:col-span-7 flex flex-col gap-2"
|
||||
data-testid="skills-list"
|
||||
>
|
||||
{loadingCatalog ? (
|
||||
Array.from({ length: 6 }).map((_, i) => <SkillCardSkeleton key={i} />)
|
||||
) : filteredSkills.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border p-8 text-center">
|
||||
<span className="material-symbols-outlined text-[32px] text-text-muted mb-3">
|
||||
search_off
|
||||
</span>
|
||||
<p className="text-sm text-text-muted">{t("noSkillsFound")}</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredSkills.map((skill) => (
|
||||
<SkillCard
|
||||
key={skill.id}
|
||||
skill={skill}
|
||||
selected={selectedId === skill.id}
|
||||
onClick={() => handleSelectCard(skill.id)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Right: preview pane (col-span 5) */}
|
||||
<div className="col-span-12 lg:col-span-5" data-testid="preview-column">
|
||||
<SkillPreviewPane
|
||||
skillId={selectedId}
|
||||
markdown={selectedMarkdown}
|
||||
loading={loadingPreview}
|
||||
onRefresh={selectedId ? handleRefreshPreview : undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default AgentSkillsPageClient;
|
||||
@@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { SkillCoverage } from "@/lib/agentSkills/types";
|
||||
|
||||
interface CoverageBarProps {
|
||||
coverage: SkillCoverage;
|
||||
}
|
||||
|
||||
function barColor(have: number, total: number): string {
|
||||
const pct = total > 0 ? have / total : 0;
|
||||
if (pct >= 1) return "bg-emerald-500";
|
||||
if (pct >= 0.75) return "bg-amber-400";
|
||||
return "bg-red-500";
|
||||
}
|
||||
|
||||
function trackColor(have: number, total: number): string {
|
||||
const pct = total > 0 ? have / total : 0;
|
||||
if (pct >= 1) return "bg-emerald-500/20";
|
||||
if (pct >= 0.75) return "bg-amber-400/20";
|
||||
return "bg-red-500/20";
|
||||
}
|
||||
|
||||
export function CoverageBar({ coverage }: CoverageBarProps): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
const { api, cli } = coverage;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 text-xs" data-testid="coverage-bar">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="font-medium text-text-muted shrink-0">
|
||||
{t("categoryApi")} {api.have}/{api.total}
|
||||
</span>
|
||||
<div
|
||||
className={`flex-1 h-2 rounded-full overflow-hidden ${trackColor(api.have, api.total)}`}
|
||||
>
|
||||
<div
|
||||
role="progressbar"
|
||||
aria-valuenow={api.have}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={api.total}
|
||||
aria-label={`${t("categoryApi")} ${api.have}/${api.total}`}
|
||||
className={`h-full rounded-full transition-all duration-500 ${barColor(api.have, api.total)}`}
|
||||
style={{ width: `${api.total > 0 ? (api.have / api.total) * 100 : 0}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="shrink-0 text-text-muted w-12 text-right">
|
||||
{api.total > 0 ? Math.round((api.have / api.total) * 100) : 0}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<span className="font-medium text-text-muted shrink-0">
|
||||
{t("categoryCli")} {cli.have}/{cli.total}
|
||||
</span>
|
||||
<div
|
||||
className={`flex-1 h-2 rounded-full overflow-hidden ${trackColor(cli.have, cli.total)}`}
|
||||
>
|
||||
<div
|
||||
role="progressbar"
|
||||
aria-valuenow={cli.have}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={cli.total}
|
||||
aria-label={`${t("categoryCli")} ${cli.have}/${cli.total}`}
|
||||
className={`h-full rounded-full transition-all duration-500 ${barColor(cli.have, cli.total)}`}
|
||||
style={{ width: `${cli.total > 0 ? (cli.have / cli.total) * 100 : 0}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="shrink-0 text-text-muted w-12 text-right">
|
||||
{cli.total > 0 ? Math.round((cli.have / cli.total) * 100) : 0}%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CoverageBar;
|
||||
@@ -0,0 +1,93 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useState, useSyncExternalStore } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// SSR-safe origin via useSyncExternalStore.
|
||||
// Server snapshot returns "" (avoids hydration mismatch).
|
||||
function useOrigin(): string {
|
||||
return useSyncExternalStore(
|
||||
() => () => {}, // no external subscription needed
|
||||
() => (typeof window !== "undefined" ? window.location.origin : ""),
|
||||
() => "", // server snapshot
|
||||
);
|
||||
}
|
||||
|
||||
interface LinkCardProps {
|
||||
label: string;
|
||||
url: string;
|
||||
icon: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
function LinkCard({ label, url, icon, prompt }: LinkCardProps): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = useCallback(async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
} catch {
|
||||
// clipboard not available — silently ignore
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
return (
|
||||
<div className="flex-1 flex items-start gap-3 rounded-lg border border-border bg-bg-subtle p-3 min-w-0">
|
||||
<div className="flex items-center justify-center size-8 rounded-lg bg-primary/10 shrink-0">
|
||||
<span className="material-symbols-outlined text-primary text-[16px]">{icon}</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between gap-1 mb-1">
|
||||
<span className="text-xs font-semibold text-text-main">{label}</span>
|
||||
<button
|
||||
onClick={() => void handleCopy()}
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors shrink-0 ${
|
||||
copied
|
||||
? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400"
|
||||
: "bg-bg text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
title={t("copyUrl")}
|
||||
aria-label={`${t("copyUrl")} ${label}`}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[11px]">
|
||||
{copied ? "check" : "content_copy"}
|
||||
</span>
|
||||
{copied ? "✓" : t("copyUrl")}
|
||||
</button>
|
||||
</div>
|
||||
<code className="block truncate text-[10px] font-mono text-text-muted">{url}</code>
|
||||
<p className="mt-1 text-[10px] text-text-muted leading-relaxed italic">{prompt}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function McpA2aLinksBar(): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
const origin = useOrigin();
|
||||
|
||||
const mcpUrl = origin ? `${origin}/api/mcp/sse` : "/api/mcp/sse";
|
||||
const a2aUrl = origin ? `${origin}/.well-known/agent.json` : "/.well-known/agent.json";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col sm:flex-row gap-2" data-testid="mcp-a2a-links-bar">
|
||||
<LinkCard
|
||||
label={t("mcpUrl")}
|
||||
url={mcpUrl}
|
||||
icon="electrical_services"
|
||||
prompt="Add this MCP endpoint to your agent to give it 37 OmniRoute tools."
|
||||
/>
|
||||
<LinkCard
|
||||
label={t("a2aLink")}
|
||||
url={a2aUrl}
|
||||
icon="hub"
|
||||
prompt="Register this Agent Card with your orchestrator to enable A2A task delegation."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default McpA2aLinksBar;
|
||||
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { AgentSkill } from "@/lib/agentSkills/types";
|
||||
|
||||
interface SkillCardProps {
|
||||
skill: AgentSkill;
|
||||
selected: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function SkillCard({ skill, selected, onClick }: SkillCardProps): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}
|
||||
},
|
||||
[onClick],
|
||||
);
|
||||
|
||||
const previewItems: string[] =
|
||||
skill.category === "api"
|
||||
? (skill.endpoints ?? []).slice(0, 2)
|
||||
: (skill.cliCommands ?? []).slice(0, 2);
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-pressed={selected}
|
||||
onClick={onClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
data-testid={`skill-card-${skill.id}`}
|
||||
className={`flex items-start gap-3 rounded-lg border p-3 cursor-pointer transition-colors outline-none focus-visible:ring-2 focus-visible:ring-primary/50 ${
|
||||
selected
|
||||
? "border-primary/40 bg-primary/5"
|
||||
: "border-border bg-bg hover:bg-bg-subtle hover:border-border"
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-lg ${
|
||||
selected ? "bg-primary/15" : "bg-bg-subtle"
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`material-symbols-outlined text-[18px] ${
|
||||
selected ? "text-primary" : "text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{skill.icon ?? "article"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-0.5 flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-sm font-semibold text-text-main">{skill.name}</span>
|
||||
|
||||
<span
|
||||
className={`rounded-full px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide ${
|
||||
skill.category === "api"
|
||||
? "bg-blue-500/10 text-blue-700 dark:text-blue-400"
|
||||
: "bg-violet-500/10 text-violet-700 dark:text-violet-400"
|
||||
}`}
|
||||
>
|
||||
{skill.category === "api" ? t("categoryApi") : t("categoryCli")}
|
||||
</span>
|
||||
|
||||
{skill.isEntry && (
|
||||
<span className="rounded-full bg-primary/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary">
|
||||
start
|
||||
</span>
|
||||
)}
|
||||
|
||||
{skill.isNew && (
|
||||
<span className="rounded-full bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-amber-700 dark:text-amber-400">
|
||||
new
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-xs leading-relaxed text-text-muted line-clamp-2">{skill.description}</p>
|
||||
|
||||
{previewItems.length > 0 && (
|
||||
<div className="mt-1.5 flex flex-wrap gap-1">
|
||||
{previewItems.map((item) => (
|
||||
<code
|
||||
key={item}
|
||||
className="rounded bg-bg-subtle px-1.5 py-0.5 font-mono text-[10px] text-text-muted border border-border/50"
|
||||
>
|
||||
{item}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SkillCard;
|
||||
@@ -0,0 +1,147 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// Lazy-load react-markdown to reduce initial bundle size.
|
||||
const ReactMarkdown = dynamic(() => import("react-markdown"), {
|
||||
loading: () => <SkeletonLines lines={6} />,
|
||||
});
|
||||
|
||||
// remark-gfm loaded lazily alongside ReactMarkdown via remarkPlugins prop.
|
||||
// We avoid rehype-raw to prevent XSS (Hard Rule #7 context).
|
||||
|
||||
interface SkillPreviewPaneProps {
|
||||
skillId: string | null;
|
||||
markdown: string | null;
|
||||
loading: boolean;
|
||||
onRefresh?: () => void;
|
||||
}
|
||||
|
||||
function SkeletonLines({ lines }: { lines: number }): JSX.Element {
|
||||
return (
|
||||
<div className="space-y-2 animate-pulse" aria-hidden="true">
|
||||
{Array.from({ length: lines }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-3 rounded bg-bg-subtle"
|
||||
style={{ width: `${60 + ((i * 17) % 40)}%` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SkillPreviewPane({
|
||||
skillId,
|
||||
markdown,
|
||||
loading,
|
||||
onRefresh,
|
||||
}: SkillPreviewPaneProps): JSX.Element {
|
||||
const t = useTranslations("agentSkills");
|
||||
|
||||
const handleCopyRawUrl = useCallback(async () => {
|
||||
if (!skillId) return;
|
||||
const rawUrl = `https://raw.githubusercontent.com/diegosouzapw/OmniRoute/refs/heads/main/skills/${skillId}/SKILL.md`;
|
||||
try {
|
||||
await navigator.clipboard.writeText(rawUrl);
|
||||
} catch {
|
||||
// clipboard not available — silently ignore
|
||||
}
|
||||
}, [skillId]);
|
||||
|
||||
const githubUrl = skillId
|
||||
? `https://github.com/diegosouzapw/OmniRoute/blob/main/skills/${skillId}/SKILL.md`
|
||||
: null;
|
||||
|
||||
// Empty state
|
||||
if (!skillId) {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col items-center justify-center h-full min-h-[300px] rounded-xl border border-dashed border-border bg-bg-subtle/30 p-8 text-center"
|
||||
data-testid="skill-preview-empty"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[32px] text-text-muted mb-3">
|
||||
article
|
||||
</span>
|
||||
<p className="text-sm text-text-muted">{t("previewEmpty")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col rounded-xl border border-border bg-bg h-full"
|
||||
data-testid="skill-preview-pane"
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between gap-2 border-b border-border px-4 py-2.5 shrink-0">
|
||||
<span className="text-xs font-mono font-semibold text-text-muted truncate">
|
||||
{skillId}/SKILL.md
|
||||
</span>
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
{onRefresh && (
|
||||
<button
|
||||
onClick={onRefresh}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-text-muted hover:text-text-main hover:bg-bg-subtle transition-colors disabled:opacity-50"
|
||||
aria-label="Refresh"
|
||||
>
|
||||
<span
|
||||
className={`material-symbols-outlined text-[14px] ${loading ? "animate-spin" : ""}`}
|
||||
>
|
||||
refresh
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => void handleCopyRawUrl()}
|
||||
disabled={!skillId}
|
||||
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-text-muted hover:text-text-main hover:bg-bg-subtle transition-colors"
|
||||
title={t("copyUrl")}
|
||||
aria-label={t("copyUrl")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">content_copy</span>
|
||||
</button>
|
||||
{githubUrl && (
|
||||
<a
|
||||
href={githubUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1 rounded px-2 py-1 text-xs text-text-muted hover:text-text-main hover:bg-bg-subtle transition-colors"
|
||||
title={t("viewOnGithub")}
|
||||
aria-label={t("viewOnGithub")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto p-4 min-h-0">
|
||||
{loading ? (
|
||||
<SkeletonLines lines={12} />
|
||||
) : markdown ? (
|
||||
<div
|
||||
className="prose prose-sm dark:prose-invert max-w-none text-text-main"
|
||||
data-testid="skill-preview-markdown"
|
||||
>
|
||||
<ReactMarkdown>{markdown}</ReactMarkdown>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="flex items-center gap-2 rounded-lg border border-red-300 bg-red-50 dark:bg-red-950/30 p-3 text-xs text-red-700 dark:text-red-400"
|
||||
data-testid="skill-preview-error"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]">error</span>
|
||||
{t("previewError")}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SkillPreviewPane;
|
||||
@@ -1,172 +1,5 @@
|
||||
"use client";
|
||||
import { AgentSkillsPageClient } from "./AgentSkillsPageClient";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
AGENT_SKILLS,
|
||||
AGENT_SKILLS_REPO_URL,
|
||||
getAgentSkillRawUrl,
|
||||
getAgentSkillBlobUrl,
|
||||
type AgentSkill,
|
||||
} from "@/shared/constants/agentSkills";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
|
||||
function CopyButton({ url }: { url: string }) {
|
||||
const t = useTranslations("agents");
|
||||
const { copied, copy } = useCopyToClipboard();
|
||||
const isCopied = copied === url;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => void copy(url, url)}
|
||||
className={`flex items-center gap-1 rounded px-2 py-1 text-xs font-medium transition-colors ${
|
||||
isCopied
|
||||
? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400"
|
||||
: "bg-bg-subtle text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
title={t("copyRawUrlTitle")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">
|
||||
{isCopied ? "check" : "content_copy"}
|
||||
</span>
|
||||
{isCopied ? t("copied") : t("copyUrl")}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function SkillRow({ skill }: { skill: AgentSkill }) {
|
||||
const t = useTranslations("agents");
|
||||
const rawUrl = getAgentSkillRawUrl(skill.id);
|
||||
const blobUrl = getAgentSkillBlobUrl(skill.id);
|
||||
|
||||
return (
|
||||
<div className="flex items-start gap-3 rounded-lg border border-border p-3 transition-colors hover:bg-bg-subtle">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-bg-subtle">
|
||||
<span className="material-symbols-outlined text-[18px] text-text-muted">{skill.icon}</span>
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-0.5 flex flex-wrap items-center gap-1.5">
|
||||
<span className="text-sm font-semibold text-text-main">{skill.name}</span>
|
||||
{skill.isEntry && (
|
||||
<span className="rounded-full bg-primary/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary">
|
||||
{t("startHere")}
|
||||
</span>
|
||||
)}
|
||||
{skill.isNew && (
|
||||
<span className="rounded-full bg-amber-500/10 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-amber-700 dark:text-amber-400">
|
||||
{t("badgeNew")}
|
||||
</span>
|
||||
)}
|
||||
{skill.endpoint && (
|
||||
<code className="rounded bg-bg-subtle px-1.5 py-0.5 font-mono text-[10px] text-text-muted">
|
||||
{skill.endpoint}
|
||||
</code>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs leading-relaxed text-text-muted">{skill.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<a
|
||||
href={blobUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center gap-1 rounded px-2 py-1 text-xs font-medium text-text-muted transition-colors hover:bg-bg-subtle hover:text-text-main"
|
||||
title={t("viewOnGithub")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
|
||||
</a>
|
||||
<CopyButton url={rawUrl} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SkillSection({
|
||||
title,
|
||||
subtitle,
|
||||
icon,
|
||||
skills,
|
||||
}: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
icon: string;
|
||||
skills: AgentSkill[];
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-3 rounded-xl border border-border bg-bg p-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="material-symbols-outlined text-[20px] text-text-muted">{icon}</span>
|
||||
<div>
|
||||
<h2 className="text-sm font-semibold text-text-main">{title}</h2>
|
||||
<p className="text-xs text-text-muted">{subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{skills.map((skill) => (
|
||||
<SkillRow key={skill.id} skill={skill} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AgentSkillsPage() {
|
||||
const t = useTranslations("agents");
|
||||
const apiSkills = AGENT_SKILLS.filter((s) => s.category === "api");
|
||||
const cliSkills = AGENT_SKILLS.filter((s) => s.category === "cli");
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* How to use — full width */}
|
||||
<div className="rounded-xl border border-border bg-bg-subtle/50 p-4">
|
||||
<div className="mb-2 flex items-center gap-2">
|
||||
<span className="material-symbols-outlined text-[18px] text-primary">info</span>
|
||||
<span className="text-sm font-semibold text-text-main">{t("howToUse")}</span>
|
||||
</div>
|
||||
<ol className="space-y-1 text-xs text-text-muted">
|
||||
<li>
|
||||
1.{" "}
|
||||
{t.rich("howToUseStep1", {
|
||||
copyUrl: t("copyUrl"),
|
||||
bold: (chunks) => <strong className="text-text-main">{chunks}</strong>,
|
||||
})}
|
||||
</li>
|
||||
<li>
|
||||
2. {t("howToUseStep2")}
|
||||
<br />
|
||||
<code className="mt-1 block rounded border border-border bg-bg px-2 py-1 font-mono text-[11px]">
|
||||
{t("howToUseStep2Code")}
|
||||
</code>
|
||||
</li>
|
||||
<li>3. {t("howToUseStep3")}</li>
|
||||
</ol>
|
||||
<a
|
||||
href={AGENT_SKILLS_REPO_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="mt-3 inline-flex items-center gap-1 text-xs font-medium text-primary hover:underline"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
|
||||
{t("browseAllSkillsOnGithub")}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Two-column grid: API Skills | CLI Skills */}
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<SkillSection
|
||||
title={t("apiSkills")}
|
||||
subtitle={t("apiSkillsSubtitle", { count: apiSkills.length })}
|
||||
icon="api"
|
||||
skills={apiSkills}
|
||||
/>
|
||||
<SkillSection
|
||||
title={t("cliSkills")}
|
||||
subtitle={t("cliSkillsSubtitle", { count: cliSkills.length })}
|
||||
icon="terminal"
|
||||
skills={cliSkills}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function Page() {
|
||||
return <AgentSkillsPageClient />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card } from "@/shared/components";
|
||||
import { SkillsConceptCard } from "@/shared/components";
|
||||
import type { SkillsProvider } from "@/lib/skills/providerSettings";
|
||||
import { OmniSkillsList } from "./components/OmniSkillsList";
|
||||
import { OmniExecutionsTab } from "./components/OmniExecutionsTab";
|
||||
import { OmniSandboxTab } from "./components/OmniSandboxTab";
|
||||
import { OmniMarketplaceTab } from "./components/OmniMarketplaceTab";
|
||||
import type { OmniSkill } from "./components/OmniSkillCard";
|
||||
|
||||
interface Execution {
|
||||
id: string;
|
||||
skillId: string;
|
||||
skillName: string;
|
||||
status: string;
|
||||
duration: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function OmniSkillsPageClient(): JSX.Element {
|
||||
const [skills, setSkills] = useState<OmniSkill[]>([]);
|
||||
const [executions, setExecutions] = useState<Execution[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [skillsPage, setSkillsPage] = useState(1);
|
||||
const [skillsTotal, setSkillsTotal] = useState(0);
|
||||
const [skillsTotalPages, setSkillsTotalPages] = useState(1);
|
||||
const [popularDefaults, setPopularDefaults] = useState<string[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [modeFilter, setModeFilter] = useState<"all" | "on" | "off" | "auto">("all");
|
||||
const [selectedSkillId, setSelectedSkillId] = useState<string | null>(null);
|
||||
|
||||
const [execPage, setExecPage] = useState(1);
|
||||
const [execTotal, setExecTotal] = useState(0);
|
||||
const [execTotalPages, setExecTotalPages] = useState(1);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<"skills" | "executions" | "sandbox" | "marketplace">(
|
||||
"skills"
|
||||
);
|
||||
const [showInstallModal, setShowInstallModal] = useState(false);
|
||||
const [installJson, setInstallJson] = useState("");
|
||||
const [installStatus, setInstallStatus] = useState<{
|
||||
type: "success" | "error";
|
||||
message: string;
|
||||
} | null>(null);
|
||||
const [installing, setInstalling] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [skillsProvider, setSkillsProvider] = useState<SkillsProvider>("skillsmp");
|
||||
|
||||
const t = useTranslations("skills");
|
||||
// NOTE: commonT is intentionally unused here but retained for potential future use
|
||||
useTranslations("common");
|
||||
|
||||
const fetchSkills = async (page: number) => {
|
||||
const params = new URLSearchParams({ page: String(page), limit: "20" });
|
||||
if (searchTerm.trim()) params.set("q", searchTerm.trim());
|
||||
if (modeFilter !== "all") params.set("mode", modeFilter);
|
||||
|
||||
const res = await fetch(`/api/skills?${params.toString()}`).then((r) => r.json());
|
||||
setSkills((res as { data?: OmniSkill[] }).data || []);
|
||||
setSkillsTotal((res as { total?: number }).total || 0);
|
||||
setSkillsTotalPages((res as { totalPages?: number }).totalPages || 1);
|
||||
setPopularDefaults(
|
||||
Array.isArray((res as { popularDefaults?: string[] }).popularDefaults)
|
||||
? (res as { popularDefaults: string[] }).popularDefaults
|
||||
: []
|
||||
);
|
||||
};
|
||||
|
||||
const fetchExecutions = async (page: number) => {
|
||||
const res = await fetch(`/api/skills/executions?page=${page}&limit=20`).then((r) => r.json());
|
||||
setExecutions((res as { data?: Execution[] }).data || []);
|
||||
setExecTotal((res as { total?: number }).total || 0);
|
||||
setExecTotalPages((res as { totalPages?: number }).totalPages || 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetch("/api/skills?page=1&limit=20").then((r) => r.json()),
|
||||
fetch("/api/skills/executions?page=1&limit=20").then((r) => r.json()),
|
||||
fetch("/api/settings").then((r) => (r.ok ? r.json() : null)),
|
||||
])
|
||||
.then(([skillsData, executionsData, settingsData]) => {
|
||||
setSkills((skillsData as { data?: OmniSkill[] }).data || []);
|
||||
setSkillsTotal((skillsData as { total?: number }).total || 0);
|
||||
setSkillsTotalPages((skillsData as { totalPages?: number }).totalPages || 1);
|
||||
setPopularDefaults(
|
||||
Array.isArray((skillsData as { popularDefaults?: string[] }).popularDefaults)
|
||||
? (skillsData as { popularDefaults: string[] }).popularDefaults
|
||||
: []
|
||||
);
|
||||
|
||||
setExecutions((executionsData as { data?: Execution[] }).data || []);
|
||||
setExecTotal((executionsData as { total?: number }).total || 0);
|
||||
setExecTotalPages((executionsData as { totalPages?: number }).totalPages || 1);
|
||||
|
||||
const sd = settingsData as { skillsProvider?: SkillsProvider } | null;
|
||||
if (sd?.skillsProvider === "skillsmp" || sd?.skillsProvider === "skillssh") {
|
||||
setSkillsProvider(sd.skillsProvider);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const refreshSkills = async () => {
|
||||
setSkillsPage(1);
|
||||
await fetchSkills(1);
|
||||
};
|
||||
|
||||
const setSkillMode = async (skillId: string, mode: "on" | "off" | "auto") => {
|
||||
await fetch(`/api/skills/${skillId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ mode }),
|
||||
});
|
||||
setSkills(skills.map((s) => (s.id === skillId ? { ...s, mode, enabled: mode !== "off" } : s)));
|
||||
};
|
||||
|
||||
const deleteSkill = async (skillId: string) => {
|
||||
const res = await fetch(`/api/skills/${skillId}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
setSkills(skills.filter((s) => s.id !== skillId));
|
||||
if (selectedSkillId === skillId) setSelectedSkillId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInstall = async () => {
|
||||
setInstalling(true);
|
||||
setInstallStatus(null);
|
||||
try {
|
||||
const manifest = JSON.parse(installJson) as Record<string, unknown>;
|
||||
const res = await fetch("/api/skills/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(manifest),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && (data as { success?: boolean }).success) {
|
||||
setInstallStatus({
|
||||
type: "success",
|
||||
message: t("installSuccess", { id: (data as { id?: string }).id || "" }),
|
||||
});
|
||||
setInstallJson("");
|
||||
await refreshSkills();
|
||||
} else {
|
||||
setInstallStatus({
|
||||
type: "error",
|
||||
message:
|
||||
(data as { error?: string; message?: string }).error ||
|
||||
(data as { error?: string; message?: string }).message ||
|
||||
t("installError"),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setInstallStatus({
|
||||
type: "error",
|
||||
message: err instanceof Error ? err.message : t("invalidJson"),
|
||||
});
|
||||
} finally {
|
||||
setInstalling(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => {
|
||||
setInstallJson((ev.target?.result as string) || "");
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-text-muted">{t("loading")}...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const enabledCount = skills.filter((s) => s.enabled).length;
|
||||
const execSuccessCount = executions.filter((e) => e.status === "success").length;
|
||||
const successRate =
|
||||
executions.length > 0 ? Math.round((execSuccessCount / executions.length) * 100) : 0;
|
||||
|
||||
const tabs: { id: "skills" | "executions" | "sandbox" | "marketplace"; labelKey: string }[] = [
|
||||
{ id: "skills", labelKey: "skillsTab" },
|
||||
{ id: "executions", labelKey: "executionsTab" },
|
||||
{ id: "sandbox", labelKey: "sandboxTab" },
|
||||
{ id: "marketplace", labelKey: "marketplaceTab" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Concept card */}
|
||||
<SkillsConceptCard variant="omni" />
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("totalSkills")}</p>
|
||||
<p className="text-2xl font-bold text-text-main mt-1">{skillsTotal}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("enabledSkills")}</p>
|
||||
<p className="text-2xl font-bold text-emerald-400 mt-1">{enabledCount}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">
|
||||
{t("totalExecutions")}
|
||||
</p>
|
||||
<p className="text-2xl font-bold text-violet-400 mt-1">{execTotal}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("successRate")}</p>
|
||||
<p className="text-2xl font-bold text-amber-400 mt-1">{successRate}%</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={() => setShowInstallModal(true)}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 transition-colors"
|
||||
>
|
||||
{t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab bar */}
|
||||
<div className="flex gap-2 border-b border-border">
|
||||
{tabs.map(({ id, labelKey }) => (
|
||||
<button
|
||||
key={id}
|
||||
onClick={() => setActiveTab(id)}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === id
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{t(labelKey)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab content */}
|
||||
{activeTab === "skills" && (
|
||||
<OmniSkillsList
|
||||
skills={skills}
|
||||
skillsTotal={skillsTotal}
|
||||
skillsPage={skillsPage}
|
||||
skillsTotalPages={skillsTotalPages}
|
||||
popularDefaults={popularDefaults}
|
||||
searchTerm={searchTerm}
|
||||
modeFilter={modeFilter}
|
||||
selectedSkillId={selectedSkillId}
|
||||
onSearchTermChange={setSearchTerm}
|
||||
onModeFilterChange={setModeFilter}
|
||||
onApplyFilters={() => {
|
||||
setSkillsPage(1);
|
||||
void fetchSkills(1);
|
||||
}}
|
||||
onPagePrev={() => {
|
||||
const p = Math.max(1, skillsPage - 1);
|
||||
setSkillsPage(p);
|
||||
void fetchSkills(p);
|
||||
}}
|
||||
onPageNext={() => {
|
||||
const p = Math.min(skillsTotalPages, skillsPage + 1);
|
||||
setSkillsPage(p);
|
||||
void fetchSkills(p);
|
||||
}}
|
||||
onSelectSkill={setSelectedSkillId}
|
||||
onSetMode={setSkillMode}
|
||||
onUninstall={deleteSkill}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeTab === "executions" && (
|
||||
<OmniExecutionsTab
|
||||
executions={executions}
|
||||
execPage={execPage}
|
||||
execTotalPages={execTotalPages}
|
||||
execTotal={execTotal}
|
||||
onPagePrev={() => {
|
||||
const p = Math.max(1, execPage - 1);
|
||||
setExecPage(p);
|
||||
void fetchExecutions(p);
|
||||
}}
|
||||
onPageNext={() => {
|
||||
const p = Math.min(execTotalPages, execPage + 1);
|
||||
setExecPage(p);
|
||||
void fetchExecutions(p);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activeTab === "sandbox" && <OmniSandboxTab />}
|
||||
|
||||
{activeTab === "marketplace" && (
|
||||
<OmniMarketplaceTab skillsProvider={skillsProvider} onRefreshSkills={refreshSkills} />
|
||||
)}
|
||||
|
||||
{/* Install modal */}
|
||||
{showInstallModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="bg-surface border border-border rounded-xl p-6 w-full max-w-lg mx-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold">{t("installSkillModalTitle")}</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowInstallModal(false);
|
||||
setInstallStatus(null);
|
||||
setInstallJson("");
|
||||
}}
|
||||
className="text-text-muted hover:text-text-main"
|
||||
>
|
||||
X
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm text-text-muted mb-4">{t("installSkillModalDesc")}</p>
|
||||
<textarea
|
||||
value={installJson}
|
||||
onChange={(e) => setInstallJson(e.target.value)}
|
||||
placeholder={t("installJsonPlaceholder")}
|
||||
className="w-full h-48 p-3 rounded-lg bg-background border border-border text-sm font-mono resize-none focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<div className="flex items-center gap-3 mt-3">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".json"
|
||||
onChange={handleFileUpload}
|
||||
className="hidden"
|
||||
/>
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-border text-text-muted hover:text-text-main transition-colors"
|
||||
>
|
||||
{t("uploadJson")}
|
||||
</button>
|
||||
<div className="flex-1" />
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowInstallModal(false);
|
||||
setInstallStatus(null);
|
||||
setInstallJson("");
|
||||
}}
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-border text-text-muted hover:text-text-main transition-colors"
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleInstall}
|
||||
disabled={installing || !installJson.trim()}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{installing ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
{installStatus && (
|
||||
<div
|
||||
className={`mt-3 p-3 rounded-lg text-sm ${
|
||||
installStatus.type === "success"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: "bg-red-500/10 text-red-400"
|
||||
}`}
|
||||
>
|
||||
{installStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniSkillsPageClient;
|
||||
@@ -0,0 +1,106 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card } from "@/shared/components";
|
||||
|
||||
interface Execution {
|
||||
id: string;
|
||||
skillId: string;
|
||||
skillName: string;
|
||||
status: string;
|
||||
duration: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface OmniExecutionsTabProps {
|
||||
executions: Execution[];
|
||||
execPage: number;
|
||||
execTotalPages: number;
|
||||
execTotal: number;
|
||||
onPagePrev: () => void;
|
||||
onPageNext: () => void;
|
||||
}
|
||||
|
||||
export function OmniExecutionsTab({
|
||||
executions,
|
||||
execPage,
|
||||
execTotalPages,
|
||||
execTotal,
|
||||
onPagePrev,
|
||||
onPageNext,
|
||||
}: OmniExecutionsTabProps): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="text-left text-sm text-text-muted border-b border-border">
|
||||
<th className="pb-3 font-medium">{t("skill")}</th>
|
||||
<th className="pb-3 font-medium">{t("status")}</th>
|
||||
<th className="pb-3 font-medium">{t("duration")}</th>
|
||||
<th className="pb-3 font-medium">{t("time")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{executions.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} className="py-8 text-center text-text-muted">
|
||||
{t("noExecutions")}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
executions.map((exec) => (
|
||||
<tr key={exec.id} className="border-b border-border/50">
|
||||
<td className="py-3 font-medium">{exec.skillName}</td>
|
||||
<td className="py-3">
|
||||
<span
|
||||
className={`text-xs px-2 py-1 rounded ${
|
||||
exec.status === "success"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: exec.status === "error"
|
||||
? "bg-red-500/10 text-red-400"
|
||||
: "bg-amber-500/10 text-amber-400"
|
||||
}`}
|
||||
>
|
||||
{exec.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-3 text-text-muted">{exec.duration}ms</td>
|
||||
<td className="py-3 text-text-muted text-sm">
|
||||
{new Date(exec.createdAt).toLocaleString()}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-4 pt-4 border-t border-border">
|
||||
<span className="text-sm text-text-muted">
|
||||
{t("pageInfo", { page: execPage, totalPages: execTotalPages, total: execTotal }) ||
|
||||
`Page ${execPage} of ${execTotalPages} (${execTotal} total)`}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={onPagePrev}
|
||||
disabled={execPage === 1}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("previous") || "Prev"}
|
||||
</button>
|
||||
<button
|
||||
onClick={onPageNext}
|
||||
disabled={execPage === execTotalPages || execTotalPages === 0}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("next") || "Next"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniExecutionsTab;
|
||||
@@ -0,0 +1,244 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card } from "@/shared/components";
|
||||
import type { SkillsProvider } from "@/lib/skills/providerSettings";
|
||||
|
||||
interface MarketplaceSkill {
|
||||
name: string;
|
||||
description: string;
|
||||
skillMdContent?: string;
|
||||
version?: string;
|
||||
sourceUrl?: string;
|
||||
}
|
||||
|
||||
interface SkillsShSkill {
|
||||
id: string;
|
||||
skillId: string;
|
||||
name: string;
|
||||
installs: number;
|
||||
source: string;
|
||||
}
|
||||
|
||||
interface OmniMarketplaceTabProps {
|
||||
skillsProvider: SkillsProvider;
|
||||
onRefreshSkills: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function OmniMarketplaceTab({
|
||||
skillsProvider,
|
||||
onRefreshSkills,
|
||||
}: OmniMarketplaceTabProps): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
const [mpQuery, setMpQuery] = useState("");
|
||||
const [mpResults, setMpResults] = useState<MarketplaceSkill[]>([]);
|
||||
const [mpLoading, setMpLoading] = useState(false);
|
||||
const [mpError, setMpError] = useState("");
|
||||
const [mpInstallingId, setMpInstallingId] = useState<string | null>(null);
|
||||
const [shQuery, setShQuery] = useState("");
|
||||
const [shResults, setShResults] = useState<SkillsShSkill[]>([]);
|
||||
const [shLoading, setShLoading] = useState(false);
|
||||
const [shError, setShError] = useState("");
|
||||
const [shInstallingId, setShInstallingId] = useState<string | null>(null);
|
||||
|
||||
const searchMarketplace = async () => {
|
||||
setMpLoading(true);
|
||||
setMpError("");
|
||||
setMpResults([]);
|
||||
try {
|
||||
const res = await fetch(`/api/skills/marketplace?q=${encodeURIComponent(mpQuery)}`);
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setMpError((data as { error?: string }).error || t("marketplaceError"));
|
||||
} else {
|
||||
setMpResults(Array.isArray(data) ? data : (data as { skills?: MarketplaceSkill[] }).skills || []);
|
||||
}
|
||||
} catch (err) {
|
||||
setMpError(err instanceof Error ? err.message : t("marketplaceError"));
|
||||
} finally {
|
||||
setMpLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const installFromMarketplace = async (skill: MarketplaceSkill) => {
|
||||
setMpInstallingId(skill.name);
|
||||
try {
|
||||
const res = await fetch("/api/skills/marketplace/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
skillMdContent: skill.skillMdContent || skill.description,
|
||||
version: skill.version || "1.0.0",
|
||||
sourceUrl: skill.sourceUrl,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && (data as { success?: boolean }).success) {
|
||||
await onRefreshSkills();
|
||||
setMpInstallingId(null);
|
||||
} else {
|
||||
setMpError((data as { error?: string }).error || t("installError"));
|
||||
setMpInstallingId(null);
|
||||
}
|
||||
} catch (err) {
|
||||
setMpError(err instanceof Error ? err.message : t("installError"));
|
||||
setMpInstallingId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const searchSkillsSh = async () => {
|
||||
setShLoading(true);
|
||||
setShError("");
|
||||
setShResults([]);
|
||||
try {
|
||||
const res = await fetch(`/api/skills/skillssh?q=${encodeURIComponent(shQuery)}`);
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setShError((data as { error?: string }).error || t("marketplaceError"));
|
||||
} else {
|
||||
setShResults((data as { skills?: SkillsShSkill[] }).skills || []);
|
||||
}
|
||||
} catch (err) {
|
||||
setShError(err instanceof Error ? err.message : t("marketplaceError"));
|
||||
} finally {
|
||||
setShLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const installFromSkillsSh = async (skill: SkillsShSkill) => {
|
||||
setShInstallingId(skill.id);
|
||||
try {
|
||||
const res = await fetch("/api/skills/skillssh/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: skill.name,
|
||||
description: `Installed from skills.sh (${skill.source})`,
|
||||
source: skill.source,
|
||||
skillId: skill.skillId,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && (data as { success?: boolean }).success) {
|
||||
await onRefreshSkills();
|
||||
setShInstallingId(null);
|
||||
} else {
|
||||
setShError((data as { error?: string }).error || t("installError"));
|
||||
setShInstallingId(null);
|
||||
}
|
||||
} catch (err) {
|
||||
setShError(err instanceof Error ? err.message : t("installError"));
|
||||
setShInstallingId(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<h3 className="font-semibold mb-2">{t("skillsMarketplace")}</h3>
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
{t("activeProvider")}{" "}
|
||||
<span className="font-medium">
|
||||
{skillsProvider === "skillsmp" ? "SkillsMP" : "skills.sh"}
|
||||
</span>
|
||||
. {t("changeInSettings")}
|
||||
</p>
|
||||
<div className="flex gap-2 mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={skillsProvider === "skillsmp" ? mpQuery : shQuery}
|
||||
onChange={(e) =>
|
||||
skillsProvider === "skillsmp" ? setMpQuery(e.target.value) : setShQuery(e.target.value)
|
||||
}
|
||||
onKeyDown={(e) =>
|
||||
e.key === "Enter" &&
|
||||
(skillsProvider === "skillsmp" ? searchMarketplace() : searchSkillsSh())
|
||||
}
|
||||
placeholder={t("searchMarketplacePlaceholder")}
|
||||
className="flex-1 px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<button
|
||||
onClick={() => (skillsProvider === "skillsmp" ? searchMarketplace() : searchSkillsSh())}
|
||||
disabled={skillsProvider === "skillsmp" ? mpLoading : shLoading}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{skillsProvider === "skillsmp"
|
||||
? mpLoading
|
||||
? t("searching")
|
||||
: t("searchMarketplace")
|
||||
: shLoading
|
||||
? t("searching")
|
||||
: t("searchMarketplace")}
|
||||
</button>
|
||||
</div>
|
||||
{(skillsProvider === "skillsmp" ? mpError : shError) && (
|
||||
<div className="p-3 rounded-lg bg-red-500/10 text-red-400 text-sm mb-4">
|
||||
{skillsProvider === "skillsmp" ? mpError : shError}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{skillsProvider === "skillsmp" && mpResults.length > 0 && (
|
||||
<div className="grid gap-3">
|
||||
{mpResults.map((skill) => (
|
||||
<Card key={skill.name}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold">{skill.name}</h4>
|
||||
<p className="text-sm text-text-muted mt-1">{skill.description}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installFromMarketplace(skill)}
|
||||
disabled={mpInstallingId === skill.name}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{mpInstallingId === skill.name ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{skillsProvider === "skillssh" && shResults.length > 0 && (
|
||||
<div className="grid gap-3">
|
||||
{shResults.map((skill) => (
|
||||
<Card key={skill.id}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold">{skill.name}</h4>
|
||||
<p className="text-sm text-text-muted mt-1">
|
||||
{skill.source} · {skill.installs.toLocaleString()} {t("installs")}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installFromSkillsSh(skill)}
|
||||
disabled={shInstallingId === skill.id}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{shInstallingId === skill.id ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{skillsProvider === "skillsmp" && !mpLoading && mpResults.length === 0 && !mpError && (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("marketplaceSkillsMpHint")}</div>
|
||||
</Card>
|
||||
)}
|
||||
{skillsProvider === "skillssh" && !shLoading && shResults.length === 0 && !shError && (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("marketplaceSkillsShHint")}</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniMarketplaceTab;
|
||||
@@ -0,0 +1,48 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card } from "@/shared/components";
|
||||
|
||||
export function OmniSandboxTab(): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
|
||||
return (
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<h3 className="font-semibold mb-4">{t("sandboxConfig")}</h3>
|
||||
<div className="grid gap-4">
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("cpuLimit")}</p>
|
||||
<p className="text-xs text-text-muted">{t("cpuLimitDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">100ms</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("memoryLimit")}</p>
|
||||
<p className="text-xs text-text-muted">{t("memoryLimitDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">256MB</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("timeout")}</p>
|
||||
<p className="text-xs text-text-muted">{t("timeoutDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">30s</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("networkAccess")}</p>
|
||||
<p className="text-xs text-text-muted">{t("networkAccessDesc")}</p>
|
||||
</div>
|
||||
<span className="text-text-muted">{t("disabled")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniSandboxTab;
|
||||
@@ -0,0 +1,98 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export interface OmniSkill {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
mode?: "on" | "off" | "auto";
|
||||
sourceProvider?: "skillsmp" | "skillssh" | "local";
|
||||
tags?: string[];
|
||||
installCount?: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface OmniSkillCardProps {
|
||||
skill: OmniSkill;
|
||||
selected: boolean;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export function OmniSkillCard({ skill, selected, onClick }: OmniSkillCardProps): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
const effectiveMode = skill.mode || (skill.enabled ? "on" : "off");
|
||||
|
||||
const modeColor =
|
||||
effectiveMode === "on"
|
||||
? "text-emerald-400"
|
||||
: effectiveMode === "auto"
|
||||
? "text-amber-400"
|
||||
: "text-text-muted";
|
||||
|
||||
const modeDot =
|
||||
effectiveMode === "on"
|
||||
? "bg-emerald-400"
|
||||
: effectiveMode === "auto"
|
||||
? "bg-amber-400"
|
||||
: "bg-border";
|
||||
|
||||
return (
|
||||
<button
|
||||
role="button"
|
||||
onClick={onClick}
|
||||
aria-pressed={selected}
|
||||
className={`w-full text-left rounded-lg border p-3 transition-all focus:outline-none focus:ring-2 focus:ring-violet-500/50 ${
|
||||
selected
|
||||
? "border-violet-500 bg-violet-500/5"
|
||||
: "border-border bg-surface/30 hover:border-border/80 hover:bg-surface/50"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-2">
|
||||
<div
|
||||
className={`flex items-center justify-center size-8 rounded-md shrink-0 ${
|
||||
selected ? "bg-violet-500/20" : "bg-surface/60"
|
||||
}`}
|
||||
>
|
||||
<span className="material-symbols-outlined text-[18px] text-text-muted">
|
||||
auto_fix_high
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5 flex-wrap">
|
||||
<span className="text-sm font-semibold text-text-main truncate">{skill.name}</span>
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded bg-surface/60 text-text-muted shrink-0">
|
||||
v{skill.version}
|
||||
</span>
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded bg-surface/60 text-text-muted shrink-0">
|
||||
{(skill.sourceProvider || "local").toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted mt-0.5 line-clamp-2">{skill.description}</p>
|
||||
{Array.isArray(skill.tags) && skill.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-1.5">
|
||||
{skill.tags.slice(0, 3).map((tag) => (
|
||||
<span
|
||||
key={`${skill.id}-${tag}`}
|
||||
className="text-[10px] px-1.5 py-0.5 rounded bg-violet-500/10 text-violet-300"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
<span className={`inline-block size-2 rounded-full ${modeDot}`} />
|
||||
<span className={`text-[10px] font-medium ${modeColor}`}>
|
||||
{t(effectiveMode === "on" ? "onMode" : effectiveMode === "auto" ? "autoMode" : "offMode")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniSkillCard;
|
||||
@@ -0,0 +1,159 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Card } from "@/shared/components";
|
||||
import { OmniSkillCard } from "./OmniSkillCard";
|
||||
import { SkillInspectorPane } from "./SkillInspectorPane";
|
||||
import type { OmniSkill } from "./OmniSkillCard";
|
||||
|
||||
interface OmniSkillsListProps {
|
||||
skills: OmniSkill[];
|
||||
skillsTotal: number;
|
||||
skillsPage: number;
|
||||
skillsTotalPages: number;
|
||||
popularDefaults: string[];
|
||||
searchTerm: string;
|
||||
modeFilter: "all" | "on" | "off" | "auto";
|
||||
selectedSkillId: string | null;
|
||||
onSearchTermChange: (v: string) => void;
|
||||
onModeFilterChange: (v: "all" | "on" | "off" | "auto") => void;
|
||||
onApplyFilters: () => void;
|
||||
onPagePrev: () => void;
|
||||
onPageNext: () => void;
|
||||
onSelectSkill: (id: string) => void;
|
||||
onSetMode: (skillId: string, mode: "on" | "off" | "auto") => void;
|
||||
onUninstall: (skillId: string) => void;
|
||||
}
|
||||
|
||||
export function OmniSkillsList({
|
||||
skills,
|
||||
skillsTotal,
|
||||
skillsPage,
|
||||
skillsTotalPages,
|
||||
popularDefaults,
|
||||
searchTerm,
|
||||
modeFilter,
|
||||
selectedSkillId,
|
||||
onSearchTermChange,
|
||||
onModeFilterChange,
|
||||
onApplyFilters,
|
||||
onPagePrev,
|
||||
onPageNext,
|
||||
onSelectSkill,
|
||||
onSetMode,
|
||||
onUninstall,
|
||||
}: OmniSkillsListProps): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
|
||||
const selectedSkill = selectedSkillId
|
||||
? (skills.find((s) => s.id === selectedSkillId) ?? null)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
{/* Left: grid + filters */}
|
||||
<div className="col-span-12 lg:col-span-7 flex flex-col gap-4">
|
||||
<Card>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 items-center">
|
||||
<input
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={(e) => onSearchTermChange(e.target.value)}
|
||||
placeholder={t("filterSkillsPlaceholder")}
|
||||
className="px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<select
|
||||
value={modeFilter}
|
||||
onChange={(e) => onModeFilterChange(e.target.value as "all" | "on" | "off" | "auto")}
|
||||
className="px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
>
|
||||
<option value="all">{t("allModes")}</option>
|
||||
<option value="on">{t("onMode")}</option>
|
||||
<option value="auto">{t("autoMode")}</option>
|
||||
<option value="off">{t("offMode")}</option>
|
||||
</select>
|
||||
<button
|
||||
onClick={onApplyFilters}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 transition-colors"
|
||||
>
|
||||
{t("applyFilters")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{popularDefaults.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<p className="text-xs text-text-muted mb-2">{t("popularDefaultsLabel")}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{popularDefaults.map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="text-xs px-2 py-1 rounded bg-violet-500/10 text-violet-300 border border-violet-500/20"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{skills.length === 0 ? (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("noSkills")}</div>
|
||||
</Card>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2">
|
||||
{skills.map((skill) => (
|
||||
<OmniSkillCard
|
||||
key={skill.id}
|
||||
skill={skill}
|
||||
selected={selectedSkillId === skill.id}
|
||||
onClick={() => onSelectSkill(skill.id)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center justify-between pt-2 border-t border-border">
|
||||
<span className="text-sm text-text-muted">
|
||||
{t("pageInfo", {
|
||||
page: skillsPage,
|
||||
totalPages: skillsTotalPages,
|
||||
total: skillsTotal,
|
||||
})}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={onPagePrev}
|
||||
disabled={skillsPage === 1}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("previous")}
|
||||
</button>
|
||||
<button
|
||||
onClick={onPageNext}
|
||||
disabled={skillsPage === skillsTotalPages || skillsTotalPages === 0}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: inspector pane */}
|
||||
<div className="col-span-12 lg:col-span-5">
|
||||
<div className="sticky top-4 rounded-xl border border-border bg-surface/30 overflow-hidden min-h-[400px]">
|
||||
<SkillInspectorPane
|
||||
selectedSkillId={selectedSkillId}
|
||||
skill={selectedSkill}
|
||||
onSetMode={onSetMode}
|
||||
onUninstall={onUninstall}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OmniSkillsList;
|
||||
@@ -0,0 +1,297 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { OmniSkill } from "./OmniSkillCard";
|
||||
|
||||
interface SkillDetail extends OmniSkill {
|
||||
schema?: {
|
||||
input?: Record<string, unknown>;
|
||||
output?: Record<string, unknown>;
|
||||
};
|
||||
handler?: string;
|
||||
}
|
||||
|
||||
interface SkillExecution {
|
||||
id: string;
|
||||
skillId: string;
|
||||
skillName: string;
|
||||
status: string;
|
||||
duration: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
type InspectorTab = "schema" | "handler" | "executions" | "sandbox";
|
||||
|
||||
interface SkillInspectorPaneProps {
|
||||
selectedSkillId: string | null;
|
||||
skill: OmniSkill | null;
|
||||
onSetMode: (skillId: string, mode: "on" | "off" | "auto") => void;
|
||||
onUninstall: (skillId: string) => void;
|
||||
}
|
||||
|
||||
export function SkillInspectorPane({
|
||||
selectedSkillId,
|
||||
skill,
|
||||
onSetMode,
|
||||
onUninstall,
|
||||
}: SkillInspectorPaneProps): JSX.Element {
|
||||
const t = useTranslations("skills");
|
||||
const [activeTab, setActiveTab] = useState<InspectorTab>("schema");
|
||||
const [detail, setDetail] = useState<SkillDetail | null>(null);
|
||||
const [executions, setExecutions] = useState<SkillExecution[]>([]);
|
||||
const [loadingExecs, setLoadingExecs] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
if (!selectedSkillId) {
|
||||
// Reset via microtask to avoid synchronous setState-in-effect
|
||||
Promise.resolve().then(() => {
|
||||
if (!cancelled) {
|
||||
setDetail(null);
|
||||
setExecutions([]);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}
|
||||
// Fetch full skill detail for schema + handler
|
||||
fetch(`/api/skills/${selectedSkillId}`)
|
||||
.then((r) => (r.ok ? r.json() : null))
|
||||
.then((data: SkillDetail | null) => {
|
||||
if (!cancelled && data) setDetail(data);
|
||||
})
|
||||
.catch(() => null);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [selectedSkillId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedSkillId || activeTab !== "executions") return;
|
||||
let cancelled = false;
|
||||
Promise.resolve().then(() => {
|
||||
if (!cancelled) setLoadingExecs(true);
|
||||
});
|
||||
fetch(`/api/skills/executions?skillId=${selectedSkillId}&limit=20`)
|
||||
.then((r) => (r.ok ? r.json() : { data: [] }))
|
||||
.then((data: { data?: SkillExecution[] }) => {
|
||||
if (!cancelled) {
|
||||
setExecutions(data.data || []);
|
||||
setLoadingExecs(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setLoadingExecs(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [selectedSkillId, activeTab]);
|
||||
|
||||
if (!selectedSkillId || !skill) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-full min-h-[300px] text-text-muted text-sm text-center p-6">
|
||||
<span className="material-symbols-outlined text-[40px] mb-3 opacity-30">
|
||||
manage_search
|
||||
</span>
|
||||
<span>Selecione uma skill à esquerda para inspecionar.</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const effectiveMode = skill.mode || (skill.enabled ? "on" : "off");
|
||||
|
||||
const tabs: { id: InspectorTab; label: string }[] = [
|
||||
{ id: "schema", label: "Schema" },
|
||||
{ id: "handler", label: "Handler" },
|
||||
{ id: "executions", label: t("executionsTab") },
|
||||
{ id: "sandbox", label: t("sandboxTab") },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Inspector header */}
|
||||
<div className="px-4 pt-4 pb-2 border-b border-border">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<span className="material-symbols-outlined text-[18px] text-violet-400">
|
||||
auto_fix_high
|
||||
</span>
|
||||
<h3 className="font-semibold text-text-main text-sm">{skill.name}</h3>
|
||||
</div>
|
||||
<p className="text-xs text-text-muted line-clamp-2">{skill.description}</p>
|
||||
<div className="flex items-center gap-1.5 mt-1.5">
|
||||
<span
|
||||
className={`text-[10px] px-1.5 py-0.5 rounded ${
|
||||
effectiveMode === "on"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: effectiveMode === "auto"
|
||||
? "bg-amber-500/10 text-amber-400"
|
||||
: "bg-surface/60 text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{t("mode")}: {effectiveMode}
|
||||
</span>
|
||||
<span className="text-[10px] px-1.5 py-0.5 rounded bg-surface/60 text-text-muted">
|
||||
{(skill.sourceProvider || "local").toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Inspector sub-tabs */}
|
||||
<div className="flex gap-0 border-b border-border px-2">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-3 py-2 text-xs font-medium border-b-2 transition-colors ${
|
||||
activeTab === tab.id
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Inspector content */}
|
||||
<div className="flex-1 overflow-auto p-4">
|
||||
{activeTab === "schema" && (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<p className="text-xs font-medium text-text-muted uppercase tracking-wide mb-1.5">
|
||||
Input Schema
|
||||
</p>
|
||||
<pre className="text-xs bg-surface/40 rounded-lg p-3 overflow-auto max-h-[200px] text-text-main font-mono whitespace-pre-wrap">
|
||||
{JSON.stringify(detail?.schema?.input ?? {}, null, 2) || "{}"}
|
||||
</pre>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-text-muted uppercase tracking-wide mb-1.5">
|
||||
Output Schema
|
||||
</p>
|
||||
<pre className="text-xs bg-surface/40 rounded-lg p-3 overflow-auto max-h-[200px] text-text-main font-mono whitespace-pre-wrap">
|
||||
{JSON.stringify(detail?.schema?.output ?? {}, null, 2) || "{}"}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "handler" && (
|
||||
<div>
|
||||
<p className="text-xs font-medium text-text-muted uppercase tracking-wide mb-1.5">
|
||||
Handler Code
|
||||
</p>
|
||||
<pre className="text-xs bg-surface/40 rounded-lg p-3 overflow-auto max-h-[400px] text-text-main font-mono whitespace-pre-wrap">
|
||||
{detail?.handler ?? "// Handler not available"}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "executions" && (
|
||||
<div>
|
||||
{loadingExecs ? (
|
||||
<div className="text-xs text-text-muted">{t("loading")}...</div>
|
||||
) : executions.length === 0 ? (
|
||||
<div className="text-xs text-text-muted text-center py-6">{t("noExecutions")}</div>
|
||||
) : (
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="text-left text-text-muted border-b border-border">
|
||||
<th className="pb-2 font-medium">{t("status")}</th>
|
||||
<th className="pb-2 font-medium">{t("duration")}</th>
|
||||
<th className="pb-2 font-medium">{t("time")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{executions.map((exec) => (
|
||||
<tr key={exec.id} className="border-b border-border/40">
|
||||
<td className="py-2">
|
||||
<span
|
||||
className={`px-1.5 py-0.5 rounded ${
|
||||
exec.status === "success"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: exec.status === "error"
|
||||
? "bg-red-500/10 text-red-400"
|
||||
: "bg-amber-500/10 text-amber-400"
|
||||
}`}
|
||||
>
|
||||
{exec.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-2 text-text-muted">{exec.duration}ms</td>
|
||||
<td className="py-2 text-text-muted">
|
||||
{new Date(exec.createdAt).toLocaleString()}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "sandbox" && (
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-medium text-text-muted uppercase tracking-wide mb-1.5">
|
||||
{t("sandboxConfig")}
|
||||
</p>
|
||||
{[
|
||||
{ label: t("cpuLimit"), desc: t("cpuLimitDesc"), value: "100ms" },
|
||||
{ label: t("memoryLimit"), desc: t("memoryLimitDesc"), value: "256MB" },
|
||||
{ label: t("timeout"), desc: t("timeoutDesc"), value: "30s" },
|
||||
{ label: t("networkAccess"), desc: t("networkAccessDesc"), value: t("disabled") },
|
||||
].map((item) => (
|
||||
<div
|
||||
key={item.label}
|
||||
className="flex items-center justify-between p-2.5 rounded-lg bg-surface/30"
|
||||
>
|
||||
<div>
|
||||
<p className="text-xs font-medium">{item.label}</p>
|
||||
<p className="text-[10px] text-text-muted">{item.desc}</p>
|
||||
</div>
|
||||
<span className="text-xs font-mono text-text-muted">{item.value}</span>
|
||||
</div>
|
||||
))}
|
||||
<button className="w-full mt-3 px-3 py-2 text-xs font-medium rounded-lg border border-border text-text-muted hover:text-text-main hover:border-violet-500/50 transition-colors">
|
||||
Run test (placeholder)
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Inspector action buttons */}
|
||||
<div className="px-4 py-3 border-t border-border flex items-center gap-1.5">
|
||||
{(["on", "auto", "off"] as const).map((mode) => (
|
||||
<button
|
||||
key={mode}
|
||||
onClick={() => onSetMode(skill.id, mode)}
|
||||
aria-label={`Set mode ${mode}`}
|
||||
className={`flex-1 text-xs px-2 py-1.5 rounded border transition-colors ${
|
||||
effectiveMode === mode
|
||||
? mode === "on"
|
||||
? "border-emerald-500 text-emerald-400 bg-emerald-500/5"
|
||||
: mode === "auto"
|
||||
? "border-amber-500 text-amber-400 bg-amber-500/5"
|
||||
: "border-red-500 text-red-400 bg-red-500/5"
|
||||
: "border-border text-text-muted hover:border-border/80"
|
||||
}`}
|
||||
>
|
||||
{mode === "on" ? t("onMode") : mode === "auto" ? t("autoMode") : t("offMode")}
|
||||
</button>
|
||||
))}
|
||||
<button
|
||||
onClick={() => onUninstall(skill.id)}
|
||||
aria-label="Uninstall skill"
|
||||
className="flex-1 text-xs px-2 py-1.5 rounded border border-border text-red-400 hover:bg-red-500/10 transition-colors"
|
||||
>
|
||||
{t("delete")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SkillInspectorPane;
|
||||
5
src/app/(dashboard)/dashboard/omni-skills/page.tsx
Normal file
5
src/app/(dashboard)/dashboard/omni-skills/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { OmniSkillsPageClient } from "./OmniSkillsPageClient";
|
||||
|
||||
export default function Page() {
|
||||
return <OmniSkillsPageClient />;
|
||||
}
|
||||
@@ -1,871 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Card } from "@/shared/components";
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { SkillsProvider } from "@/lib/skills/providerSettings";
|
||||
|
||||
interface Skill {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
mode?: "on" | "off" | "auto";
|
||||
sourceProvider?: "skillsmp" | "skillssh" | "local";
|
||||
tags?: string[];
|
||||
installCount?: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface Execution {
|
||||
id: string;
|
||||
skillId: string;
|
||||
skillName: string;
|
||||
status: string;
|
||||
duration: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export default function SkillsPage() {
|
||||
const [skills, setSkills] = useState<Skill[]>([]);
|
||||
const [executions, setExecutions] = useState<Execution[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [skillsPage, setSkillsPage] = useState(1);
|
||||
const [skillsTotal, setSkillsTotal] = useState(0);
|
||||
const [skillsTotalPages, setSkillsTotalPages] = useState(1);
|
||||
const [popularDefaults, setPopularDefaults] = useState<string[]>([]);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [modeFilter, setModeFilter] = useState<"all" | "on" | "off" | "auto">("all");
|
||||
|
||||
const [execPage, setExecPage] = useState(1);
|
||||
const [execTotal, setExecTotal] = useState(0);
|
||||
const [execTotalPages, setExecTotalPages] = useState(1);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<"skills" | "executions" | "sandbox" | "marketplace">(
|
||||
"skills"
|
||||
);
|
||||
const [showInstallModal, setShowInstallModal] = useState(false);
|
||||
const [installJson, setInstallJson] = useState("");
|
||||
const [installStatus, setInstallStatus] = useState<{
|
||||
type: "success" | "error";
|
||||
message: string;
|
||||
} | null>(null);
|
||||
const [installing, setInstalling] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [mpQuery, setMpQuery] = useState("");
|
||||
const [mpResults, setMpResults] = useState<
|
||||
{
|
||||
name: string;
|
||||
description: string;
|
||||
skillMdContent?: string;
|
||||
version?: string;
|
||||
sourceUrl?: string;
|
||||
}[]
|
||||
>([]);
|
||||
const [mpLoading, setMpLoading] = useState(false);
|
||||
const [mpError, setMpError] = useState("");
|
||||
const [mpInstallingId, setMpInstallingId] = useState<string | null>(null);
|
||||
const [shQuery, setShQuery] = useState("");
|
||||
const [shResults, setShResults] = useState<
|
||||
{ id: string; skillId: string; name: string; installs: number; source: string }[]
|
||||
>([]);
|
||||
const [shLoading, setShLoading] = useState(false);
|
||||
const [shError, setShError] = useState("");
|
||||
const [shInstallingId, setShInstallingId] = useState<string | null>(null);
|
||||
const [skillsProvider, setSkillsProvider] = useState<SkillsProvider>("skillsmp");
|
||||
const t = useTranslations("skills");
|
||||
const commonT = useTranslations("common");
|
||||
|
||||
const fetchSkills = async (page: number) => {
|
||||
const params = new URLSearchParams({ page: String(page), limit: "20" });
|
||||
if (searchTerm.trim()) params.set("q", searchTerm.trim());
|
||||
if (modeFilter !== "all") params.set("mode", modeFilter);
|
||||
|
||||
const res = await fetch(`/api/skills?${params.toString()}`).then((r) => r.json());
|
||||
setSkills(res.data || []);
|
||||
setSkillsTotal(res.total || 0);
|
||||
setSkillsTotalPages(res.totalPages || 1);
|
||||
setPopularDefaults(Array.isArray(res.popularDefaults) ? res.popularDefaults : []);
|
||||
};
|
||||
|
||||
const fetchExecutions = async (page: number) => {
|
||||
const res = await fetch(`/api/skills/executions?page=${page}&limit=20`).then((r) => r.json());
|
||||
setExecutions(res.data || []);
|
||||
setExecTotal(res.total || 0);
|
||||
setExecTotalPages(res.totalPages || 1);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetch("/api/skills?page=1&limit=20").then((r) => r.json()),
|
||||
fetch("/api/skills/executions?page=1&limit=20").then((r) => r.json()),
|
||||
fetch("/api/settings").then((r) => (r.ok ? r.json() : null)),
|
||||
])
|
||||
.then(([skillsData, executionsData, settingsData]) => {
|
||||
setSkills(skillsData.data || []);
|
||||
setSkillsTotal(skillsData.total || 0);
|
||||
setSkillsTotalPages(skillsData.totalPages || 1);
|
||||
setPopularDefaults(
|
||||
Array.isArray(skillsData.popularDefaults) ? skillsData.popularDefaults : []
|
||||
);
|
||||
|
||||
setExecutions(executionsData.data || []);
|
||||
setExecTotal(executionsData.total || 0);
|
||||
setExecTotalPages(executionsData.totalPages || 1);
|
||||
|
||||
if (
|
||||
settingsData?.skillsProvider === "skillsmp" ||
|
||||
settingsData?.skillsProvider === "skillssh"
|
||||
) {
|
||||
setSkillsProvider(settingsData.skillsProvider);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const refreshSkills = async () => {
|
||||
setSkillsPage(1);
|
||||
await fetchSkills(1);
|
||||
};
|
||||
|
||||
const toggleSkill = async (skillId: string, enabled: boolean) => {
|
||||
await fetch(`/api/skills/${skillId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ enabled: !enabled }),
|
||||
});
|
||||
setSkills(skills.map((s) => (s.id === skillId ? { ...s, enabled: !enabled } : s)));
|
||||
};
|
||||
|
||||
const setSkillMode = async (skillId: string, mode: "on" | "off" | "auto") => {
|
||||
await fetch(`/api/skills/${skillId}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ mode }),
|
||||
});
|
||||
|
||||
setSkills(skills.map((s) => (s.id === skillId ? { ...s, mode, enabled: mode !== "off" } : s)));
|
||||
};
|
||||
|
||||
const deleteSkill = async (skillId: string) => {
|
||||
const res = await fetch(`/api/skills/${skillId}`, { method: "DELETE" });
|
||||
if (res.ok) {
|
||||
setSkills(skills.filter((s) => s.id !== skillId));
|
||||
}
|
||||
};
|
||||
|
||||
const handleInstall = async () => {
|
||||
setInstalling(true);
|
||||
setInstallStatus(null);
|
||||
try {
|
||||
const manifest = JSON.parse(installJson);
|
||||
const res = await fetch("/api/skills/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(manifest),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.success) {
|
||||
setInstallStatus({ type: "success", message: t("installSuccess", { id: data.id }) });
|
||||
setInstallJson("");
|
||||
await refreshSkills();
|
||||
} else {
|
||||
setInstallStatus({
|
||||
type: "error",
|
||||
message: data.error || data.message || t("installError"),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setInstallStatus({
|
||||
type: "error",
|
||||
message: err instanceof Error ? err.message : t("invalidJson"),
|
||||
});
|
||||
} finally {
|
||||
setInstalling(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => {
|
||||
setInstallJson((ev.target?.result as string) || "");
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
|
||||
const searchMarketplace = async () => {
|
||||
setMpLoading(true);
|
||||
setMpError("");
|
||||
setMpResults([]);
|
||||
try {
|
||||
const res = await fetch(`/api/skills/marketplace?q=${encodeURIComponent(mpQuery)}`);
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setMpError(data.error || t("marketplaceError"));
|
||||
} else {
|
||||
setMpResults(Array.isArray(data) ? data : data.skills || []);
|
||||
}
|
||||
} catch (err) {
|
||||
setMpError(err instanceof Error ? err.message : t("marketplaceError"));
|
||||
} finally {
|
||||
setMpLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const installFromMarketplace = async (skill: {
|
||||
name: string;
|
||||
description: string;
|
||||
skillMdContent?: string;
|
||||
version?: string;
|
||||
sourceUrl?: string;
|
||||
}) => {
|
||||
setMpInstallingId(skill.name);
|
||||
try {
|
||||
const res = await fetch("/api/skills/marketplace/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: skill.name,
|
||||
description: skill.description,
|
||||
skillMdContent: skill.skillMdContent || skill.description,
|
||||
version: skill.version || "1.0.0",
|
||||
sourceUrl: skill.sourceUrl,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.success) {
|
||||
await refreshSkills();
|
||||
setMpInstallingId(null);
|
||||
} else {
|
||||
setMpError(data.error || t("installError"));
|
||||
setMpInstallingId(null);
|
||||
}
|
||||
} catch (err) {
|
||||
setMpError(err instanceof Error ? err.message : t("installError"));
|
||||
setMpInstallingId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const searchSkillsSh = async () => {
|
||||
setShLoading(true);
|
||||
setShError("");
|
||||
setShResults([]);
|
||||
try {
|
||||
const res = await fetch(`/api/skills/skillssh?q=${encodeURIComponent(shQuery)}`);
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setShError(data.error || t("marketplaceError"));
|
||||
} else {
|
||||
setShResults(data.skills || []);
|
||||
}
|
||||
} catch (err) {
|
||||
setShError(err instanceof Error ? err.message : t("marketplaceError"));
|
||||
} finally {
|
||||
setShLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const installFromSkillsSh = async (skill: {
|
||||
id: string;
|
||||
skillId: string;
|
||||
name: string;
|
||||
installs: number;
|
||||
source: string;
|
||||
}) => {
|
||||
setShInstallingId(skill.id);
|
||||
try {
|
||||
const res = await fetch("/api/skills/skillssh/install", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
name: skill.name,
|
||||
description: `Installed from skills.sh (${skill.source})`,
|
||||
source: skill.source,
|
||||
skillId: skill.skillId,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.success) {
|
||||
await refreshSkills();
|
||||
setShInstallingId(null);
|
||||
} else {
|
||||
setShError(data.error || t("installError"));
|
||||
setShInstallingId(null);
|
||||
}
|
||||
} catch (err) {
|
||||
setShError(err instanceof Error ? err.message : t("installError"));
|
||||
setShInstallingId(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-text-muted">{t("loading")}...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Stats computation ────────────────────────────────────────────────────
|
||||
|
||||
const enabledCount = skills.filter((s) => s.enabled).length;
|
||||
const execSuccessCount = executions.filter((e) => e.status === "success").length;
|
||||
const successRate =
|
||||
executions.length > 0 ? Math.round((execSuccessCount / executions.length) * 100) : 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* ── Stats Cards ─────────────────────────────────────────────────── */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("totalSkills")}</p>
|
||||
<p className="text-2xl font-bold text-text-main mt-1">{skillsTotal}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("enabledSkills")}</p>
|
||||
<p className="text-2xl font-bold text-emerald-400 mt-1">{enabledCount}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("totalExecutions")}</p>
|
||||
<p className="text-2xl font-bold text-violet-400 mt-1">{execTotal}</p>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<p className="text-xs text-text-muted uppercase tracking-wide">{t("successRate")}</p>
|
||||
<p className="text-2xl font-bold text-amber-400 mt-1">{successRate}%</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={() => setShowInstallModal(true)}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 transition-colors"
|
||||
>
|
||||
{t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 border-b border-border">
|
||||
<button
|
||||
onClick={() => setActiveTab("skills")}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === "skills"
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{t("skillsTab")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("executions")}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === "executions"
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{t("executionsTab")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("sandbox")}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === "sandbox"
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{t("sandboxTab")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab("marketplace")}
|
||||
className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${
|
||||
activeTab === "marketplace"
|
||||
? "border-violet-500 text-violet-400"
|
||||
: "border-transparent text-text-muted hover:text-text-main"
|
||||
}`}
|
||||
>
|
||||
{t("marketplaceTab")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{activeTab === "skills" && (
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-2 items-center">
|
||||
<input
|
||||
type="text"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
placeholder={t("filterSkillsPlaceholder")}
|
||||
className="px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<select
|
||||
value={modeFilter}
|
||||
onChange={(e) => setModeFilter(e.target.value as "all" | "on" | "off" | "auto")}
|
||||
className="px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
>
|
||||
<option value="all">{t("allModes")}</option>
|
||||
<option value="on">{t("onMode")}</option>
|
||||
<option value="auto">{t("autoMode")}</option>
|
||||
<option value="off">{t("offMode")}</option>
|
||||
</select>
|
||||
<button
|
||||
onClick={() => {
|
||||
setSkillsPage(1);
|
||||
void fetchSkills(1);
|
||||
}}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 transition-colors"
|
||||
>
|
||||
{t("applyFilters")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{popularDefaults.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<p className="text-xs text-text-muted mb-2">{t("popularDefaultsLabel")}</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{popularDefaults.map((name) => (
|
||||
<span
|
||||
key={name}
|
||||
className="text-xs px-2 py-1 rounded bg-violet-500/10 text-violet-300 border border-violet-500/20"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{skills.length === 0 ? (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("noSkills")}</div>
|
||||
</Card>
|
||||
) : (
|
||||
skills.map((skill) => (
|
||||
<Card key={skill.id}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold">{skill.name}</h3>
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-surface/50 text-text-muted">
|
||||
v{skill.version}
|
||||
</span>
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-surface/50 text-text-muted">
|
||||
{(skill.sourceProvider || "local").toUpperCase()}
|
||||
</span>
|
||||
<span className="text-xs px-2 py-0.5 rounded bg-amber-500/10 text-amber-400">
|
||||
{t("mode")}: {skill.mode || (skill.enabled ? "on" : "off")}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-text-muted mt-1">{skill.description}</p>
|
||||
{Array.isArray(skill.tags) && skill.tags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1 mt-2">
|
||||
{skill.tags.map((tag) => (
|
||||
<span
|
||||
key={`${skill.id}-${tag}`}
|
||||
className="text-[11px] px-1.5 py-0.5 rounded bg-surface/60 text-text-muted"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => setSkillMode(skill.id, "on")}
|
||||
className={`text-xs px-2 py-1 rounded border ${
|
||||
(skill.mode || (skill.enabled ? "on" : "off")) === "on"
|
||||
? "border-emerald-500 text-emerald-400"
|
||||
: "border-border text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{t("onMode")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSkillMode(skill.id, "auto")}
|
||||
className={`text-xs px-2 py-1 rounded border ${
|
||||
(skill.mode || (skill.enabled ? "on" : "off")) === "auto"
|
||||
? "border-amber-500 text-amber-400"
|
||||
: "border-border text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{t("autoMode")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSkillMode(skill.id, "off")}
|
||||
className={`text-xs px-2 py-1 rounded border ${
|
||||
(skill.mode || (skill.enabled ? "on" : "off")) === "off"
|
||||
? "border-red-500 text-red-400"
|
||||
: "border-border text-text-muted"
|
||||
}`}
|
||||
>
|
||||
{t("offMode")}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => deleteSkill(skill.id)}
|
||||
className="text-xs px-2 py-1 rounded text-red-400 hover:bg-red-500/10 transition-colors"
|
||||
>
|
||||
{t("delete")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => toggleSkill(skill.id, skill.enabled)}
|
||||
className={`relative w-11 h-6 rounded-full transition-colors ${
|
||||
skill.enabled ? "bg-violet-500" : "bg-border"
|
||||
}`}
|
||||
role="switch"
|
||||
aria-checked={skill.enabled}
|
||||
>
|
||||
<span
|
||||
className={`absolute top-1 left-1 w-4 h-4 bg-white rounded-full transition-transform ${
|
||||
skill.enabled ? "translate-x-5" : "translate-x-0"
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))
|
||||
)}
|
||||
<div className="flex items-center justify-between mt-4 pt-4 border-t border-border">
|
||||
<span className="text-sm text-text-muted">
|
||||
{t("pageInfo", {
|
||||
page: skillsPage,
|
||||
totalPages: skillsTotalPages,
|
||||
total: skillsTotal,
|
||||
})}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
const p = Math.max(1, skillsPage - 1);
|
||||
setSkillsPage(p);
|
||||
fetchSkills(p);
|
||||
}}
|
||||
disabled={skillsPage === 1}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("previous")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const p = Math.min(skillsTotalPages, skillsPage + 1);
|
||||
setSkillsPage(p);
|
||||
fetchSkills(p);
|
||||
}}
|
||||
disabled={skillsPage === skillsTotalPages || skillsTotalPages === 0}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("next")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "executions" && (
|
||||
<Card>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="text-left text-sm text-text-muted border-b border-border">
|
||||
<th className="pb-3 font-medium">{t("skill")}</th>
|
||||
<th className="pb-3 font-medium">{t("status")}</th>
|
||||
<th className="pb-3 font-medium">{t("duration")}</th>
|
||||
<th className="pb-3 font-medium">{t("time")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{executions.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={4} className="py-8 text-center text-text-muted">
|
||||
{t("noExecutions")}
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
executions.map((exec) => (
|
||||
<tr key={exec.id} className="border-b border-border/50">
|
||||
<td className="py-3 font-medium">{exec.skillName}</td>
|
||||
<td className="py-3">
|
||||
<span
|
||||
className={`text-xs px-2 py-1 rounded ${
|
||||
exec.status === "success"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: exec.status === "error"
|
||||
? "bg-red-500/10 text-red-400"
|
||||
: "bg-amber-500/10 text-amber-400"
|
||||
}`}
|
||||
>
|
||||
{exec.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="py-3 text-text-muted">{exec.duration}ms</td>
|
||||
<td className="py-3 text-text-muted text-sm">
|
||||
{new Date(exec.createdAt).toLocaleString()}
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex items-center justify-between mt-4 pt-4 border-t border-border">
|
||||
<span className="text-sm text-text-muted">
|
||||
{t("pageInfo", { page: execPage, totalPages: execTotalPages, total: execTotal }) ||
|
||||
`Page ${execPage} of ${execTotalPages} (${execTotal} total)`}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
const p = Math.max(1, execPage - 1);
|
||||
setExecPage(p);
|
||||
fetchExecutions(p);
|
||||
}}
|
||||
disabled={execPage === 1}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("previous") || "Prev"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const p = Math.min(execTotalPages, execPage + 1);
|
||||
setExecPage(p);
|
||||
fetchExecutions(p);
|
||||
}}
|
||||
disabled={execPage === execTotalPages || execTotalPages === 0}
|
||||
className="px-3 py-1 text-sm rounded border border-border text-text-muted hover:text-text-main disabled:opacity-40 transition-colors"
|
||||
>
|
||||
{t("next") || "Next"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{activeTab === "sandbox" && (
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<h3 className="font-semibold mb-4">{t("sandboxConfig")}</h3>
|
||||
<div className="grid gap-4">
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("cpuLimit")}</p>
|
||||
<p className="text-xs text-text-muted">{t("cpuLimitDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">100ms</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("memoryLimit")}</p>
|
||||
<p className="text-xs text-text-muted">{t("memoryLimitDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">256MB</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("timeout")}</p>
|
||||
<p className="text-xs text-text-muted">{t("timeoutDesc")}</p>
|
||||
</div>
|
||||
<span className="font-mono">30s</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 rounded-lg bg-surface/30">
|
||||
<div>
|
||||
<p className="font-medium">{t("networkAccess")}</p>
|
||||
<p className="text-xs text-text-muted">{t("networkAccessDesc")}</p>
|
||||
</div>
|
||||
<span className="text-text-muted">{t("disabled")}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === "marketplace" && (
|
||||
<div className="grid gap-4">
|
||||
<Card>
|
||||
<h3 className="font-semibold mb-2">{t("skillsMarketplace")}</h3>
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
{t("activeProvider")}{" "}
|
||||
<span className="font-medium">
|
||||
{skillsProvider === "skillsmp" ? "SkillsMP" : "skills.sh"}
|
||||
</span>
|
||||
. {t("changeInSettings")}
|
||||
</p>
|
||||
<div className="flex gap-2 mb-4">
|
||||
<input
|
||||
type="text"
|
||||
value={skillsProvider === "skillsmp" ? mpQuery : shQuery}
|
||||
onChange={(e) =>
|
||||
skillsProvider === "skillsmp"
|
||||
? setMpQuery(e.target.value)
|
||||
: setShQuery(e.target.value)
|
||||
}
|
||||
onKeyDown={(e) =>
|
||||
e.key === "Enter" &&
|
||||
(skillsProvider === "skillsmp" ? searchMarketplace() : searchSkillsSh())
|
||||
}
|
||||
placeholder={t("searchMarketplacePlaceholder")}
|
||||
className="flex-1 px-3 py-2 rounded-lg bg-background border border-border text-sm focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<button
|
||||
onClick={() =>
|
||||
skillsProvider === "skillsmp" ? searchMarketplace() : searchSkillsSh()
|
||||
}
|
||||
disabled={skillsProvider === "skillsmp" ? mpLoading : shLoading}
|
||||
className="px-4 py-2 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{skillsProvider === "skillsmp"
|
||||
? mpLoading
|
||||
? t("searching")
|
||||
: t("searchMarketplace")
|
||||
: shLoading
|
||||
? t("searching")
|
||||
: t("searchMarketplace")}
|
||||
</button>
|
||||
</div>
|
||||
{(skillsProvider === "skillsmp" ? mpError : shError) && (
|
||||
<div className="p-3 rounded-lg bg-red-500/10 text-red-400 text-sm mb-4">
|
||||
{skillsProvider === "skillsmp" ? mpError : shError}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{skillsProvider === "skillsmp" && mpResults.length > 0 && (
|
||||
<div className="grid gap-3">
|
||||
{mpResults.map((skill) => (
|
||||
<Card key={skill.name}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold">{skill.name}</h4>
|
||||
<p className="text-sm text-text-muted mt-1">{skill.description}</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installFromMarketplace(skill)}
|
||||
disabled={mpInstallingId === skill.name}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{mpInstallingId === skill.name ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{skillsProvider === "skillssh" && shResults.length > 0 && (
|
||||
<div className="grid gap-3">
|
||||
{shResults.map((skill) => (
|
||||
<Card key={skill.id}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h4 className="font-semibold">{skill.name}</h4>
|
||||
<p className="text-sm text-text-muted mt-1">
|
||||
{skill.source} · {skill.installs.toLocaleString()} {t("installs")}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => installFromSkillsSh(skill)}
|
||||
disabled={shInstallingId === skill.id}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{shInstallingId === skill.id ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{skillsProvider === "skillsmp" && !mpLoading && mpResults.length === 0 && !mpError && (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("marketplaceSkillsMpHint")}</div>
|
||||
</Card>
|
||||
)}
|
||||
{skillsProvider === "skillssh" && !shLoading && shResults.length === 0 && !shError && (
|
||||
<Card>
|
||||
<div className="text-center py-8 text-text-muted">{t("marketplaceSkillsShHint")}</div>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showInstallModal && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="bg-surface border border-border rounded-xl p-6 w-full max-w-lg mx-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-lg font-semibold">{t("installSkillModalTitle")}</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowInstallModal(false);
|
||||
setInstallStatus(null);
|
||||
setInstallJson("");
|
||||
}}
|
||||
className="text-text-muted hover:text-text-main"
|
||||
>
|
||||
X
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-sm text-text-muted mb-4">{t("installSkillModalDesc")}</p>
|
||||
<textarea
|
||||
value={installJson}
|
||||
onChange={(e) => setInstallJson(e.target.value)}
|
||||
placeholder={t("installJsonPlaceholder")}
|
||||
className="w-full h-48 p-3 rounded-lg bg-background border border-border text-sm font-mono resize-none focus:outline-none focus:ring-1 focus:ring-violet-500"
|
||||
/>
|
||||
<div className="flex items-center gap-3 mt-3">
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
accept=".json"
|
||||
onChange={handleFileUpload}
|
||||
className="hidden"
|
||||
/>
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-border text-text-muted hover:text-text-main transition-colors"
|
||||
>
|
||||
{t("uploadJson")}
|
||||
</button>
|
||||
<div className="flex-1" />
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowInstallModal(false);
|
||||
setInstallStatus(null);
|
||||
setInstallJson("");
|
||||
}}
|
||||
className="px-3 py-1.5 text-sm rounded-lg border border-border text-text-muted hover:text-text-main transition-colors"
|
||||
>
|
||||
{t("cancel")}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleInstall}
|
||||
disabled={installing || !installJson.trim()}
|
||||
className="px-4 py-1.5 text-sm font-medium rounded-lg bg-violet-500 text-white hover:bg-violet-600 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{installing ? t("installing") : t("installSkillButton")}
|
||||
</button>
|
||||
</div>
|
||||
{installStatus && (
|
||||
<div
|
||||
className={`mt-3 p-3 rounded-lg text-sm ${
|
||||
installStatus.type === "success"
|
||||
? "bg-emerald-500/10 text-emerald-400"
|
||||
: "bg-red-500/10 text-red-400"
|
||||
}`}
|
||||
>
|
||||
{installStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -102,6 +102,15 @@ export async function GET() {
|
||||
"Summarize active rate limits and lockouts",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "list-capabilities",
|
||||
name: "List Capabilities",
|
||||
description:
|
||||
"Returns the full catalog of 42 OmniRoute agent skills (22 API + 20 CLI) " +
|
||||
"with raw URLs for the SKILL.md docs.",
|
||||
tags: ["discovery", "capabilities"],
|
||||
examples: ["What can you do?", "List your skills", "Show capabilities"],
|
||||
},
|
||||
],
|
||||
authentication: {
|
||||
schemes: ["api-key"],
|
||||
|
||||
73
src/app/api/agent-skills/[id]/raw/route.ts
Normal file
73
src/app/api/agent-skills/[id]/raw/route.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* GET /api/agent-skills/[id]/raw
|
||||
*
|
||||
* Returns the raw SKILL.md content for a given skill as text/markdown.
|
||||
* Resolution order: local filesystem → GitHub raw URL (1-hour cache).
|
||||
*
|
||||
* Response: text/markdown; charset=utf-8
|
||||
* 404 if skill not found in catalog.
|
||||
* 502 if upstream GitHub fetch fails.
|
||||
*/
|
||||
import { buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { getSkillById, fetchSkillMarkdown } from "@/lib/agentSkills/catalog";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
if (!id || typeof id !== "string") {
|
||||
return new Response(JSON.stringify(buildErrorBody(400, "Missing skill id")), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
// Validate the skill exists in catalog before attempting fetch
|
||||
const skill = getSkillById(id);
|
||||
if (!skill) {
|
||||
return new Response(JSON.stringify(buildErrorBody(404, `Skill not found: ${id}`)), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const markdown = await fetchSkillMarkdown(id);
|
||||
|
||||
// Return the full markdown content (frontmatter + body reconstructed)
|
||||
const content =
|
||||
markdown.frontmatter.name
|
||||
? `---\nname: ${markdown.frontmatter.name}\ndescription: ${markdown.frontmatter.description}\n---\n${markdown.body}`
|
||||
: markdown.body;
|
||||
|
||||
return new Response(content, {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "text/markdown; charset=utf-8",
|
||||
"Cache-Control": "public, max-age=3600",
|
||||
"X-Skill-Source": markdown.source,
|
||||
"X-Skill-Fetched-At": markdown.fetchedAt,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Failed to fetch skill markdown";
|
||||
|
||||
// Distinguish GitHub upstream failures (502) from internal errors (500)
|
||||
if (message.includes("GitHub raw fetch failed")) {
|
||||
return new Response(
|
||||
JSON.stringify(buildErrorBody(502, "Upstream fetch failed — try again later")),
|
||||
{ status: 502, headers: { "Content-Type": "application/json" } },
|
||||
);
|
||||
}
|
||||
|
||||
console.error("[API] GET /api/agent-skills/[id]/raw error:", error);
|
||||
return new Response(JSON.stringify(buildErrorBody(500, "Failed to fetch skill content")), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
37
src/app/api/agent-skills/[id]/route.ts
Normal file
37
src/app/api/agent-skills/[id]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* GET /api/agent-skills/[id]
|
||||
*
|
||||
* Returns a single AgentSkill by its canonical ID.
|
||||
*
|
||||
* Response: AgentSkill
|
||||
* 404 if skill not found in catalog.
|
||||
*/
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { getSkillById } from "@/lib/agentSkills/catalog";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(
|
||||
_request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> },
|
||||
) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
|
||||
if (!id || typeof id !== "string") {
|
||||
return NextResponse.json(buildErrorBody(400, "Missing skill id"), { status: 400 });
|
||||
}
|
||||
|
||||
const skill = getSkillById(id);
|
||||
if (!skill) {
|
||||
return NextResponse.json(buildErrorBody(404, `Skill not found: ${id}`), { status: 404 });
|
||||
}
|
||||
|
||||
return NextResponse.json(skill);
|
||||
} catch (error) {
|
||||
console.error("[API] GET /api/agent-skills/[id] error:", error);
|
||||
return NextResponse.json(buildErrorBody(500, "Failed to load skill"), { status: 500 });
|
||||
}
|
||||
}
|
||||
25
src/app/api/agent-skills/coverage/route.ts
Normal file
25
src/app/api/agent-skills/coverage/route.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* GET /api/agent-skills/coverage
|
||||
*
|
||||
* Returns SkillCoverage: how many skills have a SKILL.md on filesystem vs catalog total.
|
||||
*
|
||||
* Response: SkillCoverage
|
||||
*/
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { computeCoverage } from "@/lib/agentSkills/catalog";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const coverage = computeCoverage();
|
||||
return NextResponse.json(coverage);
|
||||
} catch (error) {
|
||||
console.error("[API] GET /api/agent-skills/coverage error:", error);
|
||||
return NextResponse.json(buildErrorBody(500, "Failed to compute skill coverage"), {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
68
src/app/api/agent-skills/generate/route.ts
Normal file
68
src/app/api/agent-skills/generate/route.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* POST /api/agent-skills/generate
|
||||
*
|
||||
* Triggers the Agent Skills generator. Requires management auth.
|
||||
* Default: dryRun=true, prune=false (safe preview mode).
|
||||
*
|
||||
* Body (all optional):
|
||||
* - dryRun?: boolean (default true)
|
||||
* - prune?: boolean (default false)
|
||||
* - onlyIds?: string[] (only regenerate these skill IDs)
|
||||
*
|
||||
* Response: GeneratorReport
|
||||
* 401 if unauthenticated.
|
||||
* 400 if body is malformed.
|
||||
* 503 if generator module is not yet available (F3 not merged).
|
||||
* 500 on internal error.
|
||||
*/
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { GenerateBodySchema } from "@/lib/agentSkills/schemas";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
// Management auth — returns null if OK, error Response if not
|
||||
const authError = await requireManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
// Parse + validate request body
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(buildErrorBody(400, "Request body must be valid JSON"), {
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const parsed = GenerateBodySchema.safeParse(rawBody ?? {});
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json(
|
||||
buildErrorBody(400, parsed.error.issues[0]?.message ?? "Invalid request body"),
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
// Dynamic import — F3 (generator.ts) may not be merged yet
|
||||
let generateAgentSkills: (opts: typeof parsed.data) => Promise<unknown>;
|
||||
try {
|
||||
const mod = await import("@/lib/agentSkills/generator");
|
||||
generateAgentSkills = mod.generateAgentSkills;
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
buildErrorBody(503, "Generator module is not yet available"),
|
||||
{ status: 503 },
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const report = await generateAgentSkills(parsed.data);
|
||||
return NextResponse.json(report);
|
||||
} catch (error) {
|
||||
console.error("[API] POST /api/agent-skills/generate error:", error);
|
||||
return NextResponse.json(buildErrorBody(500, "Generator failed"), { status: 500 });
|
||||
}
|
||||
}
|
||||
47
src/app/api/agent-skills/route.ts
Normal file
47
src/app/api/agent-skills/route.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* GET /api/agent-skills
|
||||
*
|
||||
* Returns the full Agent Skills catalog with optional filtering by category or area.
|
||||
*
|
||||
* Query params:
|
||||
* - category?: "api" | "cli"
|
||||
* - area?: string
|
||||
*
|
||||
* Response: { skills: AgentSkill[], count: number, coverage: SkillCoverage }
|
||||
*/
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import { buildErrorBody } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { ListQuerySchema } from "@/lib/agentSkills/schemas";
|
||||
import { filterCatalog, computeCoverage } from "@/lib/agentSkills/catalog";
|
||||
|
||||
// Catalog reads filesystem on demand — disable static caching
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
|
||||
const parsed = ListQuerySchema.safeParse({
|
||||
category: searchParams.get("category") ?? undefined,
|
||||
area: searchParams.get("area") ?? undefined,
|
||||
});
|
||||
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json(
|
||||
buildErrorBody(400, parsed.error.issues[0]?.message ?? "Invalid query parameters"),
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const skills = filterCatalog(parsed.data);
|
||||
const coverage = computeCoverage();
|
||||
|
||||
return NextResponse.json({ skills, count: skills.length, coverage });
|
||||
} catch (error) {
|
||||
console.error("[API] GET /api/agent-skills error:", error);
|
||||
return NextResponse.json(buildErrorBody(500, "Failed to load agent skills catalog"), {
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1261,7 +1261,8 @@
|
||||
"settingsResilienceDescription": "Circuit breaker, retry, and fallback configuration",
|
||||
"settingsAdvancedDescription": "Advanced payload rules, request limits, and proxy API settings",
|
||||
"mitmProxyDescription": "Configure MITM proxy settings for traffic inspection and debugging",
|
||||
"oneProxyDescription": "Configure 1Proxy settings for advanced proxy chaining"
|
||||
"oneProxyDescription": "Configure 1Proxy settings for advanced proxy chaining",
|
||||
"omniSkillsDescription": "Install and manage sandbox skills for automated prompt and tool execution"
|
||||
},
|
||||
"home": {
|
||||
"quickStart": "Quick Start",
|
||||
@@ -7878,5 +7879,76 @@
|
||||
"spawnArgs": "Spawn arguments",
|
||||
"spawnArgsPlaceholder": "e.g.: --quiet, --json",
|
||||
"cliCodeRedirectCta": "Open CLI Code's"
|
||||
},
|
||||
"agentSkills": {
|
||||
"pageTitle": "Agent Skills",
|
||||
"pageSubtitle": "Teach your agent to operate OmniRoute — 22 API areas + 20 CLI families",
|
||||
"conceptCard": {
|
||||
"agent": {
|
||||
"title": "Agent Skills — Outbound",
|
||||
"description": "Agent Skills are machine-readable SKILL.md documents that external AI agents (Claude Code, Cursor, Copilot…) fetch from GitHub to learn how to operate OmniRoute via REST or CLI. They are read by the agent, not executed by OmniRoute.",
|
||||
"crossLinkLabel": "Understand the difference →"
|
||||
},
|
||||
"omni": {
|
||||
"title": "Omni Skills — Inbound",
|
||||
"description": "Omni Skills are sandbox tools that OmniRoute injects into the model's context on every request. They are executed by OmniRoute, not read by the agent.",
|
||||
"crossLinkLabel": "Understand the difference →"
|
||||
},
|
||||
"comparison": {
|
||||
"colAgent": "Agent Skills",
|
||||
"colOmni": "Omni Skills",
|
||||
"whatIs": {
|
||||
"label": "What it is",
|
||||
"agent": "Machine-readable SKILL.md that teaches an external agent to operate OmniRoute",
|
||||
"omni": "Executable tools that OmniRoute injects as tools into LLM requests"
|
||||
},
|
||||
"direction": {
|
||||
"label": "Direction",
|
||||
"agent": "Outbound — external agent learns to control OmniRoute",
|
||||
"omni": "Inbound — OmniRoute gives tools to models passing through it"
|
||||
},
|
||||
"executor": {
|
||||
"label": "Executor",
|
||||
"agent": "External agent (reads markdown → acts via API/CLI)",
|
||||
"omni": "OmniRoute itself (intercepts tool_calls, Docker sandbox)"
|
||||
},
|
||||
"storage": {
|
||||
"label": "Storage",
|
||||
"agent": "Dynamic catalog (/api/agent-skills) → SKILL.md on GitHub",
|
||||
"omni": "SQLite (SkillsMP / skills.sh / local)"
|
||||
},
|
||||
"tagline": {
|
||||
"label": "Tagline",
|
||||
"agent": "Teach your agent to use OmniRoute",
|
||||
"omni": "Give executable tools to OmniRoute models"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"category": "Category",
|
||||
"area": "Area",
|
||||
"searchPlaceholder": "Search skills…"
|
||||
},
|
||||
"categoryApi": "API",
|
||||
"categoryCli": "CLI",
|
||||
"filterAll": "All",
|
||||
"coverageLabel": "Coverage",
|
||||
"mcpUrl": "MCP URL",
|
||||
"a2aLink": "A2A",
|
||||
"copyUrl": "Copy URL",
|
||||
"viewOnGithub": "View on GitHub",
|
||||
"previewLoading": "Loading skill documentation…",
|
||||
"previewError": "Failed to load skill documentation.",
|
||||
"previewEmpty": "Select a skill to preview its documentation.",
|
||||
"generateButton": "Generate missing skills",
|
||||
"coverageBar": {
|
||||
"complete": "Complete",
|
||||
"partial": "Partial"
|
||||
},
|
||||
"noSkillsFound": "No skills found matching your filters.",
|
||||
"regenerateConfirm": "This will regenerate all missing SKILL.md files. Continue?",
|
||||
"regenerateRunning": "Regenerating skills…",
|
||||
"regenerateSuccess": "Skills regenerated successfully.",
|
||||
"regenerateError": "Failed to regenerate skills."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3110,7 +3110,8 @@
|
||||
"settingsResilienceDescription": "Circuit breaker, retry, and fallback configuration",
|
||||
"settingsAdvancedDescription": "Advanced payload rules, request limits, and proxy API settings",
|
||||
"mitmProxyDescription": "Configure MITM proxy settings for traffic inspection and debugging",
|
||||
"oneProxyDescription": "Configure 1Proxy settings for advanced proxy chaining"
|
||||
"oneProxyDescription": "Configure 1Proxy settings for advanced proxy chaining",
|
||||
"omniSkillsDescription": "Instale e gerencie skills sandbox para execução automatizada de prompts e ferramentas"
|
||||
},
|
||||
"health": {
|
||||
"title": "Saúde do Sistema",
|
||||
@@ -7870,5 +7871,76 @@
|
||||
"spawnArgs": "Argumentos de spawn",
|
||||
"spawnArgsPlaceholder": "ex: --quiet, --json",
|
||||
"cliCodeRedirectCta": "Abrir CLI Code's"
|
||||
},
|
||||
"agentSkills": {
|
||||
"pageTitle": "Agent Skills",
|
||||
"pageSubtitle": "Ensine seu agente a operar o OmniRoute — 22 áreas de API + 20 famílias de CLI",
|
||||
"conceptCard": {
|
||||
"agent": {
|
||||
"title": "Agent Skills — Saída",
|
||||
"description": "Agent Skills são documentos SKILL.md legíveis por máquina que agentes externos (Claude Code, Cursor, Copilot…) baixam do GitHub para aprender a operar o OmniRoute via REST ou CLI. São lidos pelo agente, não executados pelo OmniRoute.",
|
||||
"crossLinkLabel": "Entenda a diferença →"
|
||||
},
|
||||
"omni": {
|
||||
"title": "Omni Skills — Entrada",
|
||||
"description": "Omni Skills são ferramentas sandbox que o OmniRoute injeta no contexto do modelo a cada solicitação. São executadas pelo OmniRoute, não lidas pelo agente.",
|
||||
"crossLinkLabel": "Entenda a diferença →"
|
||||
},
|
||||
"comparison": {
|
||||
"colAgent": "Agent Skills",
|
||||
"colOmni": "Omni Skills",
|
||||
"whatIs": {
|
||||
"label": "O que é",
|
||||
"agent": "Documentação (SKILL.md) que ensina um agente externo a operar o OmniRoute",
|
||||
"omni": "Ferramentas executáveis que o Omni injeta como tools nos requests LLM"
|
||||
},
|
||||
"direction": {
|
||||
"label": "Direção",
|
||||
"agent": "Outbound — externo aprende a controlar o Omni",
|
||||
"omni": "Inbound — Omni dá tools aos modelos que passam por ele"
|
||||
},
|
||||
"executor": {
|
||||
"label": "Executor",
|
||||
"agent": "Agente externo (lê markdown → age via API/CLI)",
|
||||
"omni": "O próprio Omni (intercepta tool_calls, sandbox Docker)"
|
||||
},
|
||||
"storage": {
|
||||
"label": "Storage",
|
||||
"agent": "Catálogo dinâmico (/api/agent-skills) → SKILL.md no GitHub",
|
||||
"omni": "SQLite (SkillsMP / skills.sh / local)"
|
||||
},
|
||||
"tagline": {
|
||||
"label": "Frase",
|
||||
"agent": "Ensine seu agente a usar o OmniRoute",
|
||||
"omni": "Dê ferramentas executáveis aos modelos do OmniRoute"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filters": {
|
||||
"category": "Categoria",
|
||||
"area": "Área",
|
||||
"searchPlaceholder": "Buscar skills…"
|
||||
},
|
||||
"categoryApi": "API",
|
||||
"categoryCli": "CLI",
|
||||
"filterAll": "Todas",
|
||||
"coverageLabel": "Cobertura",
|
||||
"mcpUrl": "URL MCP",
|
||||
"a2aLink": "A2A",
|
||||
"copyUrl": "Copiar URL",
|
||||
"viewOnGithub": "Ver no GitHub",
|
||||
"previewLoading": "Carregando documentação da skill…",
|
||||
"previewError": "Falha ao carregar documentação da skill.",
|
||||
"previewEmpty": "Selecione uma skill para visualizar sua documentação.",
|
||||
"generateButton": "Gerar skills faltantes",
|
||||
"coverageBar": {
|
||||
"complete": "Completo",
|
||||
"partial": "Parcial"
|
||||
},
|
||||
"noSkillsFound": "Nenhuma skill encontrada para os filtros selecionados.",
|
||||
"regenerateConfirm": "Isso irá regenerar todos os arquivos SKILL.md faltantes. Continuar?",
|
||||
"regenerateRunning": "Regenerando skills…",
|
||||
"regenerateSuccess": "Skills regeneradas com sucesso.",
|
||||
"regenerateError": "Falha ao regenerar skills."
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user