diff --git a/deploy/openpost/.env.openpost.example b/deploy/openpost/.env.openpost.example new file mode 100644 index 00000000000..4741542d83b --- /dev/null +++ b/deploy/openpost/.env.openpost.example @@ -0,0 +1,15 @@ +# OpenPost Reference Deployment Environment +# Upstream: https://github.com/getopenpost/openpost (v4.12.0) + +OPENPOST_PORT=8080 +OPENPOST_JWT_SECRET=generate-a-secure-random-32-byte-secret +OPENPOST_ENCRYPTION_KEY=generate-a-secure-random-32-byte-key +OPENPOST_API_TOKEN=generate-a-secure-random-token-for-pao-hubpro-access +OPENPOST_STORAGE_PATH=/data/media + +# Social Publishing Control Plane (Pao-hubPro side) +SOCIAL_PUBLISHING_ENABLED=true +OPENPOST_BASE_URL=http://localhost:8080 +OPENPOST_API_TOKEN=generate-a-secure-random-token-for-pao-hubpro-access +OPENPOST_TRANSPORT=hybrid + diff --git a/deploy/openpost/README.md b/deploy/openpost/README.md new file mode 100644 index 00000000000..8393e3048af --- /dev/null +++ b/deploy/openpost/README.md @@ -0,0 +1,29 @@ +# Self-Hosted OpenPost Deployment for Pao-hubPro + +Phase 20.60 integrates Pao-hubPro with OpenPost (`https://github.com/getopenpost/openpost`), an AGPL-3.0-licensed open-source multi-platform social media publishing suite. + +## Architecture Boundary + +Pao-hubPro and OpenPost run as separated services communicating via HTTP REST API and MCP: +- **Pao-hubPro**: Orchestration, policy evaluation, human approval workflow, master content briefs, rendition planning, audit logging, and cross-platform analytics. +- **OpenPost**: Provider OAuth connections, provider token storage, delivery execution, and social network API interaction (X, Mastodon, Bluesky, LinkedIn, Threads, Instagram, TikTok, YouTube, Discord). + +## Quick Start + +```bash +cd deploy/openpost +cp .env.openpost.example .env +# Edit .env with your secrets +docker compose -f docker-compose.openpost.yml up -d +``` + +Verify service health: +```bash +curl http://localhost:8080/api/v1/health +``` + +Register instance in Pao-hubPro: +```bash +ocx social instances register --name "Production OpenPost" --url "http://localhost:8080" +``` + diff --git a/deploy/openpost/docker-compose.openpost.yml b/deploy/openpost/docker-compose.openpost.yml new file mode 100644 index 00000000000..4988796e264 --- /dev/null +++ b/deploy/openpost/docker-compose.openpost.yml @@ -0,0 +1,31 @@ +version: "3.8" + +services: + openpost: + image: ghcr.io/getopenpost/openpost:v4.12.0 + container_name: openpost-publishing + restart: unless-stopped + ports: + - "8080:8080" + environment: + - PORT=8080 + - DATABASE_URL=sqlite:///data/openpost.db + - JWT_SECRET=${OPENPOST_JWT_SECRET} + - ENCRYPTION_KEY=${OPENPOST_ENCRYPTION_KEY} + - API_TOKEN=${OPENPOST_API_TOKEN} + - STORAGE_PATH=/data/media + - MCP_ENABLED=true + - MCP_PORT=8081 + volumes: + - openpost-data:/data + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/api/v1/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 20s + +volumes: + openpost-data: + driver: local + diff --git a/docs/legal/openpost-integration.md b/docs/legal/openpost-integration.md new file mode 100644 index 00000000000..5992b184a19 --- /dev/null +++ b/docs/legal/openpost-integration.md @@ -0,0 +1,13 @@ +# Legal and Licensing Boundary — OpenPost Integration + +**Upstream Project:** OpenPost (`https://github.com/getopenpost/openpost`) +**License:** AGPL-3.0-only +**Integration Boundary:** Network Service Boundary (HTTP API / MCP) + +## Engineering Policy + +1. **No Code Merging:** OpenPost source code is not copied, vendored, or compiled into Pao-hubPro core repositories. +2. **Network Protocol Boundary:** All interactions between Pao-hubPro and OpenPost occur across a network protocol boundary via standard HTTP REST requests and Model Context Protocol (MCP) tool executions. +3. **Deployment Separation:** OpenPost is deployed as an independent container or service process. Pao-hubPro acts solely as a client orchestrator. +4. **Secret Isolation:** Social media provider credentials (OAuth tokens, refresh tokens, client secrets) are stored exclusively in OpenPost's persistent storage and are never exposed or synchronized to Pao-hubPro databases or agent context. + diff --git a/gui/src/App.tsx b/gui/src/App.tsx index 489cb4e7f66..c6e7dfc1f44 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -11,6 +11,10 @@ import CodexSet from "./pages/CodexSet"; import Integrations from "./pages/Integrations"; import Startup from "./pages/Startup"; import RemoteWorkspace from "./pages/RemoteWorkspace"; +import { Skills } from "./pages/Skills"; +import { Security } from "./pages/Security"; +import { Credentials } from "./pages/Credentials"; +import { Social } from "./pages/Social"; import ErrorBoundary from "./components/ErrorBoundary"; import { SidebarGithubRow } from "./components/sidebar-github-row"; import { IconGrid, IconServer, IconBoxes, IconBot, IconList, IconActivity, IconHardDrive, IconCodex, IconMenu, IconSun, IconMoon, IconMonitor, IconGlobe, IconPower, IconX, IconRefresh} from "./icons"; @@ -33,6 +37,10 @@ const PAGE_TKEY: Record = { providers: "nav.providers", models: "nav.models", subagents: "nav.subagents", + skills: "nav.skills", + security: "nav.security", + credentials: "nav.credentials", + social: "nav.social", logs: "nav.logs", usage: "nav.usage", storage: "nav.storage", @@ -453,6 +461,10 @@ export default function App() { {page === "remote" && } {page === "codex-set" && } {page === "integrations" && } + {page === "skills" && } + {page === "security" && } + {page === "credentials" && } + {page === "social" && } )} diff --git a/gui/src/app-routing.ts b/gui/src/app-routing.ts index 9dbf4d28bc5..826b454c9a5 100644 --- a/gui/src/app-routing.ts +++ b/gui/src/app-routing.ts @@ -8,6 +8,10 @@ export type Page = | "providers" | "models" | "subagents" + | "skills" + | "security" + | "credentials" + | "social" | "logs" | "usage" | "storage" @@ -21,6 +25,10 @@ export const VALID_PAGES = new Set([ "providers", "models", "subagents", + "skills", + "security", + "credentials", + "social", "logs", "usage", "storage", @@ -107,11 +115,38 @@ export const INTEGRATION_TAB_HASHES = [ "integrations/cline", ] as const; + +export const SKILLS_TAB_HASHES = [ + "skills/marketplace", "skills/registry", "skills/editor", "skills/matrix", + "skills/agents", "skills/nodes", "skills/drift", "skills/reviews", "skills/audit", +] as const; + +export const SECURITY_TAB_HASHES = [ + "security/authorizations", "security/scopes", "security/campaigns", "security/approvals", + "security/findings", "security/evidence", "security/skills", "security/tools", + "security/mcp", "security/policies", "security/audit", +] as const; + +export const CREDENTIALS_TAB_HASHES = [ + "credentials/list", "credentials/providers", "credentials/oauth", "credentials/pool", + "credentials/health", "credentials/quota", "credentials/policies", + "credentials/approvals", "credentials/audit", +] as const; + +export const SOCIAL_TAB_HASHES = [ + "social/accounts", "social/publications", "social/approvals", + "social/jobs", "social/analytics", "social/instances", "social/audit", +] as const; + export function hashBelongsToPage(rawHash: string, page: Page): boolean { return rawHash === page || (page === "logs" && rawHash === "logs/debug") || (page === "codex-set" && rawHash === "codex-set/prompt") || (page === "models" && (MODELS_TAB_HASHES as readonly string[]).includes(rawHash)) + || (page === "skills" && (SKILLS_TAB_HASHES as readonly string[]).includes(rawHash)) + || (page === "security" && (SECURITY_TAB_HASHES as readonly string[]).includes(rawHash)) + || (page === "credentials" && (CREDENTIALS_TAB_HASHES as readonly string[]).includes(rawHash)) + || (page === "social" && (SOCIAL_TAB_HASHES as readonly string[]).includes(rawHash)) || (page === "dashboard" && (rawHash === DASHBOARD_UPDATE_HASH || (DASHBOARD_TAB_HASHES as readonly string[]).includes(rawHash))) || (page === "integrations" diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index a68a1b933e3..1282e326857 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -73,6 +73,10 @@ export const de: Record = { "nav.models": "Modelle", "nav.combos": "Combos", "nav.subagents": "Sub-Agenten", + "nav.skills": "Skills", + "nav.security": "Sicherheit", + "nav.credentials": "Anmeldedaten", + "nav.social": "Social Publishing", // routing intelligence "routing.title": "Routing-Intelligenz (beta)", @@ -2797,6 +2801,225 @@ export const de: Record = { "models.pickerOrder.saveDraft": "Entwurf speichern", "models.pickerOrder.reloadDraft": "Neu laden und Entwurf verwerfen", "models.pickerOrder.catalogRequired": "Modellidentitäten fehlen oder sind mehrdeutig. Laden Sie die Modellseite neu, um den Katalog vor der Bearbeitung zu aktualisieren.", + "skills.title": "Skill-Steuerungsebene", + "skills.subtitle": "Universelles Agent-Skill-Register, visueller Marktplatz und richtliniengesteuerte Bereitstellung.", + "skills.refresh": "Aktualisieren", + "skills.refreshing": "Aktualisiere…", + "skills.tab.overview": "Übersicht", + "skills.tab.marketplace": "Marktplatz", + "skills.tab.registry": "Register ({count})", + "skills.tab.editor": "Skill-Editor", + "skills.tab.matrix": "Bereitstellungsmatrix", + "skills.tab.agents": "Agenten", + "skills.tab.nodes": "Remote-Knoten", + "skills.tab.drift": "Drift und Rollback", + "skills.tab.reviews": "Prüfwarteschlange", + "skills.tab.audit": "Prüfprotokoll", + "skills.card.registered": "Registrierte Skills", + "skills.card.deployments": "Aktive Bereitstellungen", + "skills.card.marketplace": "Marktplatz-Einträge", + "skills.card.audit": "Prüfereignisse", + "skills.recentActivity": "Aktuelle Steuerungsebenen-Aktivität", + "skills.noEvents": "Keine aktuellen Ereignisse aufgezeichnet.", + "skills.col.timestamp": "Zeitstempel", + "skills.col.event": "Ereignis", + "skills.col.actor": "Akteur", + "skills.col.target": "Ziel", + "skills.searchPlaceholder": "Öffentlichen skills.sh-Katalog durchsuchen…", + "skills.byAuthor": "v{version} von {author}", + "skills.verified": "Verifiziert", + "skills.importToRegistry": "Ins Register importieren", + "skills.emptyRegistry": "Noch keine Skills registriert. Importieren Sie einen vom Marktplatz oder aus einem lokalen Ordner.", + "skills.col.skill": "Skill", + "skills.col.version": "Version", + "skills.col.status": "Status", + "skills.col.risk": "Risiko", + "skills.col.tags": "Tags", + "skills.col.actions": "Aktionen", + "skills.publish": "Veröffentlichen", + "skills.deployCodex": "Nach Codex bereitstellen", + "skills.editorTitle": "SKILL.md-Editor", + "skills.editorNamePlaceholder": "Skill-Name", + "skills.editorVersionPlaceholder": "Version", + "skills.editorDefaultName": "Benutzerdefinierter Assistent", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "Entwurf speichern", + "skills.previewTitle": "Live-Vorschau", + "skills.matrixTitle": "Flotten-Bereitstellungsmatrix", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "Nicht installiert", + "skills.inSync": "v{version} synchron", + "skills.blocked": "Blockiert", + "skills.agentsTitle": "Ziel-Agentenflotte", + "skills.col.agent": "Agent", + "skills.col.userRoot": "Zielwurzel (Benutzerbereich)", + "skills.col.projectRoot": "Zielwurzel (Projektbereich)", + "skills.col.adapterVersion": "Adapterversion", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "Universeller Agent", + "skills.statusReady": "Bereit", + "skills.nodesTitle": "Remote-Ausführungsknoten (SSH)", + "skills.nodesHelp": "Verwalten Sie Remote-VPS, GPU-Worker und Entwicklungshosts mit strikter Host-Key-Pinning.", + "skills.col.node": "Knoten", + "skills.col.type": "Typ", + "skills.col.host": "Host", + "skills.col.environment": "Umgebung", + "skills.localMachine": "Lokaler Rechner", + "skills.self": "Selbst", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "Verbindung testen", + "skills.driftTitle": "Konfigurationsdrift und Rollback", + "skills.driftHelp": "Die kontinuierliche Überwachung vergleicht Live-Dateien mit unveränderlichen Registry-Hashes.", + "skills.col.deployment": "Bereitstellung", + "skills.col.targetPath": "Zielpfad", + "skills.col.driftState": "Drift-Status", + "skills.checkHash": "Hash prüfen", + "skills.reviewsTitle": "Richtlinien- und Freigabewarteschlange", + "skills.reviewsEmpty": "Keine ausstehenden erhöhten Prüfungen erfordern eine Operatorentscheidung.", + "skills.auditTitle": "Prüfprotokoll", + "skills.col.eventType": "Ereignistyp", + "skills.col.targetId": "Ziel-ID", + "skills.system": "system", + "skills.status.importing": "Importiere {id}…", + "skills.status.imported": "{id} importiert.", + "skills.status.importFailed": "Import fehlgeschlagen: {error}", + "skills.status.publishing": "Veröffentliche {id}…", + "skills.status.published": "{id} veröffentlicht.", + "skills.status.publishFailed": "Veröffentlichung fehlgeschlagen: {error}", + "skills.status.planning": "Plane Bereitstellung von {id} nach {agent}…", + "skills.status.deployed": "{id} nach {agent} bereitgestellt.", + "skills.status.deployFailed": "Bereitstellung fehlgeschlagen: {error}", + "skills.status.saved": "Skill-Entwurf im Register gespeichert.", + "skills.status.saveFailed": "Speichern fehlgeschlagen: {error}", + "skills.loadFailed": "Skill-Steuerungsebene konnte nicht geladen werden.", + "security.title": "Sicherheits-Steuerungsebene", + "security.subtitle": "Autorisierungsorientierte Erkundung und Validierung. Zielgerichtete Arbeit ist standardmäßig verweigert. R3 ist blockiert. Hash-routbar; keine Sidebar-NAV-Zeile.", + "security.refresh": "Aktualisieren", + "security.refreshing": "Aktualisiere…", + "security.banner": "Nur autorisierte Ziele. Unbekannte Assets, abgelaufene Autorisierungen und ausgeschlossene Hosts werden verweigert. Live-Drittanbieter-Tools werden nicht aufgerufen; die Erkundung nutzt lokale Fixtures.", + "security.flagOff": "Feature-Flag PAO_SECURITY_CONTROL_PLANE ist aus — Änderungen werden abgelehnt.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "Fehlgeschlagen: {error}", + "security.evidence.campaign": "Kampagnen-Nachweis: {campaign}", + "security.evidence.none": "Keine aktive Kampagne", + "security.tab.overview": "Übersicht", + "security.tab.authorizations": "Autorisierungen", + "security.tab.scopes": "Bereiche", + "security.tab.campaigns": "Kampagnen", + "security.tab.approvals": "Freigaben", + "security.tab.findings": "Findings", + "security.tab.evidence": "Nachweise", + "security.tab.skills": "Pakete", + "security.tab.tools": "Werkzeuge", + "security.tab.mcp": "MCP", + "security.tab.policies": "Richtlinien", + "security.tab.audit": "Protokoll", + "security.card.enabled": "Aktiviert", + "security.card.campaigns": "Kampagnen", + "security.card.approvals": "Freigaben", + "security.card.leads": "Leads", + "security.card.validated": "Validiert", + "security.card.blocked": "Blockiert", + "security.card.authExpiring": "Autorisierung läuft ab", + "security.card.breakers": "Schutzschalter", + "security.yes": "ja", + "security.no": "nein", + "security.col.id": "ID", + "security.col.type": "Typ", + "security.col.status": "Status", + "security.col.validUntil": "Gültig bis", + "security.col.name": "Name", + "security.col.actions": "Aktionen", + "security.col.target": "Ziel", + "security.col.title": "Titel", + "security.col.severity": "Schwere", + "security.col.sha256": "SHA-256", + "security.col.preview": "Vorschau", + "security.col.compatibility": "Kompatibilität", + "security.col.kind": "Art", + "security.col.allowlisted": "Auf der Allowlist", + "security.col.health": "Zustand", + "security.col.credential": "Anmeldedaten", + "security.col.ceiling": "Obergrenze", + "security.col.blockR3": "R3 blockieren", + "security.col.when": "Wann", + "security.col.event": "Ereignis", + "security.col.actor": "Akteur", + "security.start": "Starten", + "security.pause": "Pausieren", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "Einmal freigeben", + "security.deny": "Ablehnen", + "security.reasonBounded": "einmalig begrenzt", + "security.reasonDenied": "abgelehnt", + "security.hashes": "Lesezeichen-Hashes: {hashes}", + "security.loadFailed": "Sicherheits-Steuerungsebene konnte nicht geladen werden.", + "credentials.title": "Provider-Zugriffssteuerung", + "credentials.subtitle": "Verschlüsselte Anmeldedaten, gesundheitsgeprüfter Token-Pool, OAuth-Gateway und richtliniengesteuerte Leases. Hash-routbar; keine Sidebar-NAV-Zeile.", + "credentials.refresh": "Aktualisieren", + "credentials.refreshing": "Aktualisiere…", + "credentials.banner": "Geheimnisse werden niemals im Klartext aufgelistet. Gesundheitsprüfungen in dieser Phase sind Fixture/In-Process. Diese Ebene registriert keine Anbieterkonten.", + "credentials.flagOff": "Feature-Flag CREDENTIAL_RUNTIME_ENABLED ist aus — Mutationen werden abgelehnt.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "Fehlgeschlagen: {error}", + "credentials.tab.overview": "Übersicht", + "credentials.tab.list": "Anmeldedaten", + "credentials.tab.providers": "Anbieter", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "Pool", + "credentials.tab.health": "Gesundheit", + "credentials.tab.quota": "Kontingent", + "credentials.tab.policies": "Richtlinien", + "credentials.tab.approvals": "Freigaben", + "credentials.tab.audit": "Audit", + "credentials.card.enabled": "Aktiviert", + "credentials.card.total": "Gesamt", + "credentials.card.healthy": "Gesund", + "credentials.card.degraded": "Beeinträchtigt", + "credentials.card.quarantined": "Quarantäne", + "credentials.card.revoked": "Widerrufen", + "credentials.card.leases": "Leases", + "credentials.card.breakers": "Unterbrecher", + "credentials.card.vault": "Tresor", + "credentials.yes": "ja", + "credentials.no": "nein", + "credentials.available": "verfügbar", + "credentials.unavailable": "nicht verfügbar", + "credentials.col.id": "ID", + "credentials.col.name": "Name", + "credentials.col.provider": "Anbieter", + "credentials.col.status": "Status", + "credentials.col.health": "Gesundheit", + "credentials.col.secret": "Geheimnis", + "credentials.col.eligible": "Berechtigt", + "credentials.col.actions": "Aktionen", + "credentials.col.score": "Score", + "credentials.col.adapter": "Adapter", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "Aktiviert", + "credentials.col.session": "Sitzung", + "credentials.col.redirect": "Weiterleitung", + "credentials.col.budget": "Budget", + "credentials.col.priority": "Priorität", + "credentials.col.action": "Aktion", + "credentials.col.target": "Ziel", + "credentials.col.when": "Wann", + "credentials.col.event": "Ereignis", + "credentials.col.actor": "Akteur", + "credentials.col.decision": "Entscheidung", + "credentials.validate": "Validieren", + "credentials.quarantine": "Quarantäne", + "credentials.revoke": "Widerrufen", + "credentials.healthRun": "Gesundheitsprüfung starten", + "credentials.approve": "Freigeben", + "credentials.reject": "Ablehnen", + "credentials.hashes": "Lesezeichen-Hashes: {hashes}", + "credentials.loadFailed": "Anmeldedaten-Laufzeitdaten konnten nicht geladen werden.", "nav.remote": "Remote-Arbeitsbereich", "remote.title": "Remote-Arbeitsbereich", "remote.subtitle": "Codex, Claude Code oder Pi laufen auf diesem Hub; Dateien, Befehle, Tests und Builds bleiben auf dem ausgewählten Computer.", @@ -2858,4 +3081,77 @@ export const de: Record = { "remote.event.status": "Status", "remote.event.tool": "Remote-Werkzeug", "remote.event.error": "Fehler", + "social.title": "Social-Publishing-Steuerungsebene", + "social.subtitle": "Agentische Social-Media-Orchestrierung, plattformübergreifende Versionen und OpenPost-Zeitplanung.", + "social.refresh": "Aktualisieren", + "social.refreshing": "Aktualisiere…", + "social.banner": "Mit OpenPost verbunden. Provider-OAuth-Geheimnisse bleiben in OpenPost geschützt. Menschliche Freigabe erforderlich.", + "social.flagOff": "Funktionsflag SOCIAL_PUBLISHING_ENABLED ist aus — Änderungen werden verweigert.", + "social.tab.overview": "Übersicht", + "social.tab.accounts": "Konten", + "social.tab.publications": "Veröffentlichungen", + "social.tab.approvals": "Freigaben", + "social.tab.jobs": "Zustellungsjobs", + "social.tab.analytics": "Analysen", + "social.tab.instances": "OpenPost-Instanzen", + "social.tab.audit": "Prüfprotokoll", + "social.card.enabled": "Aktiviert", + "social.card.instances": "Instanzen", + "social.card.accounts": "Konten", + "social.card.readyAccounts": "Bereite Konten", + "social.card.publications": "Veröffentlichungen", + "social.card.scheduled": "Geplant", + "social.card.pendingApprovals": "Ausstehende Freigaben", + "social.card.failedDeliveries": "Fehlgeschlagene Zustellungen", + "social.yes": "ja", + "social.no": "nein", + "social.col.id": "ID", + "social.col.platform": "Plattform", + "social.col.displayName": "Anzeigename", + "social.col.readiness": "Bereitschaft", + "social.col.enabled": "Aktiviert", + "social.col.actions": "Aktionen", + "social.col.sourceType": "Quelltyp", + "social.col.title": "Titel", + "social.col.status": "Status", + "social.col.scheduledAt": "Geplant für", + "social.col.name": "Name", + "social.col.baseUrl": "Basis-URL", + "social.col.version": "Version", + "social.col.pubId": "Veröffentlichung", + "social.col.jobType": "Job-Typ", + "social.col.attempts": "Versuche", + "social.col.error": "Fehler", + "social.col.when": "Zeitstempel", + "social.col.actor": "Akteur", + "social.col.action": "Aktion", + "social.col.resource": "Ressource", + "social.col.resourceId": "Ressourcen-ID", + "social.col.decision": "Entscheidung", + "social.col.approver": "Genehmiger", + "social.col.approvalScope": "Genehmigungsumfang", + "social.col.contentHash": "Inhalts-Hash", + "social.col.account": "Konto", + "social.col.capturedAt": "Erfasst am", + "social.col.views": "Aufrufe", + "social.col.impressions": "Impressionen", + "social.col.engagements": "Interaktionen", + "social.col.followersDelta": "Follower Δ", + "social.emptyAccounts": "Keine verknüpften Social-Media-Konten gefunden. Synchronisieren Sie Konten aus OpenPost.", + "social.emptyPublications": "Noch keine Veröffentlichungen erstellt.", + "social.emptyInstances": "Keine OpenPost-Instanzen registriert.", + "social.emptyJobs": "Keine Zustellungsjobs erfasst.", + "social.emptyAudit": "Keine Prüfereignisse erfasst.", + "social.emptyApprovals": "Noch keine Genehmigungen erfasst.", + "social.emptyAnalytics": "Noch keine Analyse-Snapshots erfasst.", + "social.refreshCaps": "Funktionen aktualisieren", + "social.validate": "Prüfen", + "social.approve": "Freigeben", + "social.publish": "Jetzt veröffentlichen", + "social.test": "Verbindung testen", + "social.syncAccounts": "Konten synchronisieren", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "Fehlgeschlagen: {error}", + "social.hashes": "Bookmarkbare Hashes: {hashes}", }; diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index 8caebec35f0..ff755147ce3 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -75,6 +75,10 @@ export const en = { "nav.models": "Models", "nav.combos": "Combos", "nav.subagents": "Subagents", + "nav.skills": "Skills", + "nav.security": "Security", + "nav.credentials": "Credentials", + "nav.social": "Social Publishing", "nav.logs": "Logs & Debug", "nav.usage": "Usage", "common.github": "GitHub", @@ -2831,6 +2835,225 @@ export const en = { "models.pickerOrder.saveDraft": "Save draft", "models.pickerOrder.reloadDraft": "Reload and discard draft", "models.pickerOrder.catalogRequired": "Model identities are missing or ambiguous. Reload the Models page to refresh its catalog before editing Custom.", + "skills.title": "Skill Control Plane", + "skills.subtitle": "Universal Agent Skill Registry, visual marketplace, and policy-governed deployment.", + "skills.refresh": "Refresh", + "skills.refreshing": "Refreshing…", + "skills.tab.overview": "Overview", + "skills.tab.marketplace": "Marketplace", + "skills.tab.registry": "Registry ({count})", + "skills.tab.editor": "Skill Editor", + "skills.tab.matrix": "Deployment Matrix", + "skills.tab.agents": "Agents", + "skills.tab.nodes": "Remote Nodes", + "skills.tab.drift": "Drift & Rollback", + "skills.tab.reviews": "Review Queue", + "skills.tab.audit": "Audit Trail", + "skills.card.registered": "Registered Skills", + "skills.card.deployments": "Active Deployments", + "skills.card.marketplace": "Marketplace Items", + "skills.card.audit": "Audit Events", + "skills.recentActivity": "Recent control-plane activity", + "skills.noEvents": "No recent events recorded.", + "skills.col.timestamp": "Timestamp", + "skills.col.event": "Event", + "skills.col.actor": "Actor", + "skills.col.target": "Target", + "skills.searchPlaceholder": "Search the public skills.sh catalog…", + "skills.byAuthor": "v{version} by {author}", + "skills.verified": "Verified", + "skills.importToRegistry": "Import to registry", + "skills.emptyRegistry": "No skills registered yet. Import one from Marketplace or a local folder.", + "skills.col.skill": "Skill", + "skills.col.version": "Version", + "skills.col.status": "Status", + "skills.col.risk": "Risk", + "skills.col.tags": "Tags", + "skills.col.actions": "Actions", + "skills.publish": "Publish", + "skills.deployCodex": "Deploy to Codex", + "skills.editorTitle": "SKILL.md editor", + "skills.editorNamePlaceholder": "Skill name", + "skills.editorVersionPlaceholder": "Version", + "skills.editorDefaultName": "Custom Assistant", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "Save draft", + "skills.previewTitle": "Live rendered preview", + "skills.matrixTitle": "Fleet deployment matrix", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "Not installed", + "skills.inSync": "v{version} in sync", + "skills.blocked": "Blocked", + "skills.agentsTitle": "Target agent fleet", + "skills.col.agent": "Agent", + "skills.col.userRoot": "Target root (user scope)", + "skills.col.projectRoot": "Target root (project scope)", + "skills.col.adapterVersion": "Adapter version", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "Universal Agent", + "skills.statusReady": "Ready", + "skills.nodesTitle": "Remote execution nodes (SSH)", + "skills.nodesHelp": "Manage remote VPS, GPU workers, and development hosts with strict host-key pinning.", + "skills.col.node": "Node", + "skills.col.type": "Type", + "skills.col.host": "Host", + "skills.col.environment": "Environment", + "skills.localMachine": "Local machine", + "skills.self": "Self", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "Test connection", + "skills.driftTitle": "Configuration drift and rollback", + "skills.driftHelp": "Continuous monitoring compares live disk files against immutable registry content hashes.", + "skills.col.deployment": "Deployment", + "skills.col.targetPath": "Target path", + "skills.col.driftState": "Drift state", + "skills.checkHash": "Check hash", + "skills.reviewsTitle": "Policy and approval queue", + "skills.reviewsEmpty": "No pending elevated reviews require an operator decision.", + "skills.auditTitle": "Audit log", + "skills.col.eventType": "Event type", + "skills.col.targetId": "Target ID", + "skills.system": "system", + "skills.status.importing": "Importing {id}…", + "skills.status.imported": "Imported {id}.", + "skills.status.importFailed": "Import failed: {error}", + "skills.status.publishing": "Publishing {id}…", + "skills.status.published": "Published {id}.", + "skills.status.publishFailed": "Publish failed: {error}", + "skills.status.planning": "Planning deployment for {id} to {agent}…", + "skills.status.deployed": "Deployed {id} to {agent}.", + "skills.status.deployFailed": "Deploy failed: {error}", + "skills.status.saved": "Skill draft saved to the registry.", + "skills.status.saveFailed": "Save failed: {error}", + "skills.loadFailed": "Could not load skill control plane data.", + "security.title": "Security Control Plane", + "security.subtitle": "Authorization-first recon and validation. Target-facing work is deny-by-default. R3 is blocked. Hash-routable; not a sidebar NAV row.", + "security.refresh": "Refresh", + "security.refreshing": "Refreshing…", + "security.banner": "Authorized targets only. Unknown assets, expired authorizations, and excluded hosts deny. Live third-party tools are not invoked; recon uses local fixtures.", + "security.flagOff": "Feature flag PAO_SECURITY_CONTROL_PLANE is off — mutations are refused.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "Failed: {error}", + "security.evidence.campaign": "Campaign evidence: {campaign}", + "security.evidence.none": "No active campaign", + "security.tab.overview": "Overview", + "security.tab.authorizations": "Authorizations", + "security.tab.scopes": "Scopes", + "security.tab.campaigns": "Campaigns", + "security.tab.approvals": "Approvals", + "security.tab.findings": "Findings", + "security.tab.evidence": "Evidence", + "security.tab.skills": "Packages", + "security.tab.tools": "Tools", + "security.tab.mcp": "MCP", + "security.tab.policies": "Policies", + "security.tab.audit": "Audit", + "security.card.enabled": "Enabled", + "security.card.campaigns": "Campaigns", + "security.card.approvals": "Approvals", + "security.card.leads": "Leads", + "security.card.validated": "Validated", + "security.card.blocked": "Blocked", + "security.card.authExpiring": "Auth expiring", + "security.card.breakers": "Breakers", + "security.yes": "yes", + "security.no": "no", + "security.col.id": "ID", + "security.col.type": "Type", + "security.col.status": "Status", + "security.col.validUntil": "Valid until", + "security.col.name": "Name", + "security.col.actions": "Actions", + "security.col.target": "Target", + "security.col.title": "Title", + "security.col.severity": "Severity", + "security.col.sha256": "SHA-256", + "security.col.preview": "Preview", + "security.col.compatibility": "Compatibility", + "security.col.kind": "Kind", + "security.col.allowlisted": "Allowlisted", + "security.col.health": "Health", + "security.col.credential": "Credential", + "security.col.ceiling": "Ceiling", + "security.col.blockR3": "Block R3", + "security.col.when": "When", + "security.col.event": "Event", + "security.col.actor": "Actor", + "security.start": "Start", + "security.pause": "Pause", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "Approve once", + "security.deny": "Deny", + "security.reasonBounded": "bounded once", + "security.reasonDenied": "denied", + "security.hashes": "Bookmarkable hashes: {hashes}", + "security.loadFailed": "Could not load security control plane data.", + "credentials.title": "Provider Access Control Plane", + "credentials.subtitle": "Encrypted credentials, health-checked token pool, OAuth gateway, and policy-governed leases. Hash-routable; not a sidebar NAV row.", + "credentials.refresh": "Refresh", + "credentials.refreshing": "Refreshing…", + "credentials.banner": "Secrets are never listed in plaintext. Health checks in this phase are fixture/in-process. This plane does not register provider accounts.", + "credentials.flagOff": "Feature flag CREDENTIAL_RUNTIME_ENABLED is off — mutations are refused.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "Failed: {error}", + "credentials.tab.overview": "Overview", + "credentials.tab.list": "Credentials", + "credentials.tab.providers": "Providers", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "Pool", + "credentials.tab.health": "Health", + "credentials.tab.quota": "Quota", + "credentials.tab.policies": "Policies", + "credentials.tab.approvals": "Approvals", + "credentials.tab.audit": "Audit", + "credentials.card.enabled": "Enabled", + "credentials.card.total": "Total", + "credentials.card.healthy": "Healthy", + "credentials.card.degraded": "Degraded", + "credentials.card.quarantined": "Quarantined", + "credentials.card.revoked": "Revoked", + "credentials.card.leases": "Leases", + "credentials.card.breakers": "Breakers", + "credentials.card.vault": "Vault", + "credentials.yes": "yes", + "credentials.no": "no", + "credentials.available": "available", + "credentials.unavailable": "unavailable", + "credentials.col.id": "ID", + "credentials.col.name": "Name", + "credentials.col.provider": "Provider", + "credentials.col.status": "Status", + "credentials.col.health": "Health", + "credentials.col.secret": "Secret", + "credentials.col.eligible": "Eligible", + "credentials.col.actions": "Actions", + "credentials.col.score": "Score", + "credentials.col.adapter": "Adapter", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "Enabled", + "credentials.col.session": "Session", + "credentials.col.redirect": "Redirect", + "credentials.col.budget": "Budget", + "credentials.col.priority": "Priority", + "credentials.col.action": "Action", + "credentials.col.target": "Target", + "credentials.col.when": "When", + "credentials.col.event": "Event", + "credentials.col.actor": "Actor", + "credentials.col.decision": "Decision", + "credentials.validate": "Validate", + "credentials.quarantine": "Quarantine", + "credentials.revoke": "Revoke", + "credentials.healthRun": "Run health pass", + "credentials.approve": "Approve", + "credentials.reject": "Reject", + "credentials.hashes": "Bookmarkable hashes: {hashes}", + "credentials.loadFailed": "Could not load credential runtime data.", "nav.remote": "Remote Workspace", "remote.title": "Remote Workspace", "remote.subtitle": "Run Codex, Claude Code, or Pi from this Hub while files, commands, tests, and builds stay on the computer you select.", @@ -2892,6 +3115,79 @@ export const en = { "remote.event.status": "Status", "remote.event.tool": "Remote tool", "remote.event.error": "Error", + "social.title": "Social Publishing Control Plane", + "social.subtitle": "Agentic social publishing orchestration, multi-platform renditions, and durable OpenPost scheduling.", + "social.refresh": "Refresh", + "social.refreshing": "Refreshing…", + "social.banner": "Connected to OpenPost. Provider OAuth secrets remain protected inside OpenPost. Human approval required before publication.", + "social.flagOff": "Feature flag SOCIAL_PUBLISHING_ENABLED is off — mutations are refused.", + "social.tab.overview": "Overview", + "social.tab.accounts": "Accounts", + "social.tab.publications": "Publications", + "social.tab.approvals": "Approvals", + "social.tab.jobs": "Delivery Jobs", + "social.tab.analytics": "Analytics", + "social.tab.instances": "OpenPost Instances", + "social.tab.audit": "Audit Trail", + "social.card.enabled": "Enabled", + "social.card.instances": "Instances", + "social.card.accounts": "Accounts", + "social.card.readyAccounts": "Ready Accounts", + "social.card.publications": "Publications", + "social.card.scheduled": "Scheduled", + "social.card.pendingApprovals": "Pending Approvals", + "social.card.failedDeliveries": "Failed Deliveries", + "social.yes": "yes", + "social.no": "no", + "social.col.id": "ID", + "social.col.platform": "Platform", + "social.col.displayName": "Display Name", + "social.col.readiness": "Readiness", + "social.col.enabled": "Enabled", + "social.col.actions": "Actions", + "social.col.sourceType": "Source Type", + "social.col.title": "Title", + "social.col.status": "Status", + "social.col.scheduledAt": "Scheduled At", + "social.col.name": "Name", + "social.col.baseUrl": "Base URL", + "social.col.version": "Version", + "social.col.pubId": "Publication", + "social.col.jobType": "Job Type", + "social.col.attempts": "Attempts", + "social.col.error": "Error", + "social.col.when": "Timestamp", + "social.col.actor": "Actor", + "social.col.action": "Action", + "social.col.resource": "Resource", + "social.col.resourceId": "Resource ID", + "social.col.decision": "Decision", + "social.col.approver": "Approver", + "social.col.approvalScope": "Approval Scope", + "social.col.contentHash": "Content Hash", + "social.col.account": "Account", + "social.col.capturedAt": "Captured At", + "social.col.views": "Views", + "social.col.impressions": "Impressions", + "social.col.engagements": "Engagements", + "social.col.followersDelta": "Followers Δ", + "social.emptyAccounts": "No connected social accounts found. Sync accounts from an OpenPost instance.", + "social.emptyPublications": "No publications created yet.", + "social.emptyInstances": "No OpenPost instances registered.", + "social.emptyJobs": "No delivery jobs recorded.", + "social.emptyAudit": "No social audit events recorded.", + "social.emptyApprovals": "No approvals recorded yet.", + "social.emptyAnalytics": "No analytics snapshots recorded yet.", + "social.refreshCaps": "Refresh capabilities", + "social.validate": "Validate", + "social.approve": "Approve", + "social.publish": "Publish now", + "social.test": "Test connection", + "social.syncAccounts": "Sync accounts", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "Failed: {error}", + "social.hashes": "Bookmarkable hashes: {hashes}", } as const; export type TKey = keyof typeof en; diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts index d6fe2bf5970..fe3fbffcea2 100644 --- a/gui/src/i18n/fr.ts +++ b/gui/src/i18n/fr.ts @@ -72,6 +72,10 @@ export const fr: Record = { "nav.models": "Modèles", "nav.combos": "Combinaisons", "nav.subagents": "Sous-agents", + "nav.skills": "Compétences", + "nav.security": "Sécurité", + "nav.credentials": "Identifiants", + "nav.social": "Publication sociale", "nav.logs": "Journaux et débogage", "nav.usage": "Utilisation", "common.github": "GitHub", @@ -2785,6 +2789,225 @@ export const fr: Record = { "models.pickerOrder.saveDraft": "Enregistrer le brouillon", "models.pickerOrder.reloadDraft": "Recharger et supprimer le brouillon", "models.pickerOrder.catalogRequired": "Les identités des modèles sont manquantes ou ambiguës. Rechargez la page Modèles pour actualiser le catalogue avant de personnaliser l’ordre.", + "skills.title": "Plan de contrôle des compétences", + "skills.subtitle": "Registre universel des compétences d’agent, marketplace visuel et déploiement gouverné par la politique.", + "skills.refresh": "Actualiser", + "skills.refreshing": "Actualisation…", + "skills.tab.overview": "Aperçu", + "skills.tab.marketplace": "Marketplace", + "skills.tab.registry": "Registre ({count})", + "skills.tab.editor": "Éditeur de compétence", + "skills.tab.matrix": "Matrice de déploiement", + "skills.tab.agents": "Agents", + "skills.tab.nodes": "Nœuds distants", + "skills.tab.drift": "Dérive et restauration", + "skills.tab.reviews": "File d’examen", + "skills.tab.audit": "Journal d’audit", + "skills.card.registered": "Compétences enregistrées", + "skills.card.deployments": "Déploiements actifs", + "skills.card.marketplace": "Éléments du marketplace", + "skills.card.audit": "Événements d’audit", + "skills.recentActivity": "Activité récente du plan de contrôle", + "skills.noEvents": "Aucun événement récent enregistré.", + "skills.col.timestamp": "Horodatage", + "skills.col.event": "Événement", + "skills.col.actor": "Acteur", + "skills.col.target": "Cible", + "skills.searchPlaceholder": "Rechercher le catalogue public skills.sh…", + "skills.byAuthor": "v{version} par {author}", + "skills.verified": "Vérifié", + "skills.importToRegistry": "Importer dans le registre", + "skills.emptyRegistry": "Aucune compétence enregistrée. Importez-en une depuis le marketplace ou un dossier local.", + "skills.col.skill": "Compétence", + "skills.col.version": "Version", + "skills.col.status": "Statut", + "skills.col.risk": "Risque", + "skills.col.tags": "Étiquettes", + "skills.col.actions": "Actions", + "skills.publish": "Publier", + "skills.deployCodex": "Déployer vers Codex", + "skills.editorTitle": "Éditeur SKILL.md", + "skills.editorNamePlaceholder": "Nom de la compétence", + "skills.editorVersionPlaceholder": "Version", + "skills.editorDefaultName": "Assistant personnalisé", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "Enregistrer le brouillon", + "skills.previewTitle": "Aperçu rendu en direct", + "skills.matrixTitle": "Matrice de déploiement de flotte", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "Non installé", + "skills.inSync": "v{version} synchronisé", + "skills.blocked": "Bloqué", + "skills.agentsTitle": "Flotte d’agents cibles", + "skills.col.agent": "Agent", + "skills.col.userRoot": "Racine cible (portée utilisateur)", + "skills.col.projectRoot": "Racine cible (portée projet)", + "skills.col.adapterVersion": "Version de l’adaptateur", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "Agent universel", + "skills.statusReady": "Prêt", + "skills.nodesTitle": "Nœuds d’exécution distants (SSH)", + "skills.nodesHelp": "Gérez les VPS distants, les workers GPU et les hôtes de développement avec un épinglage strict des clés d’hôte.", + "skills.col.node": "Nœud", + "skills.col.type": "Type", + "skills.col.host": "Hôte", + "skills.col.environment": "Environnement", + "skills.localMachine": "Machine locale", + "skills.self": "Soi", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "Tester la connexion", + "skills.driftTitle": "Dérive de configuration et restauration", + "skills.driftHelp": "La surveillance continue compare les fichiers disque aux hachages immuables du registre.", + "skills.col.deployment": "Déploiement", + "skills.col.targetPath": "Chemin cible", + "skills.col.driftState": "État de dérive", + "skills.checkHash": "Vérifier le hachage", + "skills.reviewsTitle": "File de politique et d’approbation", + "skills.reviewsEmpty": "Aucune revue élevée en attente n’exige une décision d’opérateur.", + "skills.auditTitle": "Journal d’audit", + "skills.col.eventType": "Type d’événement", + "skills.col.targetId": "ID cible", + "skills.system": "system", + "skills.status.importing": "Importation de {id}…", + "skills.status.imported": "{id} importé.", + "skills.status.importFailed": "Échec de l’importation : {error}", + "skills.status.publishing": "Publication de {id}…", + "skills.status.published": "{id} publié.", + "skills.status.publishFailed": "Échec de la publication : {error}", + "skills.status.planning": "Planification du déploiement de {id} vers {agent}…", + "skills.status.deployed": "{id} déployé vers {agent}.", + "skills.status.deployFailed": "Échec du déploiement : {error}", + "skills.status.saved": "Brouillon de compétence enregistré dans le registre.", + "skills.status.saveFailed": "Échec de l’enregistrement : {error}", + "skills.loadFailed": "Impossible de charger les données du plan de contrôle des compétences.", + "security.title": "Plan de contrôle de sécurité", + "security.subtitle": "Reconnaissance et validation d’abord autorisées. Le travail face à une cible est refusé par défaut. R3 est bloqué. Accessible par hash ; pas une ligne NAV de barre latérale.", + "security.refresh": "Actualiser", + "security.refreshing": "Actualisation…", + "security.banner": "Cibles autorisées uniquement. Les actifs inconnus, les autorisations expirées et les hôtes exclus sont refusés. Les outils tiers en direct ne sont pas invoqués ; la reconnaissance utilise des fixtures locales.", + "security.flagOff": "Le drapeau PAO_SECURITY_CONTROL_PLANE est désactivé — les mutations sont refusées.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "Échec : {error}", + "security.evidence.campaign": "Preuve de campagne : {campaign}", + "security.evidence.none": "Aucune campagne active", + "security.tab.overview": "Aperçu", + "security.tab.authorizations": "Autorisations", + "security.tab.scopes": "Portées", + "security.tab.campaigns": "Campagnes", + "security.tab.approvals": "Approbations", + "security.tab.findings": "Constats", + "security.tab.evidence": "Preuves", + "security.tab.skills": "Paquets", + "security.tab.tools": "Outils", + "security.tab.mcp": "MCP", + "security.tab.policies": "Politiques", + "security.tab.audit": "Audit", + "security.card.enabled": "Activé", + "security.card.campaigns": "Campagnes", + "security.card.approvals": "Approbations", + "security.card.leads": "Pistes", + "security.card.validated": "Validé", + "security.card.blocked": "Bloqué", + "security.card.authExpiring": "Autorisations expirant", + "security.card.breakers": "Disjoncteurs", + "security.yes": "oui", + "security.no": "non", + "security.col.id": "ID", + "security.col.type": "Type", + "security.col.status": "Statut", + "security.col.validUntil": "Valide jusqu’au", + "security.col.name": "Nom", + "security.col.actions": "Actions", + "security.col.target": "Cible", + "security.col.title": "Titre", + "security.col.severity": "Gravité", + "security.col.sha256": "SHA-256", + "security.col.preview": "Aperçu", + "security.col.compatibility": "Compatibilité", + "security.col.kind": "Genre", + "security.col.allowlisted": "Sur liste d’autorisation", + "security.col.health": "Santé", + "security.col.credential": "Identifiant", + "security.col.ceiling": "Plafond", + "security.col.blockR3": "Bloquer R3", + "security.col.when": "Quand", + "security.col.event": "Événement", + "security.col.actor": "Acteur", + "security.start": "Démarrer", + "security.pause": "Pause", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "Approuver une fois", + "security.deny": "Refuser", + "security.reasonBounded": "une fois limitée", + "security.reasonDenied": "refusé", + "security.hashes": "Hashs favoris : {hashes}", + "security.loadFailed": "Impossible de charger les données du plan de contrôle de sécurité.", + "credentials.title": "Plan de contrôle d’accès fournisseur", + "credentials.subtitle": "Identifiants chiffrés, pool de jetons contrôlé, passerelle OAuth et baux gouvernés par politique. Routable par hash ; pas une ligne NAV.", + "credentials.refresh": "Actualiser", + "credentials.refreshing": "Actualisation…", + "credentials.banner": "Les secrets ne sont jamais listés en clair. Les contrôles de santé de cette phase sont des fixtures in-process. Ce plan n’inscrit pas de comptes fournisseur.", + "credentials.flagOff": "Le drapeau CREDENTIAL_RUNTIME_ENABLED est désactivé — les mutations sont refusées.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "Échec : {error}", + "credentials.tab.overview": "Aperçu", + "credentials.tab.list": "Identifiants", + "credentials.tab.providers": "Fournisseurs", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "Pool", + "credentials.tab.health": "Santé", + "credentials.tab.quota": "Quota", + "credentials.tab.policies": "Politiques", + "credentials.tab.approvals": "Approbations", + "credentials.tab.audit": "Audit", + "credentials.card.enabled": "Activé", + "credentials.card.total": "Total", + "credentials.card.healthy": "Sain", + "credentials.card.degraded": "Dégradé", + "credentials.card.quarantined": "Quarantaine", + "credentials.card.revoked": "Révoqué", + "credentials.card.leases": "Baux", + "credentials.card.breakers": "Disjoncteurs", + "credentials.card.vault": "Coffre", + "credentials.yes": "oui", + "credentials.no": "non", + "credentials.available": "disponible", + "credentials.unavailable": "indisponible", + "credentials.col.id": "ID", + "credentials.col.name": "Nom", + "credentials.col.provider": "Fournisseur", + "credentials.col.status": "Statut", + "credentials.col.health": "Santé", + "credentials.col.secret": "Secret", + "credentials.col.eligible": "Éligible", + "credentials.col.actions": "Actions", + "credentials.col.score": "Score", + "credentials.col.adapter": "Adaptateur", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "Activé", + "credentials.col.session": "Session", + "credentials.col.redirect": "Redirection", + "credentials.col.budget": "Budget", + "credentials.col.priority": "Priorité", + "credentials.col.action": "Action", + "credentials.col.target": "Cible", + "credentials.col.when": "Quand", + "credentials.col.event": "Événement", + "credentials.col.actor": "Acteur", + "credentials.col.decision": "Décision", + "credentials.validate": "Valider", + "credentials.quarantine": "Mettre en quarantaine", + "credentials.revoke": "Révoquer", + "credentials.healthRun": "Lancer le contrôle de santé", + "credentials.approve": "Approuver", + "credentials.reject": "Refuser", + "credentials.hashes": "Hashs favoris : {hashes}", + "credentials.loadFailed": "Impossible de charger les données du runtime d’identifiants.", "nav.remote": "Espace distant", "remote.title": "Espace de travail distant", "remote.subtitle": "Codex, Claude Code ou Pi s'exécutent sur ce Hub tandis que fichiers, commandes, tests et builds restent sur l'ordinateur choisi.", @@ -2846,4 +3069,77 @@ export const fr: Record = { "remote.event.status": "État", "remote.event.tool": "Outil distant", "remote.event.error": "Erreur", + "social.title": "Plan de contrôle de publication sociale", + "social.subtitle": "Orchestration sociale agentique, versions multi-plateformes et planification OpenPost.", + "social.refresh": "Actualiser", + "social.refreshing": "Actualisation…", + "social.banner": "Connecté à OpenPost. Les secrets OAuth restent protégés dans OpenPost. Approbation humaine requise.", + "social.flagOff": "Option SOCIAL_PUBLISHING_ENABLED désactivée — mutations refusées.", + "social.tab.overview": "Aperçu", + "social.tab.accounts": "Comptes", + "social.tab.publications": "Publications", + "social.tab.approvals": "Approbations", + "social.tab.jobs": "Tâches d'envoi", + "social.tab.analytics": "Statistiques", + "social.tab.instances": "Instances OpenPost", + "social.tab.audit": "Journal d'audit", + "social.card.enabled": "Activé", + "social.card.instances": "Instances", + "social.card.accounts": "Comptes", + "social.card.readyAccounts": "Comptes prêts", + "social.card.publications": "Publications", + "social.card.scheduled": "Planifié", + "social.card.pendingApprovals": "En attente", + "social.card.failedDeliveries": "Échecs d'envoi", + "social.yes": "oui", + "social.no": "non", + "social.col.id": "ID", + "social.col.platform": "Plateforme", + "social.col.displayName": "Nom affiché", + "social.col.readiness": "État de préparation", + "social.col.enabled": "Activé", + "social.col.actions": "Actions", + "social.col.sourceType": "Type de source", + "social.col.title": "Titre", + "social.col.status": "Statut", + "social.col.scheduledAt": "Date prévue", + "social.col.name": "Nom", + "social.col.baseUrl": "URL de base", + "social.col.version": "Version", + "social.col.pubId": "Publication", + "social.col.jobType": "Type de tâche", + "social.col.attempts": "Tentatives", + "social.col.error": "Erreur", + "social.col.when": "Horodatage", + "social.col.actor": "Acteur", + "social.col.action": "Action", + "social.col.resource": "Ressource", + "social.col.resourceId": "ID de ressource", + "social.col.decision": "Décision", + "social.col.approver": "Approbateur", + "social.col.approvalScope": "Champ d'approbation", + "social.col.contentHash": "Empreinte du contenu", + "social.col.account": "Compte", + "social.col.capturedAt": "Capturé le", + "social.col.views": "Vues", + "social.col.impressions": "Impressions", + "social.col.engagements": "Engagements", + "social.col.followersDelta": "Abonnés Δ", + "social.emptyAccounts": "Aucun compte connecté. Synchronisez depuis OpenPost.", + "social.emptyPublications": "Aucune publication pour le moment.", + "social.emptyInstances": "Aucune instance OpenPost enregistrée.", + "social.emptyJobs": "Aucune tâche d'envoi.", + "social.emptyAudit": "Aucun événement d'audit.", + "social.emptyApprovals": "Aucune approbation enregistrée.", + "social.emptyAnalytics": "Aucun instantané analytique enregistré.", + "social.refreshCaps": "Actualiser capacités", + "social.validate": "Valider", + "social.approve": "Approuver", + "social.publish": "Publier maintenant", + "social.test": "Tester connexion", + "social.syncAccounts": "Synchroniser comptes", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "Échec : {error}", + "social.hashes": "URL mémorisables : {hashes}", }; diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 189149d37e9..5b97eaeecb5 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -73,6 +73,10 @@ export const ja: Record = { "nav.models": "モデル", "nav.combos": "コンボ", "nav.subagents": "サブエージェント", + "nav.skills": "スキル", + "nav.security": "セキュリティ", + "nav.credentials": "資格情報", + "nav.social": "ソーシャル公開", // routing intelligence "routing.title": "ルーティングインテリジェンス (beta)", @@ -2818,6 +2822,225 @@ export const ja: Record = { "models.pickerOrder.saveDraft": "下書きを保存", "models.pickerOrder.reloadDraft": "下書きを破棄して再読み込み", "models.pickerOrder.catalogRequired": "モデルの識別情報が不足しているか曖昧です。モデルページを再読み込みしてカタログを更新してからカスタム順序を編集してください。", + "skills.title": "スキル制御プレーン", + "skills.subtitle": "ユニバーサルエージェントスキル登録、ビジュアルマーケットプレイス、ポリシー管理のデプロイ。", + "skills.refresh": "更新", + "skills.refreshing": "更新中…", + "skills.tab.overview": "概要", + "skills.tab.marketplace": "マーケットプレイス", + "skills.tab.registry": "レジストリ ({count})", + "skills.tab.editor": "スキルエディタ", + "skills.tab.matrix": "デプロイ行列", + "skills.tab.agents": "エージェント", + "skills.tab.nodes": "リモートノード", + "skills.tab.drift": "ドリフトとロールバック", + "skills.tab.reviews": "レビュー待ち", + "skills.tab.audit": "監査ログ", + "skills.card.registered": "登録済みスキル", + "skills.card.deployments": "稼働中のデプロイ", + "skills.card.marketplace": "マーケットプレイス項目", + "skills.card.audit": "監査イベント", + "skills.recentActivity": "最近の制御プレーン活動", + "skills.noEvents": "最近のイベントはありません。", + "skills.col.timestamp": "時刻", + "skills.col.event": "イベント", + "skills.col.actor": "アクター", + "skills.col.target": "対象", + "skills.searchPlaceholder": "公開 skills.sh カタログを検索…", + "skills.byAuthor": "v{version} / {author}", + "skills.verified": "検証済み", + "skills.importToRegistry": "レジストリへ取り込み", + "skills.emptyRegistry": "スキルはまだ登録されていません。マーケットプレイスまたはローカルフォルダから取り込んでください。", + "skills.col.skill": "スキル", + "skills.col.version": "バージョン", + "skills.col.status": "状態", + "skills.col.risk": "リスク", + "skills.col.tags": "タグ", + "skills.col.actions": "操作", + "skills.publish": "公開", + "skills.deployCodex": "Codex へデプロイ", + "skills.editorTitle": "SKILL.md エディタ", + "skills.editorNamePlaceholder": "スキル名", + "skills.editorVersionPlaceholder": "バージョン", + "skills.editorDefaultName": "カスタムアシスタント", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "下書きを保存", + "skills.previewTitle": "ライブプレビュー", + "skills.matrixTitle": "フリートデプロイ行列", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "未インストール", + "skills.inSync": "v{version} 同期済み", + "skills.blocked": "ブロック", + "skills.agentsTitle": "対象エージェントフリート", + "skills.col.agent": "エージェント", + "skills.col.userRoot": "対象ルート(ユーザースコープ)", + "skills.col.projectRoot": "対象ルート(プロジェクトスコープ)", + "skills.col.adapterVersion": "アダプタ版", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "ユニバーサルエージェント", + "skills.statusReady": "準備完了", + "skills.nodesTitle": "リモート実行ノード (SSH)", + "skills.nodesHelp": "ホスト鍵のピン留めを厳守して、リモート VPS、GPU ワーカー、開発ホストを管理します。", + "skills.col.node": "ノード", + "skills.col.type": "種類", + "skills.col.host": "ホスト", + "skills.col.environment": "環境", + "skills.localMachine": "ローカルマシン", + "skills.self": "自身", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "接続テスト", + "skills.driftTitle": "構成ドリフトとロールバック", + "skills.driftHelp": "継続監視はライブディスクファイルを不変のレジストリハッシュと比較します。", + "skills.col.deployment": "デプロイ", + "skills.col.targetPath": "対象パス", + "skills.col.driftState": "ドリフト状態", + "skills.checkHash": "ハッシュ確認", + "skills.reviewsTitle": "ポリシーと承認キュー", + "skills.reviewsEmpty": "オペレータ判断が必要な保留中の昇格レビューはありません。", + "skills.auditTitle": "監査ログ", + "skills.col.eventType": "イベント種別", + "skills.col.targetId": "対象 ID", + "skills.system": "system", + "skills.status.importing": "{id} を取り込み中…", + "skills.status.imported": "{id} を取り込みました。", + "skills.status.importFailed": "取り込み失敗: {error}", + "skills.status.publishing": "{id} を公開中…", + "skills.status.published": "{id} を公開しました。", + "skills.status.publishFailed": "公開失敗: {error}", + "skills.status.planning": "{id} を {agent} へデプロイする計画中…", + "skills.status.deployed": "{id} を {agent} へデプロイしました。", + "skills.status.deployFailed": "デプロイ失敗: {error}", + "skills.status.saved": "スキル下書きをレジストリに保存しました。", + "skills.status.saveFailed": "保存失敗: {error}", + "skills.loadFailed": "スキル制御プレーンのデータを読み込めませんでした。", + "security.title": "セキュリティ制御プレーン", + "security.subtitle": "認可優先の偵察と検証。対象向け作業は既定で拒否。R3 はブロック。ハッシュで到達可能。サイドバー NAV 行ではありません。", + "security.refresh": "更新", + "security.refreshing": "更新中…", + "security.banner": "認可済み対象のみ。未知の資産、期限切れの認可、除外ホストは拒否されます。第三者のライブツールは呼び出しません。偵察はローカルフィクスチャを使います。", + "security.flagOff": "機能フラグ PAO_SECURITY_CONTROL_PLANE はオフです。変更は拒否されます。", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "失敗: {error}", + "security.evidence.campaign": "キャンペーンの証跡: {campaign}", + "security.evidence.none": "アクティブなキャンペーンはありません", + "security.tab.overview": "概要", + "security.tab.authorizations": "認可", + "security.tab.scopes": "スコープ", + "security.tab.campaigns": "キャンペーン", + "security.tab.approvals": "承認", + "security.tab.findings": "検出", + "security.tab.evidence": "証拠", + "security.tab.skills": "パッケージ", + "security.tab.tools": "ツール", + "security.tab.mcp": "MCP", + "security.tab.policies": "ポリシー", + "security.tab.audit": "監査", + "security.card.enabled": "有効", + "security.card.campaigns": "キャンペーン", + "security.card.approvals": "承認", + "security.card.leads": "リード", + "security.card.validated": "検証済み", + "security.card.blocked": "ブロック", + "security.card.authExpiring": "認可の期限切れ間近", + "security.card.breakers": "サーキットブレーカ", + "security.yes": "はい", + "security.no": "いいえ", + "security.col.id": "ID", + "security.col.type": "種類", + "security.col.status": "状態", + "security.col.validUntil": "有効期限", + "security.col.name": "名前", + "security.col.actions": "操作", + "security.col.target": "対象", + "security.col.title": "タイトル", + "security.col.severity": "重大度", + "security.col.sha256": "SHA-256", + "security.col.preview": "プレビュー", + "security.col.compatibility": "互換性", + "security.col.kind": "種別", + "security.col.allowlisted": "許可リスト", + "security.col.health": "健全性", + "security.col.credential": "資格情報", + "security.col.ceiling": "上限", + "security.col.blockR3": "R3 をブロック", + "security.col.when": "日時", + "security.col.event": "イベント", + "security.col.actor": "アクター", + "security.start": "開始", + "security.pause": "一時停止", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "一度だけ承認", + "security.deny": "拒否", + "security.reasonBounded": "一度限り", + "security.reasonDenied": "拒否", + "security.hashes": "ブックマーク可能なハッシュ: {hashes}", + "security.loadFailed": "セキュリティ制御プレーンのデータを読み込めませんでした。", + "credentials.title": "プロバイダーアクセス制御プレーン", + "credentials.subtitle": "暗号化された資格情報、ヘルスチェック済みトークンプール、OAuth ゲートウェイ、ポリシー管理リース。ハッシュで到達可能。サイドバー NAV 行ではありません。", + "credentials.refresh": "更新", + "credentials.refreshing": "更新中…", + "credentials.banner": "秘密は平文では一覧表示されません。このフェーズのヘルスチェックはフィクスチャ/インプロセスです。このプレーンはプロバイダーアカウントを登録しません。", + "credentials.flagOff": "機能フラグ CREDENTIAL_RUNTIME_ENABLED はオフです。変更は拒否されます。", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "失敗: {error}", + "credentials.tab.overview": "概要", + "credentials.tab.list": "資格情報", + "credentials.tab.providers": "プロバイダー", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "プール", + "credentials.tab.health": "健全性", + "credentials.tab.quota": "クォータ", + "credentials.tab.policies": "ポリシー", + "credentials.tab.approvals": "承認", + "credentials.tab.audit": "監査", + "credentials.card.enabled": "有効", + "credentials.card.total": "合計", + "credentials.card.healthy": "正常", + "credentials.card.degraded": "低下", + "credentials.card.quarantined": "隔離", + "credentials.card.revoked": "取り消済み", + "credentials.card.leases": "リース", + "credentials.card.breakers": "ブレーカ", + "credentials.card.vault": "ボールト", + "credentials.yes": "はい", + "credentials.no": "いいえ", + "credentials.available": "利用可", + "credentials.unavailable": "利用不可", + "credentials.col.id": "ID", + "credentials.col.name": "名前", + "credentials.col.provider": "プロバイダー", + "credentials.col.status": "状態", + "credentials.col.health": "健全性", + "credentials.col.secret": "秘密", + "credentials.col.eligible": "適格", + "credentials.col.actions": "操作", + "credentials.col.score": "スコア", + "credentials.col.adapter": "アダプター", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "有効", + "credentials.col.session": "セッション", + "credentials.col.redirect": "リダイレクト", + "credentials.col.budget": "予算", + "credentials.col.priority": "優先度", + "credentials.col.action": "アクション", + "credentials.col.target": "対象", + "credentials.col.when": "日時", + "credentials.col.event": "イベント", + "credentials.col.actor": "アクター", + "credentials.col.decision": "判定", + "credentials.validate": "検証", + "credentials.quarantine": "隔離", + "credentials.revoke": "取り消し", + "credentials.healthRun": "ヘルスパスを実行", + "credentials.approve": "承認", + "credentials.reject": "拒否", + "credentials.hashes": "ブックマーク可能なハッシュ: {hashes}", + "credentials.loadFailed": "資格情報ランタイムのデータを読み込めませんでした。", "nav.remote": "リモートワークスペース", "remote.title": "リモートワークスペース", "remote.subtitle": "Codex、Claude Code、Pi はこの Hub で実行し、ファイル、コマンド、テスト、ビルドは選択したコンピューターで処理します。", @@ -2879,4 +3102,77 @@ export const ja: Record = { "remote.event.status": "状態", "remote.event.tool": "リモートツール", "remote.event.error": "エラー", + "social.title": "ソーシャル公開コントロールプレーン", + "social.subtitle": "エージェント主導のSNS公開オーケストレーション、マルチプラットフォーム版、OpenPost持続的スケジュール管理。", + "social.refresh": "更新", + "social.refreshing": "更新中…", + "social.banner": "OpenPostに接続中。OAuth秘密情報はOpenPost内で保護されます。公開前に人間の承認が必要です。", + "social.flagOff": "機能フラグ SOCIAL_PUBLISHING_ENABLED が無効です — 変更は拒否されます。", + "social.tab.overview": "概要", + "social.tab.accounts": "アカウント", + "social.tab.publications": "公開コンテンツ", + "social.tab.approvals": "承認", + "social.tab.jobs": "配信ジョブ", + "social.tab.analytics": "アナリティクス", + "social.tab.instances": "OpenPostインスタンス", + "social.tab.audit": "監査ログ", + "social.card.enabled": "有効", + "social.card.instances": "インスタンス数", + "social.card.accounts": "アカウント数", + "social.card.readyAccounts": "準備完了", + "social.card.publications": "公開件数", + "social.card.scheduled": "予約済み", + "social.card.pendingApprovals": "承認待ち", + "social.card.failedDeliveries": "配信失敗", + "social.yes": "はい", + "social.no": "いいえ", + "social.col.id": "ID", + "social.col.platform": "プラットフォーム", + "social.col.displayName": "表示名", + "social.col.readiness": "準備状態", + "social.col.enabled": "有効", + "social.col.actions": "アクション", + "social.col.sourceType": "ソース形式", + "social.col.title": "タイトル", + "social.col.status": "状態", + "social.col.scheduledAt": "予約日時", + "social.col.name": "名称", + "social.col.baseUrl": "ベースURL", + "social.col.version": "バージョン", + "social.col.pubId": "公開ID", + "social.col.jobType": "ジョブ種別", + "social.col.attempts": "試行回数", + "social.col.error": "エラー", + "social.col.when": "日時", + "social.col.actor": "アクター", + "social.col.action": "操作", + "social.col.resource": "リソース", + "social.col.resourceId": "リソースID", + "social.col.decision": "判定", + "social.col.approver": "承認者", + "social.col.approvalScope": "承認範囲", + "social.col.contentHash": "コンテンツハッシュ", + "social.col.account": "アカウント", + "social.col.capturedAt": "取得日時", + "social.col.views": "表示回数", + "social.col.impressions": "インプレッション", + "social.col.engagements": "エンゲージメント", + "social.col.followersDelta": "フォロワー増減", + "social.emptyAccounts": "接続アカウントがありません。OpenPostから同期してください。", + "social.emptyPublications": "公開コンテンツはまだありません。", + "social.emptyInstances": "OpenPostインスタンスが登録されていません。", + "social.emptyJobs": "配信ジョブはありません。", + "social.emptyAudit": "監査イベントはありません。", + "social.emptyApprovals": "承認の記録はまだありません。", + "social.emptyAnalytics": "分析スナップショットはまだありません。", + "social.refreshCaps": "機能を更新", + "social.validate": "検証", + "social.approve": "承認", + "social.publish": "今すぐ公開", + "social.test": "接続テスト", + "social.syncAccounts": "アカウント同期", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "失敗: {error}", + "social.hashes": "ブックマーク可能なハッシュ: {hashes}", }; diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 2155fb5bbca..93f81f29788 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -73,6 +73,10 @@ export const ko: Record = { "nav.models": "모델", "nav.combos": "콤보", "nav.subagents": "서브에이전트", + "nav.skills": "스킬", + "nav.security": "보안", + "nav.credentials": "자격 증명", + "nav.social": "소셜 발행", // routing intelligence "routing.title": "라우팅 인텔리전스 (beta)", @@ -2819,6 +2823,225 @@ export const ko: Record = { "models.pickerOrder.saveDraft": "초안 저장", "models.pickerOrder.reloadDraft": "초안 버리고 다시 불러오기", "models.pickerOrder.catalogRequired": "모델 식별 정보가 없거나 모호합니다. 모델 페이지를 새로고침해 목록을 갱신한 뒤 사용자 지정 순서를 편집하세요.", + "skills.title": "스킬 제어 플레인", + "skills.subtitle": "범용 에이전트 스킬 레지스트리, 시각 마켓플레이스, 정책 기반 배포.", + "skills.refresh": "새로고침", + "skills.refreshing": "새로고침 중…", + "skills.tab.overview": "개요", + "skills.tab.marketplace": "마켓플레이스", + "skills.tab.registry": "레지스트리 ({count})", + "skills.tab.editor": "스킬 편집기", + "skills.tab.matrix": "배포 매트릭스", + "skills.tab.agents": "에이전트", + "skills.tab.nodes": "원격 노드", + "skills.tab.drift": "드리프트 및 롤백", + "skills.tab.reviews": "검토 대기열", + "skills.tab.audit": "감사 기록", + "skills.card.registered": "등록된 스킬", + "skills.card.deployments": "활성 배포", + "skills.card.marketplace": "마켓플레이스 항목", + "skills.card.audit": "감사 이벤트", + "skills.recentActivity": "최근 제어 플레인 활동", + "skills.noEvents": "최근 이벤트가 없습니다.", + "skills.col.timestamp": "시각", + "skills.col.event": "이벤트", + "skills.col.actor": "행위자", + "skills.col.target": "대상", + "skills.searchPlaceholder": "공개 skills.sh 카탈로그 검색…", + "skills.byAuthor": "v{version} · {author}", + "skills.verified": "검증됨", + "skills.importToRegistry": "레지스트리로 가져오기", + "skills.emptyRegistry": "등록된 스킬이 없습니다. 마켓플레이스 또는 로컬 폴더에서 가져오세요.", + "skills.col.skill": "스킬", + "skills.col.version": "버전", + "skills.col.status": "상태", + "skills.col.risk": "위험", + "skills.col.tags": "태그", + "skills.col.actions": "작업", + "skills.publish": "게시", + "skills.deployCodex": "Codex에 배포", + "skills.editorTitle": "SKILL.md 편집기", + "skills.editorNamePlaceholder": "스킬 이름", + "skills.editorVersionPlaceholder": "버전", + "skills.editorDefaultName": "사용자 지정 어시스턴트", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "초안 저장", + "skills.previewTitle": "실시간 미리보기", + "skills.matrixTitle": "플릿 배포 매트릭스", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "설치되지 않음", + "skills.inSync": "v{version} 동기화됨", + "skills.blocked": "차단됨", + "skills.agentsTitle": "대상 에이전트 플릿", + "skills.col.agent": "에이전트", + "skills.col.userRoot": "대상 루트(사용자 범위)", + "skills.col.projectRoot": "대상 루트(프로젝트 범위)", + "skills.col.adapterVersion": "어댑터 버전", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "범용 에이전트", + "skills.statusReady": "준비됨", + "skills.nodesTitle": "원격 실행 노드(SSH)", + "skills.nodesHelp": "호스트 키 핀을 엄격히 적용하여 원격 VPS, GPU 워커, 개발 호스트를 관리합니다.", + "skills.col.node": "노드", + "skills.col.type": "유형", + "skills.col.host": "호스트", + "skills.col.environment": "환경", + "skills.localMachine": "로컬 머신", + "skills.self": "자신", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "연결 테스트", + "skills.driftTitle": "구성 드리프트 및 롤백", + "skills.driftHelp": "지속 모니터링은 라이브 디스크 파일을 불변 레지스트리 콘텐츠 해시와 비교합니다.", + "skills.col.deployment": "배포", + "skills.col.targetPath": "대상 경로", + "skills.col.driftState": "드리프트 상태", + "skills.checkHash": "해시 확인", + "skills.reviewsTitle": "정책 및 승인 대기열", + "skills.reviewsEmpty": "운영자 결정이 필요한 보류 중인 승격 검토가 없습니다.", + "skills.auditTitle": "감사 로그", + "skills.col.eventType": "이벤트 유형", + "skills.col.targetId": "대상 ID", + "skills.system": "system", + "skills.status.importing": "{id} 가져오는 중…", + "skills.status.imported": "{id}을(를) 가져왔습니다.", + "skills.status.importFailed": "가져오기 실패: {error}", + "skills.status.publishing": "{id} 게시 중…", + "skills.status.published": "{id}을(를) 게시했습니다.", + "skills.status.publishFailed": "게시 실패: {error}", + "skills.status.planning": "{id}을(를) {agent}에 배포할 계획 중…", + "skills.status.deployed": "{id}을(를) {agent}에 배포했습니다.", + "skills.status.deployFailed": "배포 실패: {error}", + "skills.status.saved": "스킬 초안을 레지스트리에 저장했습니다.", + "skills.status.saveFailed": "저장 실패: {error}", + "skills.loadFailed": "스킬 제어 플레인 데이터를 불러오지 못했습니다.", + "security.title": "보안 제어 플레인", + "security.subtitle": "인가 우선 정찰 및 검증. 대상 작업은 기본적으로 거부됩니다. R3는 차단됩니다. 해시로 이동 가능하며 사이드바 NAV 행이 아닙니다.", + "security.refresh": "새로고침", + "security.refreshing": "새로고침 중…", + "security.banner": "인가된 대상만. 알 수 없는 자산, 만료된 인가, 제외된 호스트는 거부됩니다. 라이브 타사 도구는 호출하지 않으며 정찰은 로컬 픽스처를 사용합니다.", + "security.flagOff": "기능 플래그 PAO_SECURITY_CONTROL_PLANE이 꺼져 있어 변경이 거부됩니다.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "실패: {error}", + "security.evidence.campaign": "캠페인 증거: {campaign}", + "security.evidence.none": "활성 캠페인 없음", + "security.tab.overview": "개요", + "security.tab.authorizations": "인가", + "security.tab.scopes": "범위", + "security.tab.campaigns": "캠페인", + "security.tab.approvals": "승인", + "security.tab.findings": "발견", + "security.tab.evidence": "증거", + "security.tab.skills": "패키지", + "security.tab.tools": "도구", + "security.tab.mcp": "MCP", + "security.tab.policies": "정책", + "security.tab.audit": "감사", + "security.card.enabled": "사용", + "security.card.campaigns": "캠페인", + "security.card.approvals": "승인", + "security.card.leads": "리드", + "security.card.validated": "검증됨", + "security.card.blocked": "차단됨", + "security.card.authExpiring": "인가 만료 임박", + "security.card.breakers": "회로 차단기", + "security.yes": "예", + "security.no": "아니요", + "security.col.id": "ID", + "security.col.type": "유형", + "security.col.status": "상태", + "security.col.validUntil": "유효 기간", + "security.col.name": "이름", + "security.col.actions": "작업", + "security.col.target": "대상", + "security.col.title": "제목", + "security.col.severity": "심각도", + "security.col.sha256": "SHA-256", + "security.col.preview": "미리보기", + "security.col.compatibility": "호환성", + "security.col.kind": "종류", + "security.col.allowlisted": "허용 목록", + "security.col.health": "상태", + "security.col.credential": "자격 증명", + "security.col.ceiling": "상한", + "security.col.blockR3": "R3 차단", + "security.col.when": "시각", + "security.col.event": "이벤트", + "security.col.actor": "행위자", + "security.start": "시작", + "security.pause": "일시 중지", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "한 번만 승인", + "security.deny": "거부", + "security.reasonBounded": "한 번으로 제한", + "security.reasonDenied": "거부됨", + "security.hashes": "북마크 가능한 해시: {hashes}", + "security.loadFailed": "보안 제어 플레인 데이터를 불러오지 못했습니다.", + "credentials.title": "프로바이더 액세스 제어 플레인", + "credentials.subtitle": "암호화된 자격 증명, 상태 점검 토큰 풀, OAuth 게이트웨이, 정책 기반 리스. 해시로 이동 가능하며 사이드바 NAV 행이 아닙니다.", + "credentials.refresh": "새로 고침", + "credentials.refreshing": "새로 고치는 중…", + "credentials.banner": "비밀은 평문으로 목록에 표시되지 않습니다. 이 단계의 상태 점검은 픽스처/인프로세스입니다. 이 플레인은 프로바이더 계정을 등록하지 않습니다.", + "credentials.flagOff": "기능 플래그 CREDENTIAL_RUNTIME_ENABLED가 꺼져 있어 변경이 거부됩니다.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "실패: {error}", + "credentials.tab.overview": "개요", + "credentials.tab.list": "자격 증명", + "credentials.tab.providers": "프로바이더", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "풀", + "credentials.tab.health": "상태", + "credentials.tab.quota": "할당량", + "credentials.tab.policies": "정책", + "credentials.tab.approvals": "승인", + "credentials.tab.audit": "감사", + "credentials.card.enabled": "사용", + "credentials.card.total": "전체", + "credentials.card.healthy": "정상", + "credentials.card.degraded": "저하", + "credentials.card.quarantined": "격리", + "credentials.card.revoked": "취소됨", + "credentials.card.leases": "리스", + "credentials.card.breakers": "차단기", + "credentials.card.vault": "볼트", + "credentials.yes": "예", + "credentials.no": "아니요", + "credentials.available": "사용 가능", + "credentials.unavailable": "사용 불가", + "credentials.col.id": "ID", + "credentials.col.name": "이름", + "credentials.col.provider": "프로바이더", + "credentials.col.status": "상태", + "credentials.col.health": "상태", + "credentials.col.secret": "비밀", + "credentials.col.eligible": "적격", + "credentials.col.actions": "작업", + "credentials.col.score": "점수", + "credentials.col.adapter": "어댑터", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "사용", + "credentials.col.session": "세션", + "credentials.col.redirect": "리디렉션", + "credentials.col.budget": "예산", + "credentials.col.priority": "우선순위", + "credentials.col.action": "작업", + "credentials.col.target": "대상", + "credentials.col.when": "시각", + "credentials.col.event": "이벤트", + "credentials.col.actor": "행위자", + "credentials.col.decision": "결정", + "credentials.validate": "검증", + "credentials.quarantine": "격리", + "credentials.revoke": "취소", + "credentials.healthRun": "상태 점검 실행", + "credentials.approve": "승인", + "credentials.reject": "거부", + "credentials.hashes": "북마크 가능한 해시: {hashes}", + "credentials.loadFailed": "자격 증명 런타임 데이터를 불러오지 못했습니다.", "nav.remote": "원격 워크스페이스", "remote.title": "원격 워크스페이스", "remote.subtitle": "Codex, Claude Code, Pi는 이 Hub에서 실행하고 파일·명령·테스트·빌드는 선택한 컴퓨터에서 처리합니다.", @@ -2880,4 +3103,77 @@ export const ko: Record = { "remote.event.status": "상태", "remote.event.tool": "원격 도구", "remote.event.error": "오류", + "social.title": "소셜 발행 제어 플레인", + "social.subtitle": "에이전트 소셜 발행 오케스트레이션, 다중 플랫폼 렌디션 및 OpenPost 스케줄링.", + "social.refresh": "새로고침", + "social.refreshing": "새로고침 중…", + "social.banner": "OpenPost 연결됨. OAuth 비밀은 OpenPost 내에 보호됩니다. 발행 전 사람의 승인이 필요합니다.", + "social.flagOff": "기능 플래그 SOCIAL_PUBLISHING_ENABLED 꺼짐 — 변경이 거부됩니다.", + "social.tab.overview": "개요", + "social.tab.accounts": "계정", + "social.tab.publications": "발행물", + "social.tab.approvals": "승인", + "social.tab.jobs": "전달 작업", + "social.tab.analytics": "분석", + "social.tab.instances": "OpenPost 인스턴스", + "social.tab.audit": "감사 기록", + "social.card.enabled": "활성화됨", + "social.card.instances": "인스턴스", + "social.card.accounts": "계정 수", + "social.card.readyAccounts": "준비된 계정", + "social.card.publications": "발행물 수", + "social.card.scheduled": "예약됨", + "social.card.pendingApprovals": "승인 대기", + "social.card.failedDeliveries": "실패한 전달", + "social.yes": "예", + "social.no": "아니요", + "social.col.id": "ID", + "social.col.platform": "플랫폼", + "social.col.displayName": "표시 이름", + "social.col.readiness": "준비 상태", + "social.col.enabled": "활성", + "social.col.actions": "작업", + "social.col.sourceType": "소스 유형", + "social.col.title": "제목", + "social.col.status": "상태", + "social.col.scheduledAt": "예약 시간", + "social.col.name": "이름", + "social.col.baseUrl": "기본 URL", + "social.col.version": "버전", + "social.col.pubId": "발행물", + "social.col.jobType": "작업 유형", + "social.col.attempts": "시도", + "social.col.error": "오류", + "social.col.when": "시간", + "social.col.actor": "주체", + "social.col.action": "동작", + "social.col.resource": "리소스", + "social.col.resourceId": "리소스 ID", + "social.col.decision": "결정", + "social.col.approver": "승인자", + "social.col.approvalScope": "승인 범위", + "social.col.contentHash": "콘텐츠 해시", + "social.col.account": "계정", + "social.col.capturedAt": "캡처 시각", + "social.col.views": "조회수", + "social.col.impressions": "노출수", + "social.col.engagements": "참여 수", + "social.col.followersDelta": "팔로워 변동", + "social.emptyAccounts": "연결된 계정이 없습니다. OpenPost에서 동기화하세요.", + "social.emptyPublications": "발행물이 없습니다.", + "social.emptyInstances": "등록된 OpenPost 인스턴스가 없습니다.", + "social.emptyJobs": "전달 작업이 없습니다.", + "social.emptyAudit": "감사 이벤트가 없습니다.", + "social.emptyApprovals": "승인 기록이 아직 없습니다.", + "social.emptyAnalytics": "분석 스냅샷이 아직 없습니다.", + "social.refreshCaps": "기능 갱신", + "social.validate": "검증", + "social.approve": "승인", + "social.publish": "즉시 발행", + "social.test": "연결 테스트", + "social.syncAccounts": "계정 동기화", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "실패: {error}", + "social.hashes": "북마크 해시: {hashes}", }; diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index c25eb206a8d..a27c711195d 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -73,6 +73,10 @@ export const ru: Record = { "nav.models": "Модели", "nav.combos": "Комбо", "nav.subagents": "Подагенты", + "nav.skills": "Навыки", + "nav.security": "Безопасность", + "nav.credentials": "Учётные данные", + "nav.social": "Публикация в соцсетях", // routing intelligence "routing.title": "Интеллект маршрутизации (beta)", @@ -2820,6 +2824,225 @@ export const ru: Record = { "models.pickerOrder.saveDraft": "Сохранить черновик", "models.pickerOrder.reloadDraft": "Перезагрузить и сбросить черновик", "models.pickerOrder.catalogRequired": "Идентификаторы моделей отсутствуют или неоднозначны. Перезагрузите страницу моделей, чтобы обновить каталог перед редактированием порядка.", + "skills.title": "Плоскость управления навыками", + "skills.subtitle": "Универсальный реестр навыков агентов, визуальная витрина и развёртывание под управлением политик.", + "skills.refresh": "Обновить", + "skills.refreshing": "Обновление…", + "skills.tab.overview": "Обзор", + "skills.tab.marketplace": "Витрина", + "skills.tab.registry": "Реестр ({count})", + "skills.tab.editor": "Редактор навыка", + "skills.tab.matrix": "Матрица развёртывания", + "skills.tab.agents": "Агенты", + "skills.tab.nodes": "Удалённые узлы", + "skills.tab.drift": "Дрейф и откат", + "skills.tab.reviews": "Очередь проверки", + "skills.tab.audit": "Журнал аудита", + "skills.card.registered": "Зарегистрированные навыки", + "skills.card.deployments": "Активные развёртывания", + "skills.card.marketplace": "Элементы витрины", + "skills.card.audit": "События аудита", + "skills.recentActivity": "Недавняя активность плоскости управления", + "skills.noEvents": "Недавних событий нет.", + "skills.col.timestamp": "Время", + "skills.col.event": "Событие", + "skills.col.actor": "Субъект", + "skills.col.target": "Цель", + "skills.searchPlaceholder": "Поиск в публичном каталоге skills.sh…", + "skills.byAuthor": "v{version} · {author}", + "skills.verified": "Проверено", + "skills.importToRegistry": "Импортировать в реестр", + "skills.emptyRegistry": "Навыки ещё не зарегистрированы. Импортируйте из витрины или локальной папки.", + "skills.col.skill": "Навык", + "skills.col.version": "Версия", + "skills.col.status": "Статус", + "skills.col.risk": "Риск", + "skills.col.tags": "Теги", + "skills.col.actions": "Действия", + "skills.publish": "Опубликовать", + "skills.deployCodex": "Развернуть в Codex", + "skills.editorTitle": "Редактор SKILL.md", + "skills.editorNamePlaceholder": "Имя навыка", + "skills.editorVersionPlaceholder": "Версия", + "skills.editorDefaultName": "Пользовательский помощник", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "Сохранить черновик", + "skills.previewTitle": "Предпросмотр в реальном времени", + "skills.matrixTitle": "Матрица развёртывания флота", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "Не установлено", + "skills.inSync": "v{version} синхронизировано", + "skills.blocked": "Заблокировано", + "skills.agentsTitle": "Целевой флот агентов", + "skills.col.agent": "Агент", + "skills.col.userRoot": "Корневой путь (пользовательский)", + "skills.col.projectRoot": "Корневой путь (проектный)", + "skills.col.adapterVersion": "Версия адаптера", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "Универсальный агент", + "skills.statusReady": "Готово", + "skills.nodesTitle": "Удалённые узлы выполнения (SSH)", + "skills.nodesHelp": "Управляйте удалёнными VPS, GPU-воркерами и хостами разработки со строгой привязкой ключей хоста.", + "skills.col.node": "Узел", + "skills.col.type": "Тип", + "skills.col.host": "Хост", + "skills.col.environment": "Среда", + "skills.localMachine": "Локальная машина", + "skills.self": "Сама", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "Проверить соединение", + "skills.driftTitle": "Дрейф конфигурации и откат", + "skills.driftHelp": "Непрерывный мониторинг сравнивает файлы на диске с неизменяемыми хешами реестра.", + "skills.col.deployment": "Развёртывание", + "skills.col.targetPath": "Целевой путь", + "skills.col.driftState": "Состояние дрейфа", + "skills.checkHash": "Проверить хеш", + "skills.reviewsTitle": "Очередь политик и утверждений", + "skills.reviewsEmpty": "Нет ожидающих повышенных проверок, требующих решения оператора.", + "skills.auditTitle": "Журнал аудита", + "skills.col.eventType": "Тип события", + "skills.col.targetId": "ID цели", + "skills.system": "system", + "skills.status.importing": "Импорт {id}…", + "skills.status.imported": "{id} импортирован.", + "skills.status.importFailed": "Ошибка импорта: {error}", + "skills.status.publishing": "Публикация {id}…", + "skills.status.published": "{id} опубликован.", + "skills.status.publishFailed": "Ошибка публикации: {error}", + "skills.status.planning": "Планирование развёртывания {id} в {agent}…", + "skills.status.deployed": "{id} развёрнут в {agent}.", + "skills.status.deployFailed": "Ошибка развёртывания: {error}", + "skills.status.saved": "Черновик навыка сохранён в реестре.", + "skills.status.saveFailed": "Ошибка сохранения: {error}", + "skills.loadFailed": "Не удалось загрузить данные плоскости управления навыками.", + "security.title": "Плоскость управления безопасностью", + "security.subtitle": "Разведка и проверка с приоритетом авторизации. Работа с целью по умолчанию запрещена. R3 заблокирован. Доступен по хешу; не строка боковой NAV.", + "security.refresh": "Обновить", + "security.refreshing": "Обновление…", + "security.banner": "Только авторизованные цели. Неизвестные активы, истёкшие авторизации и исключённые хосты отклоняются. Живые сторонние инструменты не вызываются; разведка использует локальные фикстуры.", + "security.flagOff": "Флаг PAO_SECURITY_CONTROL_PLANE выключен — изменения отклоняются.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "Ошибка: {error}", + "security.evidence.campaign": "Доказательства кампании: {campaign}", + "security.evidence.none": "Нет активной кампании", + "security.tab.overview": "Обзор", + "security.tab.authorizations": "Авторизации", + "security.tab.scopes": "Области", + "security.tab.campaigns": "Кампании", + "security.tab.approvals": "Утверждения", + "security.tab.findings": "Находки", + "security.tab.evidence": "Доказательства", + "security.tab.skills": "Пакеты", + "security.tab.tools": "Инструменты", + "security.tab.mcp": "MCP", + "security.tab.policies": "Политики", + "security.tab.audit": "Аудит", + "security.card.enabled": "Включено", + "security.card.campaigns": "Кампании", + "security.card.approvals": "Утверждения", + "security.card.leads": "Лиды", + "security.card.validated": "Проверено", + "security.card.blocked": "Заблокировано", + "security.card.authExpiring": "Авторизация истекает", + "security.card.breakers": "Автоматы", + "security.yes": "да", + "security.no": "нет", + "security.col.id": "ID", + "security.col.type": "Тип", + "security.col.status": "Статус", + "security.col.validUntil": "Действует до", + "security.col.name": "Имя", + "security.col.actions": "Действия", + "security.col.target": "Цель", + "security.col.title": "Заголовок", + "security.col.severity": "Серьёзность", + "security.col.sha256": "SHA-256", + "security.col.preview": "Предпросмотр", + "security.col.compatibility": "Совместимость", + "security.col.kind": "Вид", + "security.col.allowlisted": "В списке разрешённых", + "security.col.health": "Состояние", + "security.col.credential": "Учётные данные", + "security.col.ceiling": "Потолок", + "security.col.blockR3": "Блокировать R3", + "security.col.when": "Когда", + "security.col.event": "Событие", + "security.col.actor": "Субъект", + "security.start": "Запустить", + "security.pause": "Пауза", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "Утвердить один раз", + "security.deny": "Отклонить", + "security.reasonBounded": "один раз", + "security.reasonDenied": "отклонено", + "security.hashes": "Хеши для закладок: {hashes}", + "security.loadFailed": "Не удалось загрузить данные плоскости управления безопасностью.", + "credentials.title": "Плоскость управления доступом к провайдерам", + "credentials.subtitle": "Зашифрованные учётные данные, пул токенов с проверкой здоровья, шлюз OAuth и аренды по политике. Доступ по хешу; не строка боковой NAV.", + "credentials.refresh": "Обновить", + "credentials.refreshing": "Обновление…", + "credentials.banner": "Секреты никогда не показываются открытым текстом. Проверки здоровья на этом этапе — фикстуры in-process. Эта плоскость не регистрирует аккаунты провайдеров.", + "credentials.flagOff": "Флаг CREDENTIAL_RUNTIME_ENABLED выключен — изменения отклоняются.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "Сбой: {error}", + "credentials.tab.overview": "Обзор", + "credentials.tab.list": "Учётные данные", + "credentials.tab.providers": "Провайдеры", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "Пул", + "credentials.tab.health": "Состояние", + "credentials.tab.quota": "Квота", + "credentials.tab.policies": "Политики", + "credentials.tab.approvals": "Утверждения", + "credentials.tab.audit": "Аудит", + "credentials.card.enabled": "Включено", + "credentials.card.total": "Всего", + "credentials.card.healthy": "Здорово", + "credentials.card.degraded": "Деградация", + "credentials.card.quarantined": "Карантин", + "credentials.card.revoked": "Отозвано", + "credentials.card.leases": "Аренды", + "credentials.card.breakers": "Автоматы", + "credentials.card.vault": "Хранилище", + "credentials.yes": "да", + "credentials.no": "нет", + "credentials.available": "доступно", + "credentials.unavailable": "недоступно", + "credentials.col.id": "ID", + "credentials.col.name": "Имя", + "credentials.col.provider": "Провайдер", + "credentials.col.status": "Статус", + "credentials.col.health": "Состояние", + "credentials.col.secret": "Секрет", + "credentials.col.eligible": "Допущен", + "credentials.col.actions": "Действия", + "credentials.col.score": "Оценка", + "credentials.col.adapter": "Адаптер", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "Включено", + "credentials.col.session": "Сессия", + "credentials.col.redirect": "Перенаправление", + "credentials.col.budget": "Бюджет", + "credentials.col.priority": "Приоритет", + "credentials.col.action": "Действие", + "credentials.col.target": "Цель", + "credentials.col.when": "Когда", + "credentials.col.event": "Событие", + "credentials.col.actor": "Субъект", + "credentials.col.decision": "Решение", + "credentials.validate": "Проверить", + "credentials.quarantine": "Карантин", + "credentials.revoke": "Отозвать", + "credentials.healthRun": "Запустить проверку", + "credentials.approve": "Утвердить", + "credentials.reject": "Отклонить", + "credentials.hashes": "Хеши для закладок: {hashes}", + "credentials.loadFailed": "Не удалось загрузить данные среды учётных данных.", "nav.remote": "Удалённое рабочее пространство", "remote.title": "Удалённое рабочее пространство", "remote.subtitle": "Codex, Claude Code или Pi работают на этом Hub, а файлы, команды, тесты и сборки остаются на выбранном компьютере.", @@ -2881,4 +3104,77 @@ export const ru: Record = { "remote.event.status": "Состояние", "remote.event.tool": "Удалённый инструмент", "remote.event.error": "Ошибка", + "social.title": "Панель управления публикацией в соцсетях", + "social.subtitle": "Агентная оркестрация публикаций, мультиплатформенные версии и планирование через OpenPost.", + "social.refresh": "Обновить", + "social.refreshing": "Обновление…", + "social.banner": "Подключено к OpenPost. Секреты OAuth защищены внутри OpenPost. Требуется подтверждение оператора.", + "social.flagOff": "Флаг SOCIAL_PUBLISHING_ENABLED отключен — изменения запрещены.", + "social.tab.overview": "Обзор", + "social.tab.accounts": "Аккаунты", + "social.tab.publications": "Публикации", + "social.tab.approvals": "Согласования", + "social.tab.jobs": "Очередь доставки", + "social.tab.analytics": "Аналитика", + "social.tab.instances": "Инстансы OpenPost", + "social.tab.audit": "Аудит", + "social.card.enabled": "Включено", + "social.card.instances": "Инстансы", + "social.card.accounts": "Аккаунты", + "social.card.readyAccounts": "Готовые аккаунты", + "social.card.publications": "Публикации", + "social.card.scheduled": "Запланировано", + "social.card.pendingApprovals": "Ожидают согласования", + "social.card.failedDeliveries": "Сбои доставки", + "social.yes": "да", + "social.no": "нет", + "social.col.id": "ID", + "social.col.platform": "Платформа", + "social.col.displayName": "Имя", + "social.col.readiness": "Готовность", + "social.col.enabled": "Активно", + "social.col.actions": "Действия", + "social.col.sourceType": "Тип источника", + "social.col.title": "Заголовок", + "social.col.status": "Статус", + "social.col.scheduledAt": "Запланировано на", + "social.col.name": "Имя", + "social.col.baseUrl": "Базовый URL", + "social.col.version": "Версия", + "social.col.pubId": "Публикация", + "social.col.jobType": "Тип задачи", + "social.col.attempts": "Попытки", + "social.col.error": "Ошибка", + "social.col.when": "Время", + "social.col.actor": "Субъект", + "social.col.action": "Действие", + "social.col.resource": "Ресурс", + "social.col.resourceId": "ID ресурса", + "social.col.decision": "Решение", + "social.col.approver": "Утвердивший", + "social.col.approvalScope": "Область утверждения", + "social.col.contentHash": "Хэш контента", + "social.col.account": "Аккаунт", + "social.col.capturedAt": "Снято", + "social.col.views": "Просмотры", + "social.col.impressions": "Показы", + "social.col.engagements": "Вовлечённость", + "social.col.followersDelta": "Δ подписчиков", + "social.emptyAccounts": "Нет подключенных аккаунтов. Синхронизируйте через OpenPost.", + "social.emptyPublications": "Публикаций пока нет.", + "social.emptyInstances": "Инстансы OpenPost не зарегистрированы.", + "social.emptyJobs": "Задачи доставки отсутствуют.", + "social.emptyAudit": "События аудита отсутствуют.", + "social.emptyApprovals": "Утверждения пока не записаны.", + "social.emptyAnalytics": "Аналитические снимки пока отсутствуют.", + "social.refreshCaps": "Обновить возможности", + "social.validate": "Проверить", + "social.approve": "Одобрить", + "social.publish": "Опубликовать сейчас", + "social.test": "Проверить связь", + "social.syncAccounts": "Синхронизировать аккаунты", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "Ошибка: {error}", + "social.hashes": "Хеши для закладок: {hashes}", }; diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts index cfb5ffc5b53..ef9375a580f 100644 --- a/gui/src/i18n/tr.ts +++ b/gui/src/i18n/tr.ts @@ -74,6 +74,10 @@ export const tr: Record = { "nav.models": "Modeller", "nav.combos": "Kombolar", "nav.subagents": "Alt Ajanlar", + "nav.skills": "Yetenekler", + "nav.security": "Güvenlik", + "nav.credentials": "Kimlik bilgileri", + "nav.social": "Sosyal Yayıncılık", "nav.logs": "Günlükler & Hata Ayıklama", "nav.usage": "Kullanım", "common.github": "GitHub", @@ -2820,6 +2824,225 @@ export const tr: Record = { "models.pickerOrder.saveDraft": "Taslağı kaydet", "models.pickerOrder.reloadDraft": "Yeniden yükle ve taslağı sil", "models.pickerOrder.catalogRequired": "Model kimlikleri eksik veya belirsiz. Özel sırayı düzenlemeden önce kataloğu yenilemek için Modeller sayfasını yeniden yükleyin.", + "skills.title": "Yetenek kontrol düzlemi", + "skills.subtitle": "Evrensel ajan yetenek kaydı, görsel pazar yeri ve politika yönetimli dağıtım.", + "skills.refresh": "Yenile", + "skills.refreshing": "Yenileniyor…", + "skills.tab.overview": "Genel bakış", + "skills.tab.marketplace": "Pazar yeri", + "skills.tab.registry": "Kayıt ({count})", + "skills.tab.editor": "Yetenek düzenleyici", + "skills.tab.matrix": "Dağıtım matrisi", + "skills.tab.agents": "Ajanlar", + "skills.tab.nodes": "Uzak düğümler", + "skills.tab.drift": "Sapma ve geri alma", + "skills.tab.reviews": "İnceleme kuyruğu", + "skills.tab.audit": "Denetim izi", + "skills.card.registered": "Kayıtlı yetenekler", + "skills.card.deployments": "Etkin dağıtımlar", + "skills.card.marketplace": "Pazar yeri öğeleri", + "skills.card.audit": "Denetim olayları", + "skills.recentActivity": "Son kontrol düzlemi etkinliği", + "skills.noEvents": "Son olay kaydı yok.", + "skills.col.timestamp": "Zaman", + "skills.col.event": "Olay", + "skills.col.actor": "Aktör", + "skills.col.target": "Hedef", + "skills.searchPlaceholder": "Herkese açık skills.sh kataloğunu ara…", + "skills.byAuthor": "v{version} · {author}", + "skills.verified": "Doğrulandı", + "skills.importToRegistry": "Kayıta içe aktar", + "skills.emptyRegistry": "Henüz kayıtlı yetenek yok. Pazar yerinden veya yerel klasörden içe aktarın.", + "skills.col.skill": "Yetenek", + "skills.col.version": "Sürüm", + "skills.col.status": "Durum", + "skills.col.risk": "Risk", + "skills.col.tags": "Etiketler", + "skills.col.actions": "Eylemler", + "skills.publish": "Yayımla", + "skills.deployCodex": "Codex’e dağıt", + "skills.editorTitle": "SKILL.md düzenleyici", + "skills.editorNamePlaceholder": "Yetenek adı", + "skills.editorVersionPlaceholder": "Sürüm", + "skills.editorDefaultName": "Özel asistan", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "Taslağı kaydet", + "skills.previewTitle": "Canlı önizleme", + "skills.matrixTitle": "Filo dağıtım matrisi", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "Yüklü değil", + "skills.inSync": "v{version} senkron", + "skills.blocked": "Engellendi", + "skills.agentsTitle": "Hedef ajan filosu", + "skills.col.agent": "Ajan", + "skills.col.userRoot": "Hedef kök (kullanıcı kapsamı)", + "skills.col.projectRoot": "Hedef kök (proje kapsamı)", + "skills.col.adapterVersion": "Bağdaştırıcı sürümü", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "Evrensel ajan", + "skills.statusReady": "Hazır", + "skills.nodesTitle": "Uzak yürütme düğümleri (SSH)", + "skills.nodesHelp": "Sıkı anahtar sabitlemesiyle uzak VPS, GPU işçileri ve geliştirme ana makinelerini yönetin.", + "skills.col.node": "Düğüm", + "skills.col.type": "Tür", + "skills.col.host": "Ana makine", + "skills.col.environment": "Ortam", + "skills.localMachine": "Yerel makine", + "skills.self": "Kendisi", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "Bağlantıyı sına", + "skills.driftTitle": "Yapılandırma sapması ve geri alma", + "skills.driftHelp": "Sürekli izleme, diskteki canlı dosyaları değişmez kayıt içerik özetleriyle karşılaştırır.", + "skills.col.deployment": "Dağıtım", + "skills.col.targetPath": "Hedef yol", + "skills.col.driftState": "Sapma durumu", + "skills.checkHash": "Özeti denetle", + "skills.reviewsTitle": "Politika ve onay kuyruğu", + "skills.reviewsEmpty": "Operatör kararı gerektiren bekleyen yükseltilmiş inceleme yok.", + "skills.auditTitle": "Denetim günlüğü", + "skills.col.eventType": "Olay türü", + "skills.col.targetId": "Hedef kimliği", + "skills.system": "system", + "skills.status.importing": "{id} içe aktarılıyor…", + "skills.status.imported": "{id} içe aktarıldı.", + "skills.status.importFailed": "İçe aktarma başarısız: {error}", + "skills.status.publishing": "{id} yayımlanıyor…", + "skills.status.published": "{id} yayımlandı.", + "skills.status.publishFailed": "Yayımlama başarısız: {error}", + "skills.status.planning": "{id} {agent} için dağıtım planlanıyor…", + "skills.status.deployed": "{id} {agent} üzerine dağıtıldı.", + "skills.status.deployFailed": "Dağıtım başarısız: {error}", + "skills.status.saved": "Yetenek taslağı kayda kaydedildi.", + "skills.status.saveFailed": "Kaydetme başarısız: {error}", + "skills.loadFailed": "Yetenek kontrol düzlemi verileri yüklenemedi.", + "security.title": "Güvenlik kontrol düzlemi", + "security.subtitle": "Yetkilendirme öncelikli keşif ve doğrulama. Hedefe yönelik iş varsayılan olarak reddedilir. R3 engellenir. Hash ile erişilebilir; kenar çubuğu NAV satırı değildir.", + "security.refresh": "Yenile", + "security.refreshing": "Yenileniyor…", + "security.banner": "Yalnızca yetkilendirilmiş hedefler. Bilinmeyen varlıklar, süresi dolmuş yetkiler ve dışlanan ana makineler reddedilir. Canlı üçüncü taraf araçlar çağrılmaz; keşif yerel fikstürler kullanır.", + "security.flagOff": "PAO_SECURITY_CONTROL_PLANE özelliği kapalı — değişiklikler reddedilir.", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "Başarısız: {error}", + "security.evidence.campaign": "Kampanya kanıtı: {campaign}", + "security.evidence.none": "Aktif kampanya yok", + "security.tab.overview": "Genel bakış", + "security.tab.authorizations": "Yetkiler", + "security.tab.scopes": "Kapsamlar", + "security.tab.campaigns": "Kampanyalar", + "security.tab.approvals": "Onaylar", + "security.tab.findings": "Bulgular", + "security.tab.evidence": "Kanıt", + "security.tab.skills": "Paketler", + "security.tab.tools": "Araçlar", + "security.tab.mcp": "MCP", + "security.tab.policies": "Politikalar", + "security.tab.audit": "Denetim", + "security.card.enabled": "Etkin", + "security.card.campaigns": "Kampanyalar", + "security.card.approvals": "Onaylar", + "security.card.leads": "Adaylar", + "security.card.validated": "Doğrulandı", + "security.card.blocked": "Engellendi", + "security.card.authExpiring": "Yetki süresi doluyor", + "security.card.breakers": "Kesiciler", + "security.yes": "evet", + "security.no": "hayır", + "security.col.id": "ID", + "security.col.type": "Tür", + "security.col.status": "Durum", + "security.col.validUntil": "Geçerlilik sonu", + "security.col.name": "Ad", + "security.col.actions": "Eylemler", + "security.col.target": "Hedef", + "security.col.title": "Başlık", + "security.col.severity": "Önem", + "security.col.sha256": "SHA-256", + "security.col.preview": "Önizleme", + "security.col.compatibility": "Uyumluluk", + "security.col.kind": "Tür", + "security.col.allowlisted": "İzin listesinde", + "security.col.health": "Sağlık", + "security.col.credential": "Kimlik bilgisi", + "security.col.ceiling": "Tavan", + "security.col.blockR3": "R3’ü engelle", + "security.col.when": "Ne zaman", + "security.col.event": "Olay", + "security.col.actor": "Aktör", + "security.start": "Başlat", + "security.pause": "Duraklat", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "Bir kez onayla", + "security.deny": "Reddet", + "security.reasonBounded": "bir kez sınırlı", + "security.reasonDenied": "reddedildi", + "security.hashes": "Yer imi hash’leri: {hashes}", + "security.loadFailed": "Güvenlik kontrol düzlemi verileri yüklenemedi.", + "credentials.title": "Sağlayıcı erişim kontrol düzlemi", + "credentials.subtitle": "Şifreli kimlik bilgileri, sağlık denetimli jeton havuzu, OAuth ağ geçidi ve politika yönetimli kiralamalar. Hash ile yönlendirilebilir; kenar çubuğu NAV satırı değil.", + "credentials.refresh": "Yenile", + "credentials.refreshing": "Yenileniyor…", + "credentials.banner": "Gizler asla düz metin olarak listelenmez. Bu aşamadaki sağlık denetimleri fikstür/in-process’tir. Bu düzlem sağlayıcı hesabı kaydetmez.", + "credentials.flagOff": "CREDENTIAL_RUNTIME_ENABLED bayrağı kapalı — değişiklikler reddedilir.", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "Başarısız: {error}", + "credentials.tab.overview": "Genel bakış", + "credentials.tab.list": "Kimlik bilgileri", + "credentials.tab.providers": "Sağlayıcılar", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "Havuz", + "credentials.tab.health": "Sağlık", + "credentials.tab.quota": "Kota", + "credentials.tab.policies": "Politikalar", + "credentials.tab.approvals": "Onaylar", + "credentials.tab.audit": "Denetim", + "credentials.card.enabled": "Etkin", + "credentials.card.total": "Toplam", + "credentials.card.healthy": "Sağlıklı", + "credentials.card.degraded": "Düşük", + "credentials.card.quarantined": "Karantina", + "credentials.card.revoked": "İptal", + "credentials.card.leases": "Kiralamalar", + "credentials.card.breakers": "Kesiciler", + "credentials.card.vault": "Kasa", + "credentials.yes": "evet", + "credentials.no": "hayır", + "credentials.available": "kullanılabilir", + "credentials.unavailable": "kullanılamaz", + "credentials.col.id": "ID", + "credentials.col.name": "Ad", + "credentials.col.provider": "Sağlayıcı", + "credentials.col.status": "Durum", + "credentials.col.health": "Sağlık", + "credentials.col.secret": "Giz", + "credentials.col.eligible": "Uygun", + "credentials.col.actions": "Eylemler", + "credentials.col.score": "Puan", + "credentials.col.adapter": "Bağdaştırıcı", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "Etkin", + "credentials.col.session": "Oturum", + "credentials.col.redirect": "Yönlendirme", + "credentials.col.budget": "Bütçe", + "credentials.col.priority": "Öncelik", + "credentials.col.action": "Eylem", + "credentials.col.target": "Hedef", + "credentials.col.when": "Ne zaman", + "credentials.col.event": "Olay", + "credentials.col.actor": "Aktör", + "credentials.col.decision": "Karar", + "credentials.validate": "Doğrula", + "credentials.quarantine": "Karantinaya al", + "credentials.revoke": "İptal et", + "credentials.healthRun": "Sağlık geçişini çalıştır", + "credentials.approve": "Onayla", + "credentials.reject": "Reddet", + "credentials.hashes": "Yer imi hash’leri: {hashes}", + "credentials.loadFailed": "Kimlik bilgisi çalışma zamanı verileri yüklenemedi.", "nav.remote": "Uzak Çalışma Alanı", "remote.title": "Uzak Çalışma Alanı", "remote.subtitle": "Codex, Claude Code veya Pi bu Hub üzerinde çalışır; dosyalar, komutlar, testler ve derlemeler seçtiğiniz bilgisayarda kalır.", @@ -2881,4 +3104,77 @@ export const tr: Record = { "remote.event.status": "Durum", "remote.event.tool": "Uzak araç", "remote.event.error": "Hata", + "social.title": "Sosyal Medya Yayın Kontrol Paneli", + "social.subtitle": "Ajan tabanlı sosyal yayın yönetimi, çoklu platform uyarlamaları ve OpenPost zamanlama.", + "social.refresh": "Yenile", + "social.refreshing": "Yenileniyor…", + "social.banner": "OpenPost sunucusuna bağlı. OAuth sırları OpenPost içinde korunur. Yayın öncesi insan onayı gerekir.", + "social.flagOff": "SOCIAL_PUBLISHING_ENABLED özelliği kapalı — değişiklikler reddedilir.", + "social.tab.overview": "Genel Bakış", + "social.tab.accounts": "Hesaplar", + "social.tab.publications": "Yayınlar", + "social.tab.approvals": "Onaylar", + "social.tab.jobs": "Teslimat İşleri", + "social.tab.analytics": "Analitik", + "social.tab.instances": "OpenPost Sunucuları", + "social.tab.audit": "Denetim Kaydı", + "social.card.enabled": "Etkin", + "social.card.instances": "Sunucular", + "social.card.accounts": "Hesaplar", + "social.card.readyAccounts": "Hazır Hesaplar", + "social.card.publications": "Yayınlar", + "social.card.scheduled": "Zamanlanmış", + "social.card.pendingApprovals": "Onay Bekleyenler", + "social.card.failedDeliveries": "Başarısız Gönderimler", + "social.yes": "evet", + "social.no": "hayır", + "social.col.id": "Kimlik", + "social.col.platform": "Platform", + "social.col.displayName": "Görünen Ad", + "social.col.readiness": "Hazırlık", + "social.col.enabled": "Etkin", + "social.col.actions": "İşlemler", + "social.col.sourceType": "Kaynak Türü", + "social.col.title": "Başlık", + "social.col.status": "Durum", + "social.col.scheduledAt": "Zamanlanan Tarih", + "social.col.name": "Ad", + "social.col.baseUrl": "Temel URL", + "social.col.version": "Sürüm", + "social.col.pubId": "Yayın", + "social.col.jobType": "İş Türü", + "social.col.attempts": "Denemeler", + "social.col.error": "Hata", + "social.col.when": "Zaman", + "social.col.actor": "Aktör", + "social.col.action": "İşlem", + "social.col.resource": "Kaynak", + "social.col.resourceId": "Kaynak Kimliği", + "social.col.decision": "Karar", + "social.col.approver": "Onaylayan", + "social.col.approvalScope": "Onay Kapsamı", + "social.col.contentHash": "İçerik Özeti", + "social.col.account": "Hesap", + "social.col.capturedAt": "Alınma Tarihi", + "social.col.views": "Görüntülenme", + "social.col.impressions": "Gösterim", + "social.col.engagements": "Etkileşim", + "social.col.followersDelta": "Takipçi Δ", + "social.emptyAccounts": "Bağlı hesap bulunamadı. OpenPost üzerinden senkronize edin.", + "social.emptyPublications": "Henüz yayın oluşturulmadı.", + "social.emptyInstances": "Kayıtlı OpenPost sunucusu yok.", + "social.emptyJobs": "Teslimat işi kaydı yok.", + "social.emptyAudit": "Denetim kaydı yok.", + "social.emptyApprovals": "Henüz onay kaydı yok.", + "social.emptyAnalytics": "Henüz analiz görüntüsü yok.", + "social.refreshCaps": "Özellikleri yenile", + "social.validate": "Doğrula", + "social.approve": "Onayla", + "social.publish": "Şimdi yayınla", + "social.test": "Bağlantıyı sına", + "social.syncAccounts": "Hesapları senkronize et", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "Başarısız: {error}", + "social.hashes": "Yer imi hash'leri: {hashes}", }; diff --git a/gui/src/i18n/vi.ts b/gui/src/i18n/vi.ts index 53b1d451804..0dcfec385e9 100644 --- a/gui/src/i18n/vi.ts +++ b/gui/src/i18n/vi.ts @@ -2850,4 +2850,300 @@ export const vi: Record = { "models.fastRows.disabled": "Đã tắt hàng model Fast.", "models.fastRows.loadFailed": "Không thể tải cài đặt hàng Fast.", "models.fastRows.updateFailed": "Không thể cập nhật cài đặt hàng Fast.", + "credentials.approve": "Phê duyệt", + "credentials.available": "Khả dụng", + "credentials.banner": "Chỉ dấu vết: thông tin xác thực được mã hóa AES-256-GCM; cổng OAuth là bản dùng thử; không đăng ký tài khoản nhà cung cấp.", + "credentials.card.breakers": "Bộ ngắt mạch", + "credentials.card.degraded": "Suy giảm", + "credentials.card.enabled": "Đã bật", + "credentials.card.healthy": "Khỏe mạnh", + "credentials.card.leases": "Hợp đồng thuê", + "credentials.card.quarantined": "Cách ly", + "credentials.card.revoked": "Thu hồi", + "credentials.card.total": "Tổng", + "credentials.card.vault": "Kho khóa", + "credentials.col.action": "Hành động", + "credentials.col.actions": "Hành động", + "credentials.col.actor": "Chủ thể", + "credentials.col.adapter": "Bộ điều hợp", + "credentials.col.budget": "Ngân sách", + "credentials.col.decision": "Quyết định", + "credentials.col.eligible": "Đủ điều kiện", + "credentials.col.enabled": "Đã bật", + "credentials.col.event": "Sự kiện", + "credentials.col.health": "Sức khỏe", + "credentials.col.id": "ID", + "credentials.col.name": "Tên", + "credentials.col.oauth": "OAuth", + "credentials.col.priority": "Ưu tiên", + "credentials.col.provider": "Nhà cung cấp", + "credentials.col.redirect": "Chuyển hướng", + "credentials.col.score": "Điểm", + "credentials.col.secret": "Bí mật", + "credentials.col.session": "Phiên", + "credentials.col.status": "Trạng thái", + "credentials.col.target": "Mục tiêu", + "credentials.col.when": "Khi", + "credentials.flagOff": "Cờ tính năng CREDENTIAL_RUNTIME_ENABLED tắt — từ chối thay đổi.", + "credentials.hashes": "Băm có thể đánh dấu: {hashes}", + "credentials.healthRun": "Chạy kiểm tra sức khỏe", + "credentials.loadFailed": "Không thể tải dữ liệu bảng điều khiển thông tin xác thực.", + "credentials.no": "không", + "credentials.quarantine": "Cách ly", + "credentials.refresh": "Làm mới", + "credentials.refreshing": "Đang làm mới…", + "credentials.reject": "Từ chối", + "credentials.revoke": "Thu hồi", + "credentials.status.failed": "Không thành công: {error}", + "credentials.status.ok": "OK {path}", + "credentials.status.posting": "POST {path}", + "credentials.subtitle": "Thông tin xác thực được mã hóa, nhóm mã thông báo được kiểm tra sức khỏe, cổng OAuth và hợp đồng thuê theo chính sách. Có thể định tuyến bằng băm; không phải hàng NAV bên cạnh.", + "credentials.tab.approvals": "Phê duyệt", + "credentials.tab.audit": "Kiểm toán", + "credentials.tab.health": "Sức khỏe", + "credentials.tab.list": "Danh sách", + "credentials.tab.oauth": "OAuth", + "credentials.tab.overview": "Tổng quan", + "credentials.tab.policies": "Chính sách", + "credentials.tab.pool": "Nhóm", + "credentials.tab.providers": "Nhà cung cấp", + "credentials.tab.quota": "Hạn mức", + "credentials.title": "Bảng điều khiển Truy cập Nhà cung cấp", + "credentials.unavailable": "Không khả dụng", + "credentials.validate": "Xác thực", + "credentials.yes": "có", + "nav.credentials": "Thông tin xác thực", + "nav.security": "An ninh", + "nav.skills": "Kỹ năng", + "nav.social": "Xuất bản mạng xã hội", + "security.approveOnce": "Phê duyệt một lần", + "security.banner": "Chỉ mục tiêu được ủy quyền. Tài sản không xác định, ủy quyền hết hạn và máy chủ bị loại trừ đều bị từ chối. Công cụ bên thứ ba trực tiếp không được gọi; trinh sát dùng dữ liệu mẫu cục bộ.", + "security.card.approvals": "Phê duyệt", + "security.card.authExpiring": "Ủy quyền sắp hết hạn", + "security.card.blocked": "Bị chặn", + "security.card.breakers": "Bộ ngắt mạch", + "security.card.campaigns": "Chiến dịch", + "security.card.enabled": "Đã bật", + "security.card.leads": "Khách tiềm năng", + "security.card.validated": "Đã xác thực", + "security.col.actions": "Hành động", + "security.col.actor": "Chủ thể", + "security.col.allowlisted": "Danh sách cho phép", + "security.col.blockR3": "Chặn R3", + "security.col.ceiling": "Trần", + "security.col.compatibility": "Tương thích", + "security.col.credential": "Thông tin xác thực", + "security.col.event": "Sự kiện", + "security.col.health": "Sức khỏe", + "security.col.id": "ID", + "security.col.kind": "Loại", + "security.col.name": "Tên", + "security.col.preview": "Xem trước", + "security.col.severity": "Mức độ nghiêm trọng", + "security.col.sha256": "SHA-256", + "security.col.status": "Trạng thái", + "security.col.target": "Mục tiêu", + "security.col.title": "Tiêu đề", + "security.col.type": "Loại", + "security.col.validUntil": "Có hiệu lực đến", + "security.col.when": "Khi", + "security.deny": "Từ chối", + "security.evidence.campaign": "Bằng chứng chiến dịch: {campaign}", + "security.evidence.none": "Không có chiến dịch đang hoạt động", + "security.flagOff": "Cờ tính năng PAO_SECURITY_CONTROL_PLANE tắt — từ chối thay đổi.", + "security.hashes": "Băm có thể đánh dấu: {hashes}", + "security.loadFailed": "Không thể tải dữ liệu bảng điều khiển an ninh.", + "security.no": "không", + "security.pause": "Tạm dừng", + "security.reasonBounded": "giới hạn một lần", + "security.reasonDenied": "đã từ chối", + "security.reconLab": "Trinh sát lab.local", + "security.refresh": "Làm mới", + "security.refreshing": "Đang làm mới…", + "security.start": "Bắt đầu", + "security.status.failed": "Không thành công: {error}", + "security.status.ok": "OK {path}", + "security.status.posting": "POST {path}", + "security.subtitle": "Trinh sát và xác thực theo ủy quyền trước. Công việc hướng tới mục tiêu bị từ chối theo mặc định. R3 bị chặn. Có thể định tuyến bằng băm; không phải hàng NAV bên cạnh.", + "security.tab.approvals": "Phê duyệt", + "security.tab.audit": "Kiểm toán", + "security.tab.authorizations": "Ủy quyền", + "security.tab.campaigns": "Chiến dịch", + "security.tab.evidence": "Bằng chứng", + "security.tab.findings": "Phát hiện", + "security.tab.mcp": "MCP", + "security.tab.overview": "Tổng quan", + "security.tab.policies": "Chính sách", + "security.tab.scopes": "Phạm vi", + "security.tab.skills": "Gói", + "security.tab.tools": "Công cụ", + "security.title": "Bảng điều khiển An ninh", + "security.yes": "có", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.codex": "Codex", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.agent.universalAgent": "Universal Agent", + "skills.agentsTitle": "Đại lý đã cấu hình", + "skills.auditTitle": "Nhật ký kiểm toán", + "skills.blocked": "Bị chặn", + "skills.byAuthor": "v{version} của {author}", + "skills.card.audit": "Sự kiện kiểm toán", + "skills.card.deployments": "Triển khai đang hoạt động", + "skills.card.marketplace": "Mục thị trường", + "skills.card.registered": "Kỹ năng đã đăng ký", + "skills.checkHash": "Kiểm tra băm", + "skills.col.actions": "Hành động", + "skills.col.actor": "Chủ thể", + "skills.col.adapterVersion": "Phiên bản bộ điều hợp", + "skills.col.agent": "Đại lý", + "skills.col.deployment": "Triển khai", + "skills.col.driftState": "Trạng thái lệch cấu hình", + "skills.col.environment": "Môi trường", + "skills.col.event": "Sự kiện", + "skills.col.eventType": "Loại sự kiện", + "skills.col.host": "Máy chủ", + "skills.col.node": "Nút", + "skills.col.projectRoot": "Thư mục gốc dự án", + "skills.col.risk": "Rủi ro", + "skills.col.skill": "Kỹ năng", + "skills.col.status": "Trạng thái", + "skills.col.tags": "Thẻ", + "skills.col.target": "Mục tiêu", + "skills.col.targetId": "ID mục tiêu", + "skills.col.targetPath": "Đường dẫn mục tiêu", + "skills.col.timestamp": "Dấu thời gian", + "skills.col.type": "Loại", + "skills.col.userRoot": "Thư mục gốc người dùng", + "skills.col.version": "Phiên bản", + "skills.deployCodex": "Triển khai tới Codex", + "skills.driftHelp": "Kiểm tra các tệp đã triển khai so với SHA-256 mong đợi; khôi phục các bản triển khai lệch cấu hình về ảnh chụp lưu trữ.", + "skills.driftTitle": "Lệch cấu hình & Khôi phục", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.editorDefaultName": "Trợ lý tùy chỉnh", + "skills.editorNamePlaceholder": "Tên kỹ năng", + "skills.editorTitle": "Trình biên tập SKILL.md", + "skills.editorVersionPlaceholder": "Phiên bản", + "skills.emptyRegistry": "Chưa có kỹ năng nào được đăng ký. Hãy nhập từ Thị trường hoặc thư mục cục bộ.", + "skills.importToRegistry": "Nhập vào đăng ký", + "skills.inSync": "Đồng bộ v{version}", + "skills.loadFailed": "Không thể tải dữ liệu bảng điều khiển kỹ năng.", + "skills.localMachine": "Máy cục bộ", + "skills.matrixTitle": "Ma trận triển khai hạm đội", + "skills.noEvents": "Chưa có sự kiện gần đây nào được ghi lại.", + "skills.nodesHelp": "Các nút SSH xác minh dấu vân tay khóa máy chủ được cấu hình tại đây. Các nút bắt đầu ở trạng thái chưa xác minh.", + "skills.nodesTitle": "Nút từ xa", + "skills.notInstalled": "Chưa cài đặt", + "skills.previewTitle": "Xem trước trực tiếp", + "skills.publish": "Xuất bản", + "skills.recentActivity": "Hoạt động bảng điều khiển gần đây", + "skills.refresh": "Làm mới", + "skills.refreshing": "Đang làm mới…", + "skills.reviewsEmpty": "Không có kỹ năng nào cần đánh giá.", + "skills.reviewsTitle": "Hàng đợi đánh giá", + "skills.saveDraft": "Lưu bản nháp", + "skills.searchPlaceholder": "Tìm kiếm danh mục skills.sh công khai…", + "skills.self": "tự thân", + "skills.status.deployFailed": "Không thể triển khai: {error}", + "skills.status.deployed": "Đã triển khai {id} tới {agent}", + "skills.status.importFailed": "Không thể nhập: {error}", + "skills.status.imported": "Đã nhập {id}", + "skills.status.importing": "Đang nhập {id}…", + "skills.status.planning": "Đang lập kế hoạch triển khai {id} tới {agent}…", + "skills.status.publishFailed": "Không thể xuất bản: {error}", + "skills.status.published": "Đã xuất bản {id}", + "skills.status.publishing": "Đang xuất bản {id}…", + "skills.status.saveFailed": "Không thể lưu: {error}", + "skills.status.saved": "Đã lưu bản nháp", + "skills.statusReady": "Sẵn sàng", + "skills.subtitle": "Kho lưu trữ Kỹ năng đại lý toàn cầu, thị trường trực quan và triển khai theo chính sách.", + "skills.system": "hệ thống", + "skills.tab.agents": "Đại lý", + "skills.tab.audit": "Nhật ký kiểm toán", + "skills.tab.drift": "Lệch cấu hình & Khôi phục", + "skills.tab.editor": "Trình biên tập Kỹ năng", + "skills.tab.marketplace": "Thị trường", + "skills.tab.matrix": "Ma trận triển khai", + "skills.tab.nodes": "Nút từ xa", + "skills.tab.overview": "Tổng quan", + "skills.tab.registry": "Đăng ký ({count})", + "skills.tab.reviews": "Hàng đợi đánh giá", + "skills.testConnection": "Kiểm tra kết nối", + "skills.title": "Bảng điều khiển Kỹ năng", + "skills.verified": "Đã xác minh", + "skills.vpsMain": "VPS chính", + "social.approve": "Phê duyệt", + "social.banner": "Pao-hubPro sở hữu việc điều phối, phê duyệt, chính sách, lập kế hoạch phiên bản và nhật ký kiểm toán. OpenPost là một dịch vụ AGPL-3.0 riêng biệt sở hữu mã thông báo OAuth và thực hiện xuất bản.", + "social.card.accounts": "Tài khoản", + "social.card.enabled": "Đã bật", + "social.card.failedDeliveries": "Gửi thất bại", + "social.card.instances": "Phiên bản OpenPost", + "social.card.pendingApprovals": "Phê duyệt đang chờ", + "social.card.publications": "Xuất bản", + "social.card.readyAccounts": "Tài khoản sẵn sàng", + "social.card.scheduled": "Đã lên lịch", + "social.col.account": "Tài khoản", + "social.col.action": "Hành động", + "social.col.actions": "Hành động", + "social.col.actor": "Chủ thể", + "social.col.approvalScope": "Phạm vi phê duyệt", + "social.col.approver": "Người phê duyệt", + "social.col.attempts": "Lần thử", + "social.col.baseUrl": "URL cơ sở", + "social.col.capturedAt": "Chụp lúc", + "social.col.contentHash": "Băm nội dung", + "social.col.decision": "Quyết định", + "social.col.displayName": "Tên hiển thị", + "social.col.enabled": "Đã bật", + "social.col.engagements": "Tương tác", + "social.col.error": "Lỗi", + "social.col.followersDelta": "Người theo dõi Δ", + "social.col.id": "ID", + "social.col.impressions": "Hiển thị", + "social.col.jobType": "Loại công việc", + "social.col.name": "Tên", + "social.col.platform": "Nền tảng", + "social.col.pubId": "Xuất bản", + "social.col.readiness": "Sẵn sàng", + "social.col.resource": "Tài nguyên", + "social.col.resourceId": "ID tài nguyên", + "social.col.scheduledAt": "Lên lịch lúc", + "social.col.sourceType": "Loại nguồn", + "social.col.status": "Trạng thái", + "social.col.title": "Tiêu đề", + "social.col.version": "Phiên bản", + "social.col.views": "Lượt xem", + "social.col.when": "Khi", + "social.emptyAccounts": "Chưa có tài khoản mạng xã hội được kết nối. Đồng bộ tài khoản từ một phiên bản OpenPost.", + "social.emptyAnalytics": "Chưa có ảnh chụp phân tích nào được ghi lại.", + "social.emptyApprovals": "Chưa có phê duyệt nào được ghi lại.", + "social.emptyAudit": "Chưa có sự kiện kiểm toán mạng xã hội nào được ghi lại.", + "social.emptyInstances": "Chưa có phiên bản OpenPost nào được đăng ký.", + "social.emptyJobs": "Chưa có công việc gửi nào được ghi lại.", + "social.emptyPublications": "Chưa có xuất bản nào được tạo.", + "social.flagOff": "Cờ tính năng SOCIAL_PUBLISHING_ENABLED tắt — từ chối thay đổi.", + "social.hashes": "Băm có thể đánh dấu: {hashes}", + "social.no": "không", + "social.publish": "Xuất bản", + "social.refresh": "Làm mới", + "social.refreshCaps": "Làm mới khả năng", + "social.refreshing": "Đang làm mới…", + "social.status.failed": "Không thành công: {error}", + "social.status.ok": "OK {path}", + "social.status.posting": "POST {path}", + "social.subtitle": "Lập kế hoạch, phê duyệt và xuất bản có kiểm soát tới các tài khoản mạng xã hội của bạn thông qua OpenPost. Có thể định tuyến bằng băm; không phải hàng NAV bên cạnh.", + "social.syncAccounts": "Đồng bộ tài khoản", + "social.tab.accounts": "Tài khoản", + "social.tab.analytics": "Phân tích", + "social.tab.approvals": "Phê duyệt", + "social.tab.audit": "Kiểm toán", + "social.tab.instances": "Phiên bản", + "social.tab.jobs": "Công việc", + "social.tab.overview": "Tổng quan", + "social.tab.publications": "Xuất bản", + "social.test": "Kiểm tra", + "social.title": "Xuất bản Mạng xã hội", + "social.validate": "Xác thực", + "social.yes": "có", }; diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts index 7f91a508391..3d3dbb03976 100644 --- a/gui/src/i18n/zh-TW.ts +++ b/gui/src/i18n/zh-TW.ts @@ -66,6 +66,10 @@ export const zhTW: Record = { "nav.models": "模型", "nav.combos": "組合", "nav.subagents": "子代理", + "nav.skills": "技能", + "nav.security": "安全", + "nav.credentials": "憑證", + "nav.social": "社群發布", "nav.logs": "日誌與除錯", "nav.usage": "用量", "common.github": "GitHub", @@ -2783,6 +2787,225 @@ export const zhTW: Record = { "models.pickerOrder.saveDraft": "儲存草稿", "models.pickerOrder.reloadDraft": "捨棄草稿並重新載入", "models.pickerOrder.catalogRequired": "模型識別資訊缺失或不明確。請重新載入模型頁面以更新目錄,再編輯自訂順序。", + "skills.title": "技能控制平面", + "skills.subtitle": "通用代理技能登錄、視覺化市集與政策治理部署。", + "skills.refresh": "重新整理", + "skills.refreshing": "正在重新整理…", + "skills.tab.overview": "總覽", + "skills.tab.marketplace": "市集", + "skills.tab.registry": "登錄({count})", + "skills.tab.editor": "技能編輯器", + "skills.tab.matrix": "部署矩陣", + "skills.tab.agents": "代理", + "skills.tab.nodes": "遠端節點", + "skills.tab.drift": "漂移與回滾", + "skills.tab.reviews": "審核佇列", + "skills.tab.audit": "稽核紀錄", + "skills.card.registered": "已登錄技能", + "skills.card.deployments": "進行中的部署", + "skills.card.marketplace": "市集項目", + "skills.card.audit": "稽核事件", + "skills.recentActivity": "最近的控制平面活動", + "skills.noEvents": "尚無最近事件。", + "skills.col.timestamp": "時間", + "skills.col.event": "事件", + "skills.col.actor": "操作者", + "skills.col.target": "目標", + "skills.searchPlaceholder": "搜尋公開 skills.sh 目錄…", + "skills.byAuthor": "v{version} · {author}", + "skills.verified": "已驗證", + "skills.importToRegistry": "匯入登錄", + "skills.emptyRegistry": "尚未登錄技能。請從市集或本機資料夾匯入。", + "skills.col.skill": "技能", + "skills.col.version": "版本", + "skills.col.status": "狀態", + "skills.col.risk": "風險", + "skills.col.tags": "標籤", + "skills.col.actions": "動作", + "skills.publish": "發佈", + "skills.deployCodex": "部署到 Codex", + "skills.editorTitle": "SKILL.md 編輯器", + "skills.editorNamePlaceholder": "技能名稱", + "skills.editorVersionPlaceholder": "版本", + "skills.editorDefaultName": "自訂助理", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "儲存草稿", + "skills.previewTitle": "即時預覽", + "skills.matrixTitle": "機群部署矩陣", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "未安裝", + "skills.inSync": "v{version} 已同步", + "skills.blocked": "已封鎖", + "skills.agentsTitle": "目標代理機群", + "skills.col.agent": "代理", + "skills.col.userRoot": "目標根目錄(使用者範圍)", + "skills.col.projectRoot": "目標根目錄(專案範圍)", + "skills.col.adapterVersion": "轉接器版本", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "通用代理", + "skills.statusReady": "就緒", + "skills.nodesTitle": "遠端執行節點(SSH)", + "skills.nodesHelp": "以嚴格主機金鑰釘選管理遠端 VPS、GPU 工作節點與開發主機。", + "skills.col.node": "節點", + "skills.col.type": "類型", + "skills.col.host": "主機", + "skills.col.environment": "環境", + "skills.localMachine": "本機", + "skills.self": "自身", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "測試連線", + "skills.driftTitle": "組態漂移與回滾", + "skills.driftHelp": "持續監控會將磁碟上的即時檔案與不可變登錄內容雜湊進行比較。", + "skills.col.deployment": "部署", + "skills.col.targetPath": "目標路徑", + "skills.col.driftState": "漂移狀態", + "skills.checkHash": "檢查雜湊", + "skills.reviewsTitle": "政策與核准佇列", + "skills.reviewsEmpty": "沒有需要操作員決策的待處理升級審核。", + "skills.auditTitle": "稽核紀錄", + "skills.col.eventType": "事件類型", + "skills.col.targetId": "目標 ID", + "skills.system": "system", + "skills.status.importing": "正在匯入 {id}…", + "skills.status.imported": "已匯入 {id}。", + "skills.status.importFailed": "匯入失敗:{error}", + "skills.status.publishing": "正在發佈 {id}…", + "skills.status.published": "已發佈 {id}。", + "skills.status.publishFailed": "發佈失敗:{error}", + "skills.status.planning": "正在規劃將 {id} 部署到 {agent}…", + "skills.status.deployed": "已將 {id} 部署到 {agent}。", + "skills.status.deployFailed": "部署失敗:{error}", + "skills.status.saved": "技能草稿已儲存到登錄。", + "skills.status.saveFailed": "儲存失敗:{error}", + "skills.loadFailed": "無法載入技能控制平面資料。", + "security.title": "安全控制平面", + "security.subtitle": "授權優先的偵察與驗證。面向目標的工作預設拒絕。R3 已封鎖。可用雜湊前往,不是側欄 NAV 列。", + "security.refresh": "重新整理", + "security.refreshing": "正在重新整理…", + "security.banner": "僅限已授權目標。未知資產、過期授權與排除主機一律拒絕。不會呼叫即時第三方工具;偵察使用本機夾具。", + "security.flagOff": "功能旗標 PAO_SECURITY_CONTROL_PLANE 已關閉 — 變更會被拒絕。", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "失敗:{error}", + "security.evidence.campaign": "活動證據:{campaign}", + "security.evidence.none": "無進行中的活動", + "security.tab.overview": "總覽", + "security.tab.authorizations": "授權", + "security.tab.scopes": "範圍", + "security.tab.campaigns": "活動", + "security.tab.approvals": "核准", + "security.tab.findings": "發現", + "security.tab.evidence": "證據", + "security.tab.skills": "套件", + "security.tab.tools": "工具", + "security.tab.mcp": "MCP", + "security.tab.policies": "政策", + "security.tab.audit": "稽核", + "security.card.enabled": "已啟用", + "security.card.campaigns": "活動", + "security.card.approvals": "核准", + "security.card.leads": "線索", + "security.card.validated": "已驗證", + "security.card.blocked": "已封鎖", + "security.card.authExpiring": "授權即將過期", + "security.card.breakers": "斷路器", + "security.yes": "是", + "security.no": "否", + "security.col.id": "ID", + "security.col.type": "類型", + "security.col.status": "狀態", + "security.col.validUntil": "有效期限", + "security.col.name": "名稱", + "security.col.actions": "動作", + "security.col.target": "目標", + "security.col.title": "標題", + "security.col.severity": "嚴重性", + "security.col.sha256": "SHA-256", + "security.col.preview": "預覽", + "security.col.compatibility": "相容性", + "security.col.kind": "種類", + "security.col.allowlisted": "已加入允許清單", + "security.col.health": "健康", + "security.col.credential": "憑證", + "security.col.ceiling": "上限", + "security.col.blockR3": "封鎖 R3", + "security.col.when": "時間", + "security.col.event": "事件", + "security.col.actor": "操作者", + "security.start": "開始", + "security.pause": "暫停", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "核准一次", + "security.deny": "拒絕", + "security.reasonBounded": "僅限一次", + "security.reasonDenied": "已拒絕", + "security.hashes": "可加入書籤的雜湊:{hashes}", + "security.loadFailed": "無法載入安全控制平面資料。", + "credentials.title": "供應商存取控制平面", + "credentials.subtitle": "加密憑證、經健康檢查的權杖池、OAuth 閘道與政策治理租約。可透過雜湊路由;不是側邊欄 NAV 列。", + "credentials.refresh": "重新整理", + "credentials.refreshing": "重新整理中…", + "credentials.banner": "密鑰絕不以明文列出。本階段的健康檢查為夾具/行程內。此平面不註冊供應商帳戶。", + "credentials.flagOff": "功能旗標 CREDENTIAL_RUNTIME_ENABLED 已關閉 — 拒絕變更。", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "失敗:{error}", + "credentials.tab.overview": "總覽", + "credentials.tab.list": "憑證", + "credentials.tab.providers": "供應商", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "池", + "credentials.tab.health": "健康", + "credentials.tab.quota": "配額", + "credentials.tab.policies": "政策", + "credentials.tab.approvals": "核准", + "credentials.tab.audit": "稽核", + "credentials.card.enabled": "已啟用", + "credentials.card.total": "總計", + "credentials.card.healthy": "健康", + "credentials.card.degraded": "降級", + "credentials.card.quarantined": "隔離", + "credentials.card.revoked": "已撤銷", + "credentials.card.leases": "租約", + "credentials.card.breakers": "斷路器", + "credentials.card.vault": "保險庫", + "credentials.yes": "是", + "credentials.no": "否", + "credentials.available": "可用", + "credentials.unavailable": "不可用", + "credentials.col.id": "ID", + "credentials.col.name": "名稱", + "credentials.col.provider": "供應商", + "credentials.col.status": "狀態", + "credentials.col.health": "健康", + "credentials.col.secret": "密鑰", + "credentials.col.eligible": "可路由", + "credentials.col.actions": "操作", + "credentials.col.score": "分數", + "credentials.col.adapter": "轉接器", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "已啟用", + "credentials.col.session": "工作階段", + "credentials.col.redirect": "重新導向", + "credentials.col.budget": "預算", + "credentials.col.priority": "優先順序", + "credentials.col.action": "動作", + "credentials.col.target": "目標", + "credentials.col.when": "時間", + "credentials.col.event": "事件", + "credentials.col.actor": "操作者", + "credentials.col.decision": "決定", + "credentials.validate": "驗證", + "credentials.quarantine": "隔離", + "credentials.revoke": "撤銷", + "credentials.healthRun": "執行健康檢查", + "credentials.approve": "核准", + "credentials.reject": "拒絕", + "credentials.hashes": "可加入書籤的雜湊:{hashes}", + "credentials.loadFailed": "無法載入憑證執行階段資料。", "nav.remote": "遠端工作區", "remote.title": "遠端工作區", "remote.subtitle": "Codex、Claude Code 或 Pi 在此 Hub 執行,檔案、命令、測試與建置則留在所選電腦上處理。", @@ -2844,4 +3067,77 @@ export const zhTW: Record = { "remote.event.status": "狀態", "remote.event.tool": "遠端工具", "remote.event.error": "錯誤", + "social.title": "社群發布控制平面", + "social.subtitle": "代理人社群發布協調、跨平台內容格式轉換與 OpenPost 排程管理。", + "social.refresh": "重新整理", + "social.refreshing": "重新整理中…", + "social.banner": "已連接 OpenPost。平台憑證安全受限於 OpenPost 內。發布前需人工審批。", + "social.flagOff": "功能開關 SOCIAL_PUBLISHING_ENABLED 已關閉 — 拒絕變更。", + "social.tab.overview": "總覽", + "social.tab.accounts": "帳號", + "social.tab.publications": "發布內容", + "social.tab.approvals": "審批", + "social.tab.jobs": "發送任務", + "social.tab.analytics": "數據分析", + "social.tab.instances": "OpenPost 實例", + "social.tab.audit": "稽核日誌", + "social.card.enabled": "已啟用", + "social.card.instances": "實例數", + "social.card.accounts": "帳號數", + "social.card.readyAccounts": "就緒帳號", + "social.card.publications": "發布內容數", + "social.card.scheduled": "已排程", + "social.card.pendingApprovals": "待審批", + "social.card.failedDeliveries": "發送失敗", + "social.yes": "是", + "social.no": "否", + "social.col.id": "ID", + "social.col.platform": "平台", + "social.col.displayName": "顯示名稱", + "social.col.readiness": "就緒狀態", + "social.col.enabled": "啟用", + "social.col.actions": "操作", + "social.col.sourceType": "素材類型", + "social.col.title": "標題", + "social.col.status": "狀態", + "social.col.scheduledAt": "排程時間", + "social.col.name": "名稱", + "social.col.baseUrl": "基本 URL", + "social.col.version": "版本", + "social.col.pubId": "發布 ID", + "social.col.jobType": "任務類型", + "social.col.attempts": "重試次數", + "social.col.error": "錯誤", + "social.col.when": "時間戳記", + "social.col.actor": "操作者", + "social.col.action": "操作", + "social.col.resource": "資源", + "social.col.resourceId": "資源 ID", + "social.col.decision": "決定", + "social.col.approver": "審核人", + "social.col.approvalScope": "審核範圍", + "social.col.contentHash": "內容雜湊", + "social.col.account": "帳號", + "social.col.capturedAt": "收集時間", + "social.col.views": "瀏覽量", + "social.col.impressions": "曝光量", + "social.col.engagements": "互動量", + "social.col.followersDelta": "粉絲變化", + "social.emptyAccounts": "未找到連線的社群帳號。請由 OpenPost 實例同步。", + "social.emptyPublications": "尚無發布內容。", + "social.emptyInstances": "未註冊 OpenPost 實例。", + "social.emptyJobs": "無發送任務記錄。", + "social.emptyAudit": "無稽核日誌記錄。", + "social.emptyApprovals": "尚無審核記錄。", + "social.emptyAnalytics": "尚無分析快照。", + "social.refreshCaps": "重新整理功能支援", + "social.validate": "驗證", + "social.approve": "核准", + "social.publish": "立即發布", + "social.test": "測試連線", + "social.syncAccounts": "同步帳號", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "失敗: {error}", + "social.hashes": "可加入書籤的雜湊:{hashes}", }; diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index 36acf63b185..3f5d58bdfa0 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -73,6 +73,10 @@ export const zh: Record = { "nav.models": "模型", "nav.combos": "组合", "nav.subagents": "子代理", + "nav.skills": "技能", + "nav.security": "安全", + "nav.credentials": "凭据", + "nav.social": "社交媒体发布", // routing intelligence "routing.title": "路由智能 (beta)", @@ -2818,6 +2822,225 @@ export const zh: Record = { "models.pickerOrder.saveDraft": "保存草稿", "models.pickerOrder.reloadDraft": "丢弃草稿并重新加载", "models.pickerOrder.catalogRequired": "模型标识信息缺失或不明确。请重新加载模型页面以刷新目录,再编辑自定义顺序。", + "skills.title": "技能控制平面", + "skills.subtitle": "通用智能体技能注册表、可视化市场与策略治理部署。", + "skills.refresh": "刷新", + "skills.refreshing": "正在刷新…", + "skills.tab.overview": "概览", + "skills.tab.marketplace": "市场", + "skills.tab.registry": "注册表({count})", + "skills.tab.editor": "技能编辑器", + "skills.tab.matrix": "部署矩阵", + "skills.tab.agents": "智能体", + "skills.tab.nodes": "远程节点", + "skills.tab.drift": "漂移与回滚", + "skills.tab.reviews": "审核队列", + "skills.tab.audit": "审计记录", + "skills.card.registered": "已注册技能", + "skills.card.deployments": "活跃部署", + "skills.card.marketplace": "市场条目", + "skills.card.audit": "审计事件", + "skills.recentActivity": "最近的控制平面活动", + "skills.noEvents": "暂无最近事件。", + "skills.col.timestamp": "时间", + "skills.col.event": "事件", + "skills.col.actor": "操作者", + "skills.col.target": "目标", + "skills.searchPlaceholder": "搜索公开 skills.sh 目录…", + "skills.byAuthor": "v{version} · {author}", + "skills.verified": "已验证", + "skills.importToRegistry": "导入到注册表", + "skills.emptyRegistry": "尚未注册技能。请从市场或本地文件夹导入。", + "skills.col.skill": "技能", + "skills.col.version": "版本", + "skills.col.status": "状态", + "skills.col.risk": "风险", + "skills.col.tags": "标签", + "skills.col.actions": "操作", + "skills.publish": "发布", + "skills.deployCodex": "部署到 Codex", + "skills.editorTitle": "SKILL.md 编辑器", + "skills.editorNamePlaceholder": "技能名称", + "skills.editorVersionPlaceholder": "版本", + "skills.editorDefaultName": "自定义助手", + "skills.editorDefaultMarkdown": "---\nname: custom-assistant\ndisplayName: Custom Assistant\nversion: 1.0.0\ndescription: Custom guidance procedure.\ntags: custom, utility\n---\n\n# Custom Assistant\n\nInstructions for the agent.\n", + "skills.saveDraft": "保存草稿", + "skills.previewTitle": "实时预览", + "skills.matrixTitle": "机群部署矩阵", + "skills.agent.codex": "Codex", + "skills.agent.claudeCode": "Claude Code", + "skills.agent.opencode": "OpenCode", + "skills.agent.universal": "Universal", + "skills.notInstalled": "未安装", + "skills.inSync": "v{version} 已同步", + "skills.blocked": "已阻止", + "skills.agentsTitle": "目标智能体机群", + "skills.col.agent": "智能体", + "skills.col.userRoot": "目标根目录(用户范围)", + "skills.col.projectRoot": "目标根目录(项目范围)", + "skills.col.adapterVersion": "适配器版本", + "skills.agent.openaiCodex": "OpenAI Codex", + "skills.agent.universalAgent": "通用智能体", + "skills.statusReady": "就绪", + "skills.nodesTitle": "远程执行节点(SSH)", + "skills.nodesHelp": "以严格主机密钥固定管理远程 VPS、GPU 工作节点和开发主机。", + "skills.col.node": "节点", + "skills.col.type": "类型", + "skills.col.host": "主机", + "skills.col.environment": "环境", + "skills.localMachine": "本机", + "skills.self": "自身", + "skills.vpsMain": "VPS Main", + "skills.testConnection": "测试连接", + "skills.driftTitle": "配置漂移与回滚", + "skills.driftHelp": "持续监控会将磁盘上的实时文件与不可变注册表内容哈希进行比较。", + "skills.col.deployment": "部署", + "skills.col.targetPath": "目标路径", + "skills.col.driftState": "漂移状态", + "skills.checkHash": "检查哈希", + "skills.reviewsTitle": "策略与审批队列", + "skills.reviewsEmpty": "没有需要操作员决策的待处理升级审核。", + "skills.auditTitle": "审计日志", + "skills.col.eventType": "事件类型", + "skills.col.targetId": "目标 ID", + "skills.system": "system", + "skills.status.importing": "正在导入 {id}…", + "skills.status.imported": "已导入 {id}。", + "skills.status.importFailed": "导入失败:{error}", + "skills.status.publishing": "正在发布 {id}…", + "skills.status.published": "已发布 {id}。", + "skills.status.publishFailed": "发布失败:{error}", + "skills.status.planning": "正在规划将 {id} 部署到 {agent}…", + "skills.status.deployed": "已将 {id} 部署到 {agent}。", + "skills.status.deployFailed": "部署失败:{error}", + "skills.status.saved": "技能草稿已保存到注册表。", + "skills.status.saveFailed": "保存失败:{error}", + "skills.loadFailed": "无法加载技能控制平面数据。", + "security.title": "安全控制平面", + "security.subtitle": "授权优先的侦察与验证。面向目标的工作默认拒绝。R3 已阻止。可通过哈希访问,不是侧栏 NAV 行。", + "security.refresh": "刷新", + "security.refreshing": "正在刷新…", + "security.banner": "仅限已授权目标。未知资产、过期授权和排除主机一律拒绝。不会调用实时第三方工具;侦察使用本地夹具。", + "security.flagOff": "功能开关 PAO_SECURITY_CONTROL_PLANE 已关闭 — 变更会被拒绝。", + "security.status.posting": "POST {path}", + "security.status.ok": "OK {path}", + "security.status.failed": "失败:{error}", + "security.evidence.campaign": "活动证据:{campaign}", + "security.evidence.none": "无进行中的活动", + "security.tab.overview": "概览", + "security.tab.authorizations": "授权", + "security.tab.scopes": "范围", + "security.tab.campaigns": "活动", + "security.tab.approvals": "审批", + "security.tab.findings": "发现", + "security.tab.evidence": "证据", + "security.tab.skills": "包", + "security.tab.tools": "工具", + "security.tab.mcp": "MCP", + "security.tab.policies": "策略", + "security.tab.audit": "审计", + "security.card.enabled": "已启用", + "security.card.campaigns": "活动", + "security.card.approvals": "审批", + "security.card.leads": "线索", + "security.card.validated": "已验证", + "security.card.blocked": "已阻止", + "security.card.authExpiring": "授权即将过期", + "security.card.breakers": "断路器", + "security.yes": "是", + "security.no": "否", + "security.col.id": "ID", + "security.col.type": "类型", + "security.col.status": "状态", + "security.col.validUntil": "有效期至", + "security.col.name": "名称", + "security.col.actions": "操作", + "security.col.target": "目标", + "security.col.title": "标题", + "security.col.severity": "严重性", + "security.col.sha256": "SHA-256", + "security.col.preview": "预览", + "security.col.compatibility": "兼容性", + "security.col.kind": "种类", + "security.col.allowlisted": "已加入允许列表", + "security.col.health": "健康", + "security.col.credential": "凭据", + "security.col.ceiling": "上限", + "security.col.blockR3": "阻止 R3", + "security.col.when": "时间", + "security.col.event": "事件", + "security.col.actor": "操作者", + "security.start": "开始", + "security.pause": "暂停", + "security.reconLab": "Recon lab.local", + "security.approveOnce": "批准一次", + "security.deny": "拒绝", + "security.reasonBounded": "仅限一次", + "security.reasonDenied": "已拒绝", + "security.hashes": "可收藏哈希:{hashes}", + "security.loadFailed": "无法加载安全控制平面数据。", + "credentials.title": "提供商访问控制平面", + "credentials.subtitle": "加密凭据、经健康检查的令牌池、OAuth 网关以及策略治理的租约。可通过哈希路由;不是侧边栏 NAV 行。", + "credentials.refresh": "刷新", + "credentials.refreshing": "正在刷新…", + "credentials.banner": "密钥绝不以明文列出。本阶段的健康检查为夹具/进程内。此平面不注册提供商账户。", + "credentials.flagOff": "功能开关 CREDENTIAL_RUNTIME_ENABLED 已关闭 — 拒绝变更。", + "credentials.status.posting": "POST {path}", + "credentials.status.ok": "OK {path}", + "credentials.status.failed": "失败:{error}", + "credentials.tab.overview": "概览", + "credentials.tab.list": "凭据", + "credentials.tab.providers": "提供商", + "credentials.tab.oauth": "OAuth", + "credentials.tab.pool": "池", + "credentials.tab.health": "健康", + "credentials.tab.quota": "配额", + "credentials.tab.policies": "策略", + "credentials.tab.approvals": "审批", + "credentials.tab.audit": "审计", + "credentials.card.enabled": "已启用", + "credentials.card.total": "总计", + "credentials.card.healthy": "健康", + "credentials.card.degraded": "降级", + "credentials.card.quarantined": "隔离", + "credentials.card.revoked": "已吊销", + "credentials.card.leases": "租约", + "credentials.card.breakers": "断路器", + "credentials.card.vault": "保险库", + "credentials.yes": "是", + "credentials.no": "否", + "credentials.available": "可用", + "credentials.unavailable": "不可用", + "credentials.col.id": "ID", + "credentials.col.name": "名称", + "credentials.col.provider": "提供商", + "credentials.col.status": "状态", + "credentials.col.health": "健康", + "credentials.col.secret": "密钥", + "credentials.col.eligible": "可路由", + "credentials.col.actions": "操作", + "credentials.col.score": "分数", + "credentials.col.adapter": "适配器", + "credentials.col.oauth": "OAuth", + "credentials.col.enabled": "已启用", + "credentials.col.session": "会话", + "credentials.col.redirect": "重定向", + "credentials.col.budget": "预算", + "credentials.col.priority": "优先级", + "credentials.col.action": "操作", + "credentials.col.target": "目标", + "credentials.col.when": "时间", + "credentials.col.event": "事件", + "credentials.col.actor": "操作者", + "credentials.col.decision": "决定", + "credentials.validate": "验证", + "credentials.quarantine": "隔离", + "credentials.revoke": "吊销", + "credentials.healthRun": "运行健康检查", + "credentials.approve": "批准", + "credentials.reject": "拒绝", + "credentials.hashes": "可收藏哈希:{hashes}", + "credentials.loadFailed": "无法加载凭据运行时数据。", "nav.remote": "远程工作区", "remote.title": "远程工作区", "remote.subtitle": "Codex、Claude Code 或 Pi 在此 Hub 上运行,文件、命令、测试和构建则留在所选电脑上执行。", @@ -2879,4 +3102,77 @@ export const zh: Record = { "remote.event.status": "状态", "remote.event.tool": "远程工具", "remote.event.error": "错误", + "social.title": "社交媒体发布控制平面", + "social.subtitle": "智能体社交发布编排、多平台内容适配和 OpenPost 队列持久调度。", + "social.refresh": "刷新", + "social.refreshing": "刷新中…", + "social.banner": "已连接 OpenPost。社交平台凭据安全受限于 OpenPost 内。发布前需要人工审批。", + "social.flagOff": "功能特性 SOCIAL_PUBLISHING_ENABLED 已关闭 — 拒绝修改。", + "social.tab.overview": "概览", + "social.tab.accounts": "账号", + "social.tab.publications": "发布内容", + "social.tab.approvals": "审批", + "social.tab.jobs": "投递任务", + "social.tab.analytics": "数据分析", + "social.tab.instances": "OpenPost 实例", + "social.tab.audit": "审计日志", + "social.card.enabled": "已启用", + "social.card.instances": "实例数", + "social.card.accounts": "账号数", + "social.card.readyAccounts": "就绪账号", + "social.card.publications": "发布内容数", + "social.card.scheduled": "已排程", + "social.card.pendingApprovals": "待审批", + "social.card.failedDeliveries": "投递失败", + "social.yes": "是", + "social.no": "否", + "social.col.id": "ID", + "social.col.platform": "平台", + "social.col.displayName": "显示名称", + "social.col.readiness": "就绪状态", + "social.col.enabled": "启用", + "social.col.actions": "操作", + "social.col.sourceType": "素材类型", + "social.col.title": "标题", + "social.col.status": "状态", + "social.col.scheduledAt": "排程时间", + "social.col.name": "名称", + "social.col.baseUrl": "基础 URL", + "social.col.version": "版本", + "social.col.pubId": "发布 ID", + "social.col.jobType": "任务类型", + "social.col.attempts": "重试次数", + "social.col.error": "错误", + "social.col.when": "时间戳", + "social.col.actor": "操作者", + "social.col.action": "操作", + "social.col.resource": "资源", + "social.col.resourceId": "资源 ID", + "social.col.decision": "决定", + "social.col.approver": "审批人", + "social.col.approvalScope": "审批范围", + "social.col.contentHash": "内容哈希", + "social.col.account": "账号", + "social.col.capturedAt": "采集时间", + "social.col.views": "浏览量", + "social.col.impressions": "展示量", + "social.col.engagements": "互动量", + "social.col.followersDelta": "粉丝变化", + "social.emptyAccounts": "未找到连接的社交账号。请从 OpenPost 实例同步。", + "social.emptyPublications": "暂无发布内容。", + "social.emptyInstances": "未注册 OpenPost 实例。", + "social.emptyJobs": "无投递任务记录。", + "social.emptyAudit": "无审计日志记录。", + "social.emptyApprovals": "暂无审批记录。", + "social.emptyAnalytics": "暂无分析快照。", + "social.refreshCaps": "刷新支持特性", + "social.validate": "策略验证", + "social.approve": "批准", + "social.publish": "立即发布", + "social.test": "测试连接", + "social.syncAccounts": "同步账号", + "social.status.posting": "POST {path}", + "social.status.ok": "OK {path}", + "social.status.failed": "失败: {error}", + "social.hashes": "可收藏哈希:{hashes}", }; diff --git a/gui/src/pages/Credentials.tsx b/gui/src/pages/Credentials.tsx new file mode 100644 index 00000000000..abe7c6103ea --- /dev/null +++ b/gui/src/pages/Credentials.tsx @@ -0,0 +1,415 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import "../styles-credentials-workspace.css"; +import { CREDENTIALS_TAB_HASHES } from "../app-routing"; +import { useDataSurface } from "../data-surface"; +import { readJsonIfOk } from "../fetch-json"; +import { navigateHash, normalizeHashPath } from "../hash-routing"; +import { useT, type TKey } from "../i18n/shared"; + +export interface CredentialsProps { + apiBase: string; +} + +type TabType = + | "overview" + | "list" + | "providers" + | "oauth" + | "pool" + | "health" + | "quota" + | "policies" + | "approvals" + | "audit"; + +const TAB_FROM_HASH: Record = { + credentials: "overview", + "credentials/list": "list", + "credentials/providers": "providers", + "credentials/oauth": "oauth", + "credentials/pool": "pool", + "credentials/health": "health", + "credentials/quota": "quota", + "credentials/policies": "policies", + "credentials/approvals": "approvals", + "credentials/audit": "audit", +}; + +function readTabFromHash(): TabType { + const raw = normalizeHashPath(typeof window !== "undefined" ? window.location.hash : "credentials"); + return TAB_FROM_HASH[raw] ?? "overview"; +} + +function hashForTab(tab: TabType): string { + return tab === "overview" ? "credentials" : `credentials/${tab}`; +} + +interface Overview { + enabled: boolean; + total: number; + healthy: number; + degraded: number; + quarantined: number; + revoked: number; + active_leases: number; + circuit_open: number; + vault_available: boolean; +} + +type Row = Record; + +interface CredentialsWorkspace { + overview: Overview | null; + credentials: Row[]; + providers: Row[]; + oauth: Row[]; + pool: Row[]; + health: Row[]; + quota: Row[]; + policies: Row[]; + approvals: Row[]; + audit: Row[]; +} + +const EMPTY_WORKSPACE: CredentialsWorkspace = { + overview: null, + credentials: [], + providers: [], + oauth: [], + pool: [], + health: [], + quota: [], + policies: [], + approvals: [], + audit: [], +}; + +const TABS: Array<{ id: TabType; labelKey: TKey }> = [ + { id: "overview", labelKey: "credentials.tab.overview" }, + { id: "list", labelKey: "credentials.tab.list" }, + { id: "providers", labelKey: "credentials.tab.providers" }, + { id: "oauth", labelKey: "credentials.tab.oauth" }, + { id: "pool", labelKey: "credentials.tab.pool" }, + { id: "health", labelKey: "credentials.tab.health" }, + { id: "quota", labelKey: "credentials.tab.quota" }, + { id: "policies", labelKey: "credentials.tab.policies" }, + { id: "approvals", labelKey: "credentials.tab.approvals" }, + { id: "audit", labelKey: "credentials.tab.audit" }, +]; + +async function readData(res: Response, fallback: T): Promise { + const payload = await readJsonIfOk<{ data?: T }>(res); + return payload?.data ?? fallback; +} + +function cell(value: unknown): string { + if (value == null) return ""; + return String(value); +} + +export function Credentials({ apiBase }: CredentialsProps): React.JSX.Element { + const t = useT(); + const [tab, setTab] = useState(readTabFromHash); + const [statusMessage, setStatusMessage] = useState(null); + + const selectTab = (next: TabType) => { + setTab(next); + navigateHash(hashForTab(next)); + }; + + useEffect(() => { + const onHash = () => setTab(readTabFromHash()); + window.addEventListener("hashchange", onHash); + return () => window.removeEventListener("hashchange", onHash); + }, []); + + const loadWorkspace = useCallback(async (signal: AbortSignal): Promise => { + const [ + overviewRes, + credentialsRes, + providersRes, + oauthRes, + poolRes, + healthRes, + quotaRes, + policiesRes, + approvalsRes, + auditRes, + ] = await Promise.all([ + fetch(`${apiBase}/api/credentials/overview`, { signal }), + fetch(`${apiBase}/api/credentials`, { signal }), + fetch(`${apiBase}/api/credentials/providers`, { signal }), + fetch(`${apiBase}/api/credentials/oauth/sessions`, { signal }), + fetch(`${apiBase}/api/credentials/pool`, { signal }), + fetch(`${apiBase}/api/credentials/health`, { signal }), + fetch(`${apiBase}/api/credentials/quota`, { signal }), + fetch(`${apiBase}/api/credentials/policies`, { signal }), + fetch(`${apiBase}/api/credentials/approvals`, { signal }), + fetch(`${apiBase}/api/credentials/audit`, { signal }), + ]); + return { + overview: await readData(overviewRes, null), + credentials: await readData(credentialsRes, []), + providers: await readData(providersRes, []), + oauth: await readData(oauthRes, []), + pool: await readData(poolRes, []), + health: await readData(healthRes, []), + quota: await readData(quotaRes, []), + policies: await readData(policiesRes, []), + approvals: await readData(approvalsRes, []), + audit: await readData(auditRes, []), + }; + }, [apiBase]); + + const resource = useDataSurface( + `credentials-workspace:${apiBase}`, + [apiBase], + loadWorkspace, + { isEmpty: () => false }, + ); + const workspace = resource.state.data ?? EMPTY_WORKSPACE; + const { + overview, + credentials, + providers, + oauth, + pool, + health, + quota, + policies, + approvals, + audit, + } = workspace; + const loading = resource.state.refreshing || resource.state.showSkeleton; + const hashList = useMemo(() => ["credentials", ...CREDENTIALS_TAB_HASHES].join(", "), []); + + const post = async (path: string, body?: Record) => { + try { + setStatusMessage(t("credentials.status.posting", { path })); + const res = await fetch(`${apiBase}${path}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body ?? { actor: "gui-operator" }), + }); + const data = await res.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(res.status); + setStatusMessage(res.ok ? t("credentials.status.ok", { path }) : t("credentials.status.failed", { error })); + resource.refresh(); + } catch (e) { + setStatusMessage(t("credentials.status.failed", { error: e instanceof Error ? e.message : String(e) })); + } + }; + + return ( +
+
+
+

{t("credentials.title")}

+

{t("credentials.subtitle")}

+
+ +
+ +
+ {t("credentials.banner")} + {overview && !overview.enabled ? ` ${t("credentials.flagOff")}` : ""} +
+ + {resource.state.showError &&
{t("credentials.loadFailed")}
} + {statusMessage &&
{statusMessage}
} + +
+ {TABS.map(entry => ( + + ))} +
+ + {tab === "overview" && overview && ( + <> +
+
{t("credentials.card.enabled")}{overview.enabled ? t("credentials.yes") : t("credentials.no")}
+
{t("credentials.card.total")}{overview.total}
+
{t("credentials.card.healthy")}{overview.healthy}
+
{t("credentials.card.degraded")}{overview.degraded}
+
{t("credentials.card.quarantined")}{overview.quarantined}
+
{t("credentials.card.revoked")}{overview.revoked}
+
{t("credentials.card.leases")}{overview.active_leases}
+
{t("credentials.card.breakers")}{overview.circuit_open}
+
{t("credentials.card.vault")}{overview.vault_available ? t("credentials.available") : t("credentials.unavailable")}
+
+
+ +
+ + )} + + {tab === "list" && ( + + + + {credentials.map(row => ( + + + + + + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.name")}{t("credentials.col.provider")}{t("credentials.col.status")}{t("credentials.col.health")}{t("credentials.col.secret")}{t("credentials.col.eligible")}{t("credentials.col.actions")}
{cell(row.id)}{cell(row.name)}{cell(row.provider_slug)}{cell(row.status)}{cell(row.health_status)}{cell(row.secret)}{row.routing_eligible === true ? t("credentials.yes") : t("credentials.no")} + + + +
+ )} + + {tab === "providers" && ( + + + + {providers.map(row => ( + + + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.name")}{t("credentials.col.adapter")}{t("credentials.col.oauth")}{t("credentials.col.enabled")}
{cell(row.slug)}{cell(row.name)}{cell(row.adapter_type)}{row.oauth_supported === true ? t("credentials.yes") : t("credentials.no")}{row.enabled === true ? t("credentials.yes") : t("credentials.no")}
+ )} + + {tab === "oauth" && ( + + + + {oauth.map(row => ( + + + + + + ))} + +
{t("credentials.col.session")}{t("credentials.col.status")}{t("credentials.col.redirect")}
{cell(row.id)}{cell(row.status)}{cell(row.redirect_uri)}
+ )} + + {tab === "pool" && ( + + + + {pool.map(row => ( + + + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.provider")}{t("credentials.col.score")}{t("credentials.col.health")}{t("credentials.col.status")}
{cell(row.credential_id)}{cell(row.provider)}{cell(row.routing_score)}{cell(row.health_status)}{cell(row.status)}
+ )} + + {tab === "health" && ( + + + + {health.map(row => ( + + + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.health")}{t("credentials.col.score")}{t("credentials.col.status")}{t("credentials.col.eligible")}
{cell(row.id)}{cell(row.health_status)}{cell(row.health_score)}{cell(row.status)}{row.routing_eligible === true ? t("credentials.yes") : t("credentials.no")}
+ )} + + {tab === "quota" && ( + + + + {quota.map(row => ( + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.budget")}{t("credentials.col.health")}
{cell(row.id)}{`${cell(row.remaining_budget)} / ${cell(row.budget_limit)}`}{cell(row.health_status)}
+ )} + + {tab === "policies" && ( + + + + {policies.map(row => ( + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.name")}{t("credentials.col.priority")}
{cell(row.id)}{cell(row.name)}{cell(row.priority)}
+ )} + + {tab === "approvals" && ( + + + + {approvals.map(row => ( + + + + + + + + ))} + +
{t("credentials.col.id")}{t("credentials.col.action")}{t("credentials.col.target")}{t("credentials.col.status")}{t("credentials.col.actions")}
{cell(row.id)}{cell(row.action)}{cell(row.target_id)}{cell(row.status)} + + +
+ )} + + {tab === "audit" && ( + + + + {audit.map(row => ( + + + + + + + ))} + +
{t("credentials.col.when")}{t("credentials.col.event")}{t("credentials.col.actor")}{t("credentials.col.decision")}
{cell(row.created_at)}{cell(row.action)}{cell(row.actor_id)}{cell(row.decision)}
+ )} + +

{t("credentials.hashes", { hashes: hashList })}

+
+ ); +} + diff --git a/gui/src/pages/Security.tsx b/gui/src/pages/Security.tsx new file mode 100644 index 00000000000..aee8455235f --- /dev/null +++ b/gui/src/pages/Security.tsx @@ -0,0 +1,467 @@ +import React, { useCallback, useEffect, useMemo, useState } from "react"; +import "../styles-security-workspace.css"; +import { SECURITY_TAB_HASHES } from "../app-routing"; +import { useDataSurface } from "../data-surface"; +import { readJsonIfOk } from "../fetch-json"; +import { navigateHash, normalizeHashPath } from "../hash-routing"; +import { useT, type TKey } from "../i18n/shared"; + +export interface SecurityProps { + apiBase: string; +} + +type TabType = + | "overview" + | "authorizations" + | "scopes" + | "campaigns" + | "approvals" + | "findings" + | "evidence" + | "skills" + | "tools" + | "mcp" + | "policies" + | "audit"; + +const TAB_FROM_HASH: Record = { + security: "overview", + "security/authorizations": "authorizations", + "security/scopes": "scopes", + "security/campaigns": "campaigns", + "security/approvals": "approvals", + "security/findings": "findings", + "security/evidence": "evidence", + "security/skills": "skills", + "security/tools": "tools", + "security/mcp": "mcp", + "security/policies": "policies", + "security/audit": "audit", +}; + +function readTabFromHash(): TabType { + const raw = normalizeHashPath(typeof window !== "undefined" ? window.location.hash : "security"); + return TAB_FROM_HASH[raw] ?? "overview"; +} + +function hashForTab(tab: TabType): string { + return tab === "overview" ? "security" : `security/${tab}`; +} + +interface Overview { + enabled: boolean; + campaigns_active: number; + approvals_pending: number; + leads_open: number; + findings_validated: number; + blocked_actions: number; + authorizations_expiring: number; + circuit_breakers_open: number; +} + +type Row = Record; + +interface SecurityWorkspace { + overview: Overview | null; + authorizations: Row[]; + scopes: Row[]; + campaigns: Row[]; + approvals: Row[]; + findings: Row[]; + evidence: Row[]; + packages: Row[]; + tools: Row[]; + mcp: Row[]; + policies: Row[]; + audit: Row[]; +} + +const EMPTY_WORKSPACE: SecurityWorkspace = { + overview: null, + authorizations: [], + scopes: [], + campaigns: [], + approvals: [], + findings: [], + evidence: [], + packages: [], + tools: [], + mcp: [], + policies: [], + audit: [], +}; + +const TABS: Array<{ id: TabType; labelKey: TKey }> = [ + { id: "overview", labelKey: "security.tab.overview" }, + { id: "authorizations", labelKey: "security.tab.authorizations" }, + { id: "scopes", labelKey: "security.tab.scopes" }, + { id: "campaigns", labelKey: "security.tab.campaigns" }, + { id: "approvals", labelKey: "security.tab.approvals" }, + { id: "findings", labelKey: "security.tab.findings" }, + { id: "evidence", labelKey: "security.tab.evidence" }, + { id: "skills", labelKey: "security.tab.skills" }, + { id: "tools", labelKey: "security.tab.tools" }, + { id: "mcp", labelKey: "security.tab.mcp" }, + { id: "policies", labelKey: "security.tab.policies" }, + { id: "audit", labelKey: "security.tab.audit" }, +]; + +async function readData(res: Response, fallback: T): Promise { + const payload = await readJsonIfOk<{ data?: T }>(res); + return payload?.data ?? fallback; +} + +export function Security({ apiBase }: SecurityProps): React.JSX.Element { + const t = useT(); + const [tab, setTab] = useState(readTabFromHash); + const [statusMessage, setStatusMessage] = useState(null); + + const selectTab = (next: TabType) => { + setTab(next); + navigateHash(hashForTab(next)); + }; + + useEffect(() => { + const onHash = () => setTab(readTabFromHash()); + window.addEventListener("hashchange", onHash); + return () => window.removeEventListener("hashchange", onHash); + }, []); + + const loadWorkspace = useCallback(async (signal: AbortSignal): Promise => { + const [ + overviewRes, + authorizationsRes, + scopesRes, + campaignsRes, + approvalsRes, + findingsRes, + packagesRes, + toolsRes, + mcpRes, + policiesRes, + auditRes, + ] = await Promise.all([ + fetch(`${apiBase}/api/security/overview`, { signal }), + fetch(`${apiBase}/api/security/authorizations`, { signal }), + fetch(`${apiBase}/api/security/scopes`, { signal }), + fetch(`${apiBase}/api/security/campaigns`, { signal }), + fetch(`${apiBase}/api/security/approvals`, { signal }), + fetch(`${apiBase}/api/security/findings`, { signal }), + fetch(`${apiBase}/api/security/skills`, { signal }), + fetch(`${apiBase}/api/security/tools`, { signal }), + fetch(`${apiBase}/api/security/mcp`, { signal }), + fetch(`${apiBase}/api/security/policies`, { signal }), + fetch(`${apiBase}/api/security/audit`, { signal }), + ]); + const campaigns = await readData(campaignsRes, []); + const campaignId = typeof campaigns[0]?.id === "string" ? campaigns[0].id : ""; + const evidence = campaignId + ? await readData( + await fetch(`${apiBase}/api/security/campaigns/${encodeURIComponent(campaignId)}/evidence`, { signal }), + [], + ) + : []; + return { + overview: await readData(overviewRes, null), + authorizations: await readData(authorizationsRes, []), + scopes: await readData(scopesRes, []), + campaigns, + approvals: await readData(approvalsRes, []), + findings: await readData(findingsRes, []), + evidence, + packages: await readData(packagesRes, []), + tools: await readData(toolsRes, []), + mcp: await readData(mcpRes, []), + policies: await readData(policiesRes, []), + audit: await readData(auditRes, []), + }; + }, [apiBase]); + + const resource = useDataSurface( + `security-workspace:${apiBase}`, + [apiBase], + loadWorkspace, + { isEmpty: () => false }, + ); + const workspace = resource.state.data ?? EMPTY_WORKSPACE; + const { + overview, + authorizations, + scopes, + campaigns, + approvals, + findings, + evidence, + packages, + tools, + mcp, + policies, + audit, + } = workspace; + const loading = resource.state.refreshing || resource.state.showSkeleton; + const hashList = useMemo(() => ["security", ...SECURITY_TAB_HASHES].join(", "), []); + + const post = async (path: string, body?: Record) => { + try { + setStatusMessage(t("security.status.posting", { path })); + const res = await fetch(`${apiBase}${path}`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body ?? { actor: "gui-reviewer" }), + }); + const data = await res.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(res.status); + setStatusMessage(res.ok ? t("security.status.ok", { path }) : t("security.status.failed", { error })); + resource.refresh(); + } catch (e) { + setStatusMessage( + t("security.status.failed", { + error: e instanceof Error ? e.message : String(e), + }), + ); + } + }; + + return ( +
+
+
+

{t("security.title")}

+

{t("security.subtitle")}

+
+ +
+ +
+ {t("security.banner")} + {overview && !overview.enabled ? ` ${t("security.flagOff")}` : ""} +
+ + {statusMessage &&
{statusMessage}
} + + {resource.state.showError && ( +
{t("security.loadFailed")}
+ )} + +
+ {TABS.map(entry => ( + + ))} +
+ + {tab === "overview" && overview && ( +
+
{t("security.card.enabled")}{overview.enabled ? t("security.yes") : t("security.no")}
+
{t("security.card.campaigns")}{overview.campaigns_active}
+
{t("security.card.approvals")}{overview.approvals_pending}
+
{t("security.card.leads")}{overview.leads_open}
+
{t("security.card.validated")}{overview.findings_validated}
+
{t("security.card.blocked")}{overview.blocked_actions}
+
{t("security.card.authExpiring")}{overview.authorizations_expiring}
+
{t("security.card.breakers")}{overview.circuit_breakers_open}
+
+ )} + + {tab === "authorizations" && ( + + + + {authorizations.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.type")}{t("security.col.status")}{t("security.col.validUntil")}
{String(row.id)}{String(row.type)}{String(row.status)}{String(row.valid_until)}
+ )} + + {tab === "scopes" && ( + + + + {scopes.map(row => ( + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.status")}
{String(row.id)}{String(row.name)}{String(row.status)}
+ )} + + {tab === "campaigns" && ( + + + + {campaigns.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.status")}{t("security.col.actions")}
{String(row.id)}{String(row.name)}{String(row.status)} + + + +
+ )} + + {tab === "approvals" && ( + + + + {approvals.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.target")}{t("security.col.status")}{t("security.col.actions")}
{String(row.id)}{String(row.target)}{String(row.status)} + + +
+ )} + + {tab === "findings" && ( + + + + {findings.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.title")}{t("security.col.status")}{t("security.col.severity")}
{String(row.id)}{String(row.title)}{String(row.status)}{String(row.severity)}
+ )} + + {tab === "evidence" && ( + <> +
+ {campaigns[0] + ? t("security.evidence.campaign", { + campaign: String(campaigns[0].name ?? campaigns[0].id), + }) + : t("security.evidence.none")} +
+ + + + {evidence.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.type")}{t("security.col.sha256")}{t("security.col.preview")}
{String(row.id)}{String(row.type)}{String(row.sha256).slice(0, 12)}…{String(row.redacted_preview ?? "")}
+ + )} + + {tab === "skills" && ( + + + + {packages.map(row => ( + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.compatibility")}
{String(row.id)}{String(row.name)}{String(row.compatibility)}
+ )} + + {tab === "tools" && ( + + + + {tools.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.kind")}{t("security.col.allowlisted")}
{String(row.id)}{String(row.name)}{String(row.kind)}{String(row.allowlisted)}
+ )} + + {tab === "mcp" && ( + + + + {mcp.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.health")}{t("security.col.credential")}
{String(row.id)}{String(row.name)}{String(row.health_state)}{String(row.credential_ref ?? "")}
+ )} + + {tab === "policies" && ( + + + + {policies.map(row => ( + + + + + + + ))} + +
{t("security.col.id")}{t("security.col.name")}{t("security.col.ceiling")}{t("security.col.blockR3")}
{String(row.id)}{String(row.name)}{String(row.default_risk_ceiling)}{String(row.block_r3)}
+ )} + + {tab === "audit" && ( + + + + {audit.map(row => ( + + + + + + + ))} + +
{t("security.col.when")}{t("security.col.event")}{t("security.col.actor")}{t("security.col.target")}
{String(row.created_at)}{String(row.event_type)}{String(row.actor_id)}{String(row.target ?? "")}
+ )} + +

{t("security.hashes", { hashes: hashList })}

+
+ ); +} diff --git a/gui/src/pages/Skills.tsx b/gui/src/pages/Skills.tsx new file mode 100644 index 00000000000..6731f910976 --- /dev/null +++ b/gui/src/pages/Skills.tsx @@ -0,0 +1,722 @@ +import React, { useCallback, useEffect, useState } from "react"; +import { useDataSurface } from "../data-surface"; +import { readJsonIfOk } from "../fetch-json"; +import { navigateHash, normalizeHashPath } from "../hash-routing"; +import { useT, type TKey } from "../i18n/shared"; +import "../styles-skills-workspace.css"; + +export interface SkillsProps { + apiBase: string; +} + +type TabType = + | "overview" + | "marketplace" + | "registry" + | "editor" + | "matrix" + | "agents" + | "nodes" + | "drift" + | "reviews" + | "audit"; + +interface SkillItem { + id: string; + namespace: string; + slug: string; + display_name: string; + description: string; + status: string; + current_version: string; + trust_level: string; + risk_level?: string; + tags: string[]; +} + +interface MarketplaceItem { + id: string; + name: string; + description: string; + version: string; + author: string; + license: string; + tags: string[]; + sourceUrl: string; + verified: boolean; +} + +interface DeploymentItem { + id: string; + skill_version_id: string; + node_id: string; + agent_id: string; + scope: string; + target_path: string; + status: string; + desired_sha256: string; + actual_sha256?: string; +} + +interface AuditEventItem { + id: string; + event_type: string; + actor_type: string; + actor_id?: string; + skill_id?: string; + created_at: string; + metadata?: Record; +} + +const TAB_FROM_HASH: Record = { + skills: "overview", + "skills/marketplace": "marketplace", + "skills/registry": "registry", + "skills/editor": "editor", + "skills/matrix": "matrix", + "skills/agents": "agents", + "skills/nodes": "nodes", + "skills/drift": "drift", + "skills/reviews": "reviews", + "skills/audit": "audit", +}; + +function readTabFromHash(): TabType { + const raw = normalizeHashPath(typeof window !== "undefined" ? window.location.hash : "skills"); + return TAB_FROM_HASH[raw] ?? "overview"; +} + +function hashForTab(tab: TabType): string { + return tab === "overview" ? "skills" : `skills/${tab}`; +} + +interface SkillsWorkspace { + skills: SkillItem[]; + marketplaceItems: MarketplaceItem[]; + deployments: DeploymentItem[]; + auditEvents: AuditEventItem[]; +} + +const EMPTY_WORKSPACE: SkillsWorkspace = { + skills: [], + marketplaceItems: [], + deployments: [], + auditEvents: [], +}; + +const TABS: Array<{ id: TabType; labelKey: TKey; count?: boolean }> = [ + { id: "overview", labelKey: "skills.tab.overview" }, + { id: "marketplace", labelKey: "skills.tab.marketplace" }, + { id: "registry", labelKey: "skills.tab.registry", count: true }, + { id: "editor", labelKey: "skills.tab.editor" }, + { id: "matrix", labelKey: "skills.tab.matrix" }, + { id: "agents", labelKey: "skills.tab.agents" }, + { id: "nodes", labelKey: "skills.tab.nodes" }, + { id: "drift", labelKey: "skills.tab.drift" }, + { id: "reviews", labelKey: "skills.tab.reviews" }, + { id: "audit", labelKey: "skills.tab.audit" }, +]; + +async function readData(res: Response, fallback: T): Promise { + const payload = await readJsonIfOk<{ data?: T }>(res); + return payload?.data ?? fallback; +} + +export function Skills({ apiBase }: SkillsProps): React.JSX.Element { + const t = useT(); + const [tab, setTab] = useState(readTabFromHash); + const [searchQuery, setSearchQuery] = useState(""); + const [statusMessage, setStatusMessage] = useState(null); + const [editorName, setEditorName] = useState(() => t("skills.editorDefaultName")); + const [editorVersion, setEditorVersion] = useState("1.0.0"); + const [editorMarkdown, setEditorMarkdown] = useState(() => t("skills.editorDefaultMarkdown")); + + const selectTab = (next: TabType) => { + setTab(next); + navigateHash(hashForTab(next)); + }; + + useEffect(() => { + const onHash = () => setTab(readTabFromHash()); + window.addEventListener("hashchange", onHash); + return () => window.removeEventListener("hashchange", onHash); + }, []); + + const loadWorkspace = useCallback(async (signal: AbortSignal): Promise => { + const [skillsRes, marketplaceRes, deploymentsRes, auditRes] = await Promise.all([ + fetch(`${apiBase}/api/skills`, { signal }), + fetch(`${apiBase}/api/skill-marketplace/search?q=${encodeURIComponent(searchQuery)}`, { signal }), + fetch(`${apiBase}/api/skill-deployments`, { signal }), + fetch(`${apiBase}/api/skill-audit`, { signal }), + ]); + return { + skills: await readData(skillsRes, []), + marketplaceItems: await readData(marketplaceRes, []), + deployments: await readData(deploymentsRes, []), + auditEvents: await readData(auditRes, []), + }; + }, [apiBase, searchQuery]); + + const resource = useDataSurface( + `skills-workspace:${apiBase}:${searchQuery}`, + [apiBase, searchQuery], + loadWorkspace, + { isEmpty: () => false }, + ); + const workspace = resource.state.data ?? EMPTY_WORKSPACE; + const { skills, marketplaceItems, deployments, auditEvents } = workspace; + const loading = resource.state.refreshing || resource.state.showSkeleton; + + const handleImportMarketplace = async (refId: string) => { + try { + setStatusMessage(t("skills.status.importing", { id: refId })); + const res = await fetch(`${apiBase}/api/skill-imports`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ type: "marketplace", ref: refId }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(res.status); + throw new Error(error); + } + setStatusMessage(t("skills.status.imported", { id: refId })); + resource.refresh(); + } catch (e) { + setStatusMessage(t("skills.status.importFailed", { error: e instanceof Error ? e.message : String(e) })); + } + }; + + const handlePublish = async (skillId: string) => { + try { + setStatusMessage(t("skills.status.publishing", { id: skillId })); + const res = await fetch(`${apiBase}/api/skills/${encodeURIComponent(skillId)}/publish`, { method: "POST" }); + if (!res.ok) { + const data = await res.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(res.status); + throw new Error(error); + } + setStatusMessage(t("skills.status.published", { id: skillId })); + resource.refresh(); + } catch (e) { + setStatusMessage(t("skills.status.publishFailed", { error: e instanceof Error ? e.message : String(e) })); + } + }; + + const handleDeployQuick = async (skillId: string, version: string, agentType: string) => { + try { + setStatusMessage(t("skills.status.planning", { id: skillId, agent: agentType })); + const planRes = await fetch(`${apiBase}/api/skill-deployments/plan`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ skillVersionId: `${skillId}@${version}`, agentType, scope: "user", nodeId: "local" }), + }); + const planData = await planRes.json().catch(() => ({})) as { error?: unknown; plan?: { planId?: string } }; + if (!planRes.ok) { + const error = typeof planData.error === "string" ? planData.error : String(planRes.status); + throw new Error(error); + } + if (!planData?.plan?.planId) { + throw new Error("No deployment plan id returned"); + } + const deployRes = await fetch(`${apiBase}/api/skill-deployments`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ planId: planData.plan.planId }), + }); + if (!deployRes.ok) { + const data = await deployRes.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(deployRes.status); + throw new Error(error); + } + setStatusMessage(t("skills.status.deployed", { id: skillId, agent: agentType })); + resource.refresh(); + } catch (e) { + setStatusMessage(t("skills.status.deployFailed", { error: e instanceof Error ? e.message : String(e) })); + } + }; + + const saveDraft = async () => { + try { + const res = await fetch(`${apiBase}/api/skills`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: editorName, markdown: editorMarkdown }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})) as { error?: unknown }; + const error = typeof data.error === "string" ? data.error : String(res.status); + throw new Error(error); + } + setStatusMessage(t("skills.status.saved")); + resource.refresh(); + } catch (e) { + setStatusMessage(t("skills.status.saveFailed", { error: String(e) })); + } + }; + + const agentStatus = (skill: SkillItem, agent: string) => { + const dep = deployments.find(d => d.agent_id === agent && d.skill_version_id.startsWith(`${skill.id}@`)); + if (!dep) return { label: t("skills.notInstalled"), cls: "empty" }; + if (dep.status === "DEPLOYED") return { label: t("skills.inSync", { version: skill.current_version }), cls: "in-sync" }; + if (dep.status === "FAILED") return { label: t("skills.blocked"), cls: "blocked" }; + return { label: dep.status, cls: "drift" }; + }; + + return ( +
+
+
+

{t("skills.title")}

+

{t("skills.subtitle")}

+
+
+ +
+
+ + {statusMessage && ( +
+ {statusMessage} +
+ )} + + {resource.state.showError && ( +
+ {t("skills.loadFailed")} +
+ )} + +
+ {TABS.map(entry => ( + + ))} +
+ + {tab === "overview" && ( +
+
+
+ {t("skills.card.registered")} + {skills.length} +
+
+ {t("skills.card.deployments")} + {deployments.filter(d => d.status === "DEPLOYED").length} +
+
+ {t("skills.card.marketplace")} + {marketplaceItems.length} +
+
+ {t("skills.card.audit")} + {auditEvents.length} +
+
+ +
+

{t("skills.recentActivity")}

+ {auditEvents.length === 0 ? ( +

{t("skills.noEvents")}

+ ) : ( + + + + + + + + + + + {auditEvents.slice(0, 8).map(ev => ( + + + + + + + ))} + +
{t("skills.col.timestamp")}{t("skills.col.event")}{t("skills.col.actor")}{t("skills.col.target")}
{new Date(ev.created_at).toLocaleTimeString()}{ev.event_type}{ev.actor_type}{ev.skill_id ?? t("skills.system")}
+ )} +
+
+ )} + + {/* TAB 2: MARKETPLACE */} + {tab === "marketplace" && ( +
+
+ setSearchQuery(e.target.value)} + style={{ + flexGrow: 1, + background: "#0f1115", + border: "1px solid var(--border, #262933)", + color: "#fff", + padding: "10px 14px", + borderRadius: 6, + fontSize: 14, + }} + /> +
+ +
+ {marketplaceItems.map(item => ( +
+
+
+

{item.name}

+ {t("skills.byAuthor", { version: item.version, author: item.author })} +
+ {item.verified && ( + + {t("skills.verified")} + + )} +
+

{item.description}

+
+ {item.tags.map(tag => ( + {tag} + ))} +
+
+ +
+
+ ))} +
+
+ )} + + {/* TAB 3: REGISTRY */} + {tab === "registry" && ( +
+ {skills.length === 0 ? ( +
+

{t("skills.emptyRegistry")}

+
+ ) : ( +
+ + + + + + + + + + + + + {skills.map(s => ( + + + + + + + + + ))} + +
{t("skills.col.skill")}{t("skills.col.version")}{t("skills.col.status")}{t("skills.col.risk")}{t("skills.col.tags")}{t("skills.col.actions")}
+ {s.display_name}
+ {s.id} +
v{s.current_version} + {s.status} + + {s.risk_level ?? "low"} + + {s.tags.slice(0, 3).map(tag => ( + {tag} + ))} + +
+ {s.status !== "PUBLISHED" && ( + + )} + +
+
+
+ )} +
+ )} + + {/* TAB 4: SKILL EDITOR */} + {tab === "editor" && ( +
+
+

{t("skills.editorTitle")}

+
+ setEditorName(e.target.value)} + style={{ flex: 1, background: "#0f1115", border: "1px solid var(--border, #262933)", color: "#fff", padding: "6px 10px", borderRadius: 4, fontSize: 13 }} + /> + setEditorVersion(e.target.value)} + style={{ width: 80, background: "#0f1115", border: "1px solid var(--border, #262933)", color: "#fff", padding: "6px 10px", borderRadius: 4, fontSize: 13 }} + /> +
+