From e02b5fe6e4de82b0f9b08355ce22e17687abee07 Mon Sep 17 00:00:00 2001 From: LukeGus Date: Sun, 4 Oct 2026 22:40:06 -0500 Subject: [PATCH] chore: move plugins to their own repos and drop the plugins folder --- .github/workflows/beta-release.yml | 4 - .github/workflows/crowdin-sync.yml | 2 +- .github/workflows/pr-check.yml | 3 - .github/workflows/release.yml | 9 +- .gitignore | 1 - crowdin.yml | 2 - docker/Dockerfile | 20 +- docker/bundled-plugins.json | 139 +- eslint.config.mjs | 129 - knip.json | 9 - package-lock.json | 3735 +-------------- package.json | 12 +- packages/plugin-sdk/cli/index.mjs | 2 +- plugins/acme-ssl/.commitlintrc.json | 21 - plugins/acme-ssl/.github/funding.yml | 1 - .../acme-ssl/.github/pull_request_template.md | 34 - plugins/acme-ssl/.gitignore | 9 - plugins/acme-ssl/.husky/commit-msg | 1 - plugins/acme-ssl/.husky/pre-commit | 1 - plugins/acme-ssl/.prettierignore | 4 - plugins/acme-ssl/.prettierrc | 9 - plugins/acme-ssl/CHANGELOG.json | 26 - plugins/acme-ssl/CONTRIBUTING.md | 27 - plugins/acme-ssl/README.md | 38 - plugins/acme-ssl/locales/en.json | 61 - .../acme-ssl/locales/translated/af_ZA.json | 61 - .../acme-ssl/locales/translated/ar_SA.json | 61 - .../acme-ssl/locales/translated/bg_BG.json | 61 - .../acme-ssl/locales/translated/bn_BD.json | 61 - .../acme-ssl/locales/translated/ca_ES.json | 61 - .../acme-ssl/locales/translated/cs_CZ.json | 61 - .../acme-ssl/locales/translated/da_DK.json | 61 - .../acme-ssl/locales/translated/de_DE.json | 61 - .../acme-ssl/locales/translated/el_GR.json | 61 - .../acme-ssl/locales/translated/es_ES.json | 61 - .../acme-ssl/locales/translated/fi_FI.json | 61 - .../acme-ssl/locales/translated/fr_FR.json | 61 - .../acme-ssl/locales/translated/he_IL.json | 61 - .../acme-ssl/locales/translated/hi_IN.json | 61 - .../acme-ssl/locales/translated/hu_HU.json | 61 - .../acme-ssl/locales/translated/id_ID.json | 61 - .../acme-ssl/locales/translated/it_IT.json | 61 - .../acme-ssl/locales/translated/ja_JP.json | 61 - .../acme-ssl/locales/translated/ko_KR.json | 61 - .../acme-ssl/locales/translated/nl_NL.json | 61 - .../acme-ssl/locales/translated/no_NO.json | 61 - .../acme-ssl/locales/translated/pl_PL.json | 61 - .../acme-ssl/locales/translated/pt_BR.json | 61 - .../acme-ssl/locales/translated/pt_PT.json | 61 - .../acme-ssl/locales/translated/ro_RO.json | 61 - .../acme-ssl/locales/translated/ru_RU.json | 61 - .../acme-ssl/locales/translated/sr_SP.json | 61 - .../acme-ssl/locales/translated/sv_SE.json | 61 - .../acme-ssl/locales/translated/th_TH.json | 61 - .../acme-ssl/locales/translated/tr_TR.json | 61 - .../acme-ssl/locales/translated/uk_UA.json | 61 - .../acme-ssl/locales/translated/vi_VN.json | 61 - .../acme-ssl/locales/translated/zh_CN.json | 61 - .../acme-ssl/locales/translated/zh_TW.json | 61 - plugins/acme-ssl/manifest.json | 122 - plugins/acme-ssl/package.json | 44 - plugins/acme-ssl/src/backend/acme.ts | 217 - plugins/acme-ssl/src/backend/cloudflare.ts | 78 - plugins/acme-ssl/src/backend/index.ts | 50 - plugins/acme-ssl/src/backend/renewal.ts | 28 - plugins/acme-ssl/src/backend/routes.ts | 73 - plugins/acme-ssl/src/backend/runner.ts | 173 - plugins/acme-ssl/src/backend/settings.ts | 77 - .../src/frontend/AcmeStatusSetting.tsx | 121 - plugins/acme-ssl/src/frontend/index.tsx | 10 - .../acme-ssl/tests/backend/acme-ssl.test.ts | 269 -- .../acme-ssl/tests/backend/transport.test.ts | 102 - .../acme-ssl/tests/frontend/activate.test.tsx | 85 - plugins/acme-ssl/tsconfig.json | 4 - plugins/acme-ssl/vitest.config.ts | 3 - plugins/ai/.commitlintrc.json | 21 - plugins/ai/.github/funding.yml | 1 - plugins/ai/.github/pull_request_template.md | 34 - plugins/ai/.gitignore | 9 - plugins/ai/.husky/commit-msg | 1 - plugins/ai/.husky/pre-commit | 1 - plugins/ai/.prettierignore | 4 - plugins/ai/.prettierrc | 9 - plugins/ai/AGENTS-FEATURE.md | 47 - plugins/ai/CHANGELOG.json | 38 - plugins/ai/CONTRIBUTING.md | 37 - plugins/ai/README.md | 48 - plugins/ai/locales/en.json | 198 - plugins/ai/locales/translated/af_ZA.json | 198 - plugins/ai/locales/translated/ar_SA.json | 198 - plugins/ai/locales/translated/bg_BG.json | 198 - plugins/ai/locales/translated/bn_BD.json | 198 - plugins/ai/locales/translated/ca_ES.json | 198 - plugins/ai/locales/translated/cs_CZ.json | 198 - plugins/ai/locales/translated/da_DK.json | 198 - plugins/ai/locales/translated/de_DE.json | 198 - plugins/ai/locales/translated/el_GR.json | 198 - plugins/ai/locales/translated/es_ES.json | 198 - plugins/ai/locales/translated/fi_FI.json | 198 - plugins/ai/locales/translated/fr_FR.json | 198 - plugins/ai/locales/translated/he_IL.json | 198 - plugins/ai/locales/translated/hi_IN.json | 198 - plugins/ai/locales/translated/hu_HU.json | 198 - plugins/ai/locales/translated/id_ID.json | 198 - plugins/ai/locales/translated/it_IT.json | 198 - plugins/ai/locales/translated/ja_JP.json | 198 - plugins/ai/locales/translated/ko_KR.json | 198 - plugins/ai/locales/translated/nl_NL.json | 198 - plugins/ai/locales/translated/no_NO.json | 198 - plugins/ai/locales/translated/pl_PL.json | 198 - plugins/ai/locales/translated/pt_BR.json | 198 - plugins/ai/locales/translated/pt_PT.json | 198 - plugins/ai/locales/translated/ro_RO.json | 198 - plugins/ai/locales/translated/ru_RU.json | 198 - plugins/ai/locales/translated/sr_SP.json | 198 - plugins/ai/locales/translated/sv_SE.json | 198 - plugins/ai/locales/translated/th_TH.json | 198 - plugins/ai/locales/translated/tr_TR.json | 198 - plugins/ai/locales/translated/uk_UA.json | 198 - plugins/ai/locales/translated/vi_VN.json | 198 - plugins/ai/locales/translated/zh_CN.json | 198 - plugins/ai/locales/translated/zh_TW.json | 198 - plugins/ai/manifest.json | 207 - .../migrations/mysql/0001_adopt_ai_tables.sql | 55 - .../migrations/mysql/0002_provider_sync.sql | 1 - .../migrations/mysql/0003_agent_sessions.sql | 46 - .../postgres/0001_adopt_ai_tables.sql | 60 - .../postgres/0002_provider_sync.sql | 1 - .../postgres/0003_agent_sessions.sql | 46 - plugins/ai/migrations/snapshot.json | 368 -- .../sqlite/0001_adopt_ai_tables.sql | 60 - .../migrations/sqlite/0002_provider_sync.sql | 2 - .../migrations/sqlite/0003_agent_sessions.sql | 46 - plugins/ai/package.json | 50 - plugins/ai/src/backend/agents/forwarding.ts | 95 - plugins/ai/src/backend/agents/install.ts | 250 - .../ai/src/backend/agents/remote-runner.ts | 192 - plugins/ai/src/backend/agents/routes.ts | 709 --- .../ai/src/backend/agents/session-state.ts | 64 - plugins/ai/src/backend/agents/store.ts | 221 - plugins/ai/src/backend/agents/types.ts | 84 - plugins/ai/src/backend/agents/workspace.ts | 153 - plugins/ai/src/backend/context.ts | 55 - plugins/ai/src/backend/egress.ts | 134 - plugins/ai/src/backend/engine.ts | 175 - plugins/ai/src/backend/errors.ts | 6 - plugins/ai/src/backend/gating.ts | 90 - plugins/ai/src/backend/history.ts | 98 - plugins/ai/src/backend/host-import.ts | 16 - plugins/ai/src/backend/index.ts | 44 - plugins/ai/src/backend/providers/anthropic.ts | 161 - plugins/ai/src/backend/providers/gemini.ts | 191 - plugins/ai/src/backend/providers/http.ts | 257 -- plugins/ai/src/backend/providers/ollama.ts | 131 - plugins/ai/src/backend/providers/openai.ts | 182 - plugins/ai/src/backend/providers/registry.ts | 64 - plugins/ai/src/backend/providers/types.ts | 100 - plugins/ai/src/backend/redaction.ts | 90 - plugins/ai/src/backend/repository.ts | 421 -- plugins/ai/src/backend/routes.ts | 1106 ----- plugins/ai/src/backend/services.ts | 163 - plugins/ai/src/backend/sync.ts | 50 - plugins/ai/src/backend/tables.ts | 222 - plugins/ai/src/backend/tools/catalog.ts | 86 - .../ai/src/backend/tools/command-allowlist.ts | 136 - plugins/ai/src/backend/tools/executor.ts | 280 -- plugins/ai/src/backend/tools/propose-tools.ts | 251 - plugins/ai/src/backend/tools/read-tools.ts | 313 -- plugins/ai/src/backend/tools/types.ts | 88 - plugins/ai/src/frontend/AiAssistantStep.tsx | 83 - plugins/ai/src/frontend/AiMessage.tsx | 23 - plugins/ai/src/frontend/AiPanel.tsx | 442 -- .../ai/src/frontend/AiProviderSettings.tsx | 558 --- plugins/ai/src/frontend/AiToolCall.tsx | 80 - plugins/ai/src/frontend/ProposalCard.tsx | 208 - .../ai/src/frontend/agents/AgentComposer.tsx | 236 - plugins/ai/src/frontend/agents/AgentPanel.tsx | 487 -- .../ai/src/frontend/agents/Attachments.tsx | 138 - .../ai/src/frontend/agents/InstallRuntime.tsx | 119 - .../ai/src/frontend/agents/SessionTools.tsx | 148 - .../ai/src/frontend/agents/useAgentStream.ts | 104 - plugins/ai/src/frontend/ai-api.ts | 244 - plugins/ai/src/frontend/app-ref.ts | 13 - plugins/ai/src/frontend/errors.ts | 4 - plugins/ai/src/frontend/index.tsx | 221 - plugins/ai/src/frontend/labels.ts | 84 - .../frontend/settings/ProvidersSetting.tsx | 59 - .../src/frontend/terminal/TerminalAiPanel.tsx | 355 -- .../terminal/TerminalProposalCard.tsx | 142 - .../src/frontend/terminal/panel-geometry.ts | 90 - .../frontend/terminal/terminal-slot-types.ts | 31 - plugins/ai/src/frontend/transcript.ts | 82 - plugins/ai/src/frontend/use-ai-stream.ts | 240 - plugins/ai/src/frontend/useMentions.ts | 114 - plugins/ai/tests/backend/activate.test.ts | 73 - .../ai/tests/backend/agents/access.test.ts | 140 - .../ai/tests/backend/agents/agents.test.ts | 158 - .../tests/backend/agents/forwarding.test.ts | 105 - .../ai/tests/backend/agents/install.test.ts | 74 - plugins/ai/tests/backend/agents/queue.test.ts | 110 - .../backend/agents/session-state.test.ts | 48 - plugins/ai/tests/backend/agents/store.test.ts | 101 - .../ai/tests/backend/agents/workspace.test.ts | 108 - .../backend/command-history-service.test.ts | 60 - plugins/ai/tests/backend/context.test.ts | 47 - plugins/ai/tests/backend/egress.test.ts | 122 - plugins/ai/tests/backend/engine.test.ts | 278 -- plugins/ai/tests/backend/gating.test.ts | 127 - plugins/ai/tests/backend/helpers.ts | 181 - plugins/ai/tests/backend/history.test.ts | 116 - plugins/ai/tests/backend/migrations.test.ts | 124 - plugins/ai/tests/backend/permissions.test.ts | 63 - .../backend/providers/provider-http.test.ts | 118 - .../tests/backend/providers/providers.test.ts | 337 -- plugins/ai/tests/backend/redaction.test.ts | 76 - plugins/ai/tests/backend/routes.test.ts | 216 - plugins/ai/tests/backend/sync.test.ts | 112 - .../backend/tools/command-allowlist.test.ts | 79 - .../backend/tools/proposal-executor.test.ts | 281 -- .../tools/readonly-command-tool.test.ts | 75 - .../tests/backend/tools/tool-catalog.test.ts | 112 - plugins/ai/tests/frontend/AgentPanel.test.tsx | 145 - .../frontend/AiProviderSettings.test.tsx | 99 - plugins/ai/tests/frontend/activate.test.tsx | 71 - .../ai/tests/frontend/agent-stream.test.tsx | 71 - plugins/ai/tests/frontend/labels.test.ts | 73 - plugins/ai/tests/frontend/transcript.test.ts | 67 - plugins/ai/tsconfig.json | 4 - plugins/ai/vitest.config.ts | 3 - plugins/alerts/.commitlintrc.json | 21 - plugins/alerts/.github/funding.yml | 1 - .../alerts/.github/pull_request_template.md | 34 - plugins/alerts/.gitignore | 9 - plugins/alerts/.husky/commit-msg | 1 - plugins/alerts/.husky/pre-commit | 1 - plugins/alerts/.prettierignore | 4 - plugins/alerts/.prettierrc | 9 - plugins/alerts/CHANGELOG.json | 30 - plugins/alerts/CONTRIBUTING.md | 27 - plugins/alerts/README.md | 39 - plugins/alerts/locales/en.json | 175 - plugins/alerts/locales/translated/af_ZA.json | 175 - plugins/alerts/locales/translated/ar_SA.json | 175 - plugins/alerts/locales/translated/bg_BG.json | 175 - plugins/alerts/locales/translated/bn_BD.json | 175 - plugins/alerts/locales/translated/ca_ES.json | 175 - plugins/alerts/locales/translated/cs_CZ.json | 175 - plugins/alerts/locales/translated/da_DK.json | 175 - plugins/alerts/locales/translated/de_DE.json | 175 - plugins/alerts/locales/translated/el_GR.json | 175 - plugins/alerts/locales/translated/es_ES.json | 175 - plugins/alerts/locales/translated/fi_FI.json | 175 - plugins/alerts/locales/translated/fr_FR.json | 175 - plugins/alerts/locales/translated/he_IL.json | 175 - plugins/alerts/locales/translated/hi_IN.json | 175 - plugins/alerts/locales/translated/hu_HU.json | 175 - plugins/alerts/locales/translated/id_ID.json | 175 - plugins/alerts/locales/translated/it_IT.json | 175 - plugins/alerts/locales/translated/ja_JP.json | 175 - plugins/alerts/locales/translated/ko_KR.json | 175 - plugins/alerts/locales/translated/nl_NL.json | 175 - plugins/alerts/locales/translated/no_NO.json | 175 - plugins/alerts/locales/translated/pl_PL.json | 175 - plugins/alerts/locales/translated/pt_BR.json | 175 - plugins/alerts/locales/translated/pt_PT.json | 175 - plugins/alerts/locales/translated/ro_RO.json | 175 - plugins/alerts/locales/translated/ru_RU.json | 175 - plugins/alerts/locales/translated/sr_SP.json | 175 - plugins/alerts/locales/translated/sv_SE.json | 175 - plugins/alerts/locales/translated/th_TH.json | 175 - plugins/alerts/locales/translated/tr_TR.json | 175 - plugins/alerts/locales/translated/uk_UA.json | 175 - plugins/alerts/locales/translated/vi_VN.json | 175 - plugins/alerts/locales/translated/zh_CN.json | 175 - plugins/alerts/locales/translated/zh_TW.json | 175 - plugins/alerts/manifest.json | 149 - .../migrations/mysql/0001_create_alerts.sql | 54 - .../postgres/0001_create_alerts.sql | 55 - plugins/alerts/migrations/snapshot.json | 203 - .../migrations/sqlite/0001_create_alerts.sql | 55 - plugins/alerts/package.json | 47 - plugins/alerts/src/backend/announcements.ts | 164 - plugins/alerts/src/backend/hub.ts | 193 - plugins/alerts/src/backend/index.ts | 84 - plugins/alerts/src/backend/mail.ts | 46 - plugins/alerts/src/backend/repository.ts | 552 --- plugins/alerts/src/backend/routes.ts | 694 --- plugins/alerts/src/backend/senders.ts | 224 - plugins/alerts/src/backend/stream.ts | 77 - plugins/alerts/src/backend/tables.ts | 101 - .../alerts/src/frontend/AlertFeedWidget.tsx | 197 - plugins/alerts/src/frontend/AlertList.tsx | 166 - plugins/alerts/src/frontend/AlertToaster.tsx | 49 - plugins/alerts/src/frontend/AlertsView.tsx | 69 - plugins/alerts/src/frontend/ChannelDialog.tsx | 325 -- plugins/alerts/src/frontend/ChannelsView.tsx | 204 - plugins/alerts/src/frontend/InboxView.tsx | 283 -- plugins/alerts/src/frontend/RulesView.tsx | 337 -- plugins/alerts/src/frontend/api.ts | 123 - plugins/alerts/src/frontend/channel-config.ts | 101 - plugins/alerts/src/frontend/format.ts | 30 - plugins/alerts/src/frontend/index.tsx | 112 - plugins/alerts/src/frontend/popups.ts | 9 - plugins/alerts/src/frontend/sections.ts | 26 - plugins/alerts/src/frontend/store.ts | 177 - plugins/alerts/src/types.ts | 91 - plugins/alerts/tests/backend/helpers.ts | 142 - plugins/alerts/tests/backend/hub.test.ts | 328 -- .../alerts/tests/backend/migrations.test.ts | 87 - plugins/alerts/tests/backend/routes.test.ts | 434 -- plugins/alerts/tests/backend/senders.test.ts | 213 - .../alerts/tests/frontend/activate.test.tsx | 197 - plugins/alerts/tests/frontend/store.test.ts | 140 - plugins/alerts/tsconfig.json | 4 - plugins/alerts/vitest.config.ts | 3 - plugins/automations/.commitlintrc.json | 21 - plugins/automations/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/automations/.gitignore | 9 - plugins/automations/.husky/commit-msg | 1 - plugins/automations/.husky/pre-commit | 1 - plugins/automations/.prettierignore | 4 - plugins/automations/.prettierrc | 9 - plugins/automations/CHANGELOG.json | 34 - plugins/automations/CONTRIBUTING.md | 19 - plugins/automations/README.md | 53 - plugins/automations/locales/en.json | 225 - .../automations/locales/translated/af_ZA.json | 225 - .../automations/locales/translated/ar_SA.json | 225 - .../automations/locales/translated/bg_BG.json | 225 - .../automations/locales/translated/bn_BD.json | 225 - .../automations/locales/translated/ca_ES.json | 225 - .../automations/locales/translated/cs_CZ.json | 225 - .../automations/locales/translated/da_DK.json | 225 - .../automations/locales/translated/de_DE.json | 225 - .../automations/locales/translated/el_GR.json | 225 - .../automations/locales/translated/es_ES.json | 225 - .../automations/locales/translated/fi_FI.json | 225 - .../automations/locales/translated/fr_FR.json | 225 - .../automations/locales/translated/he_IL.json | 225 - .../automations/locales/translated/hi_IN.json | 225 - .../automations/locales/translated/hu_HU.json | 225 - .../automations/locales/translated/id_ID.json | 225 - .../automations/locales/translated/it_IT.json | 225 - .../automations/locales/translated/ja_JP.json | 225 - .../automations/locales/translated/ko_KR.json | 225 - .../automations/locales/translated/nl_NL.json | 225 - .../automations/locales/translated/no_NO.json | 225 - .../automations/locales/translated/pl_PL.json | 225 - .../automations/locales/translated/pt_BR.json | 225 - .../automations/locales/translated/pt_PT.json | 225 - .../automations/locales/translated/ro_RO.json | 225 - .../automations/locales/translated/ru_RU.json | 225 - .../automations/locales/translated/sr_SP.json | 225 - .../automations/locales/translated/sv_SE.json | 225 - .../automations/locales/translated/th_TH.json | 225 - .../automations/locales/translated/tr_TR.json | 225 - .../automations/locales/translated/uk_UA.json | 225 - .../automations/locales/translated/vi_VN.json | 225 - .../automations/locales/translated/zh_CN.json | 225 - .../automations/locales/translated/zh_TW.json | 225 - plugins/automations/manifest.json | 139 - .../mysql/0001_adopt_automations.sql | 86 - .../migrations/mysql/0002_unlink_channels.sql | 15 - .../mysql/0003_host_maintenance.sql | 12 - .../mysql/0004_rename_maintenance_index.sql | 5 - .../postgres/0001_adopt_automations.sql | 94 - .../postgres/0002_unlink_channels.sql | 16 - .../postgres/0003_host_maintenance.sql | 12 - .../0004_rename_maintenance_index.sql | 5 - plugins/automations/migrations/snapshot.json | 332 -- .../sqlite/0001_adopt_automations.sql | 94 - .../sqlite/0002_unlink_channels.sql | 15 - .../sqlite/0003_host_maintenance.sql | 12 - .../sqlite/0004_rename_maintenance_index.sql | 5 - plugins/automations/package.json | 47 - .../src/backend/actions/host-targets.ts | 85 - .../automations/src/backend/actions/index.ts | 442 -- .../automations/src/backend/actions/types.ts | 66 - plugins/automations/src/backend/conditions.ts | 203 - plugins/automations/src/backend/cron.ts | 291 -- plugins/automations/src/backend/deps.ts | 121 - .../automations/src/backend/docker-watcher.ts | 90 - plugins/automations/src/backend/engine.ts | 501 -- .../src/backend/headless-viewer.ts | 101 - plugins/automations/src/backend/index.ts | 92 - .../src/backend/maintenance-repository.ts | 66 - .../src/backend/maintenance-routes.ts | 155 - .../src/backend/maintenance-service.ts | 152 - .../src/backend/maintenance-time.ts | 155 - plugins/automations/src/backend/repository.ts | 738 --- .../automations/src/backend/requirements.ts | 93 - plugins/automations/src/backend/routes.ts | 618 --- plugins/automations/src/backend/scheduler.ts | 166 - plugins/automations/src/backend/service.ts | 139 - plugins/automations/src/backend/tables.ts | 217 - plugins/automations/src/backend/template.ts | 101 - plugins/automations/src/backend/triggers.ts | 485 -- plugins/automations/src/backend/validate.ts | 153 - .../src/frontend/AutomationsPanel.tsx | 557 --- .../src/frontend/HostMaintenance.tsx | 343 -- .../src/frontend/automations-api.ts | 160 - .../frontend/automations/AutomationEditor.tsx | 259 -- .../automations/HostSelectorField.tsx | 137 - .../src/frontend/automations/StepBlock.tsx | 601 --- .../src/frontend/automations/TriggerCard.tsx | 445 -- .../src/frontend/automations/editor-types.ts | 72 - plugins/automations/src/frontend/index.tsx | 84 - .../src/frontend/maintenance-store.ts | 85 - plugins/automations/src/maintenance.ts | 33 - plugins/automations/src/types.ts | 286 -- .../tests/backend/activate.test.ts | 82 - .../tests/backend/conditions.test.ts | 192 - .../automations/tests/backend/cron.test.ts | 182 - .../tests/backend/engine-runtime.test.ts | 458 -- .../automations/tests/backend/engine.test.ts | 615 --- plugins/automations/tests/backend/helpers.ts | 205 - .../backend/maintenance-scheduler.test.ts | 47 - .../tests/backend/maintenance-time.test.ts | 130 - .../tests/backend/maintenance.test.ts | 224 - .../tests/backend/migrations.test.ts | 259 -- .../tests/backend/permissions.test.ts | 60 - .../automations/tests/backend/routes.test.ts | 231 - .../tests/backend/sudo-capability.test.ts | 17 - .../tests/backend/template.test.ts | 99 - .../tests/backend/triggers.test.ts | 569 --- .../tests/frontend/activate.test.tsx | 53 - .../tests/frontend/automations-editor.test.ts | 56 - .../tests/frontend/maintenance.test.tsx | 148 - .../automations/tests/frontend/panel.test.tsx | 78 - plugins/automations/tsconfig.json | 4 - plugins/automations/vitest.config.ts | 3 - plugins/docker/.commitlintrc.json | 21 - plugins/docker/.github/funding.yml | 1 - .../docker/.github/pull_request_template.md | 34 - plugins/docker/.gitignore | 9 - plugins/docker/.husky/commit-msg | 1 - plugins/docker/.husky/pre-commit | 1 - plugins/docker/.prettierignore | 4 - plugins/docker/.prettierrc | 9 - plugins/docker/CHANGELOG.json | 30 - plugins/docker/CONTRIBUTING.md | 22 - plugins/docker/README.md | 48 - plugins/docker/locales/en.json | 147 - plugins/docker/locales/translated/af_ZA.json | 147 - plugins/docker/locales/translated/ar_SA.json | 147 - plugins/docker/locales/translated/bg_BG.json | 147 - plugins/docker/locales/translated/bn_BD.json | 147 - plugins/docker/locales/translated/ca_ES.json | 147 - plugins/docker/locales/translated/cs_CZ.json | 147 - plugins/docker/locales/translated/da_DK.json | 147 - plugins/docker/locales/translated/de_DE.json | 147 - plugins/docker/locales/translated/el_GR.json | 147 - plugins/docker/locales/translated/es_ES.json | 147 - plugins/docker/locales/translated/fi_FI.json | 147 - plugins/docker/locales/translated/fr_FR.json | 147 - plugins/docker/locales/translated/he_IL.json | 147 - plugins/docker/locales/translated/hi_IN.json | 147 - plugins/docker/locales/translated/hu_HU.json | 147 - plugins/docker/locales/translated/id_ID.json | 147 - plugins/docker/locales/translated/it_IT.json | 147 - plugins/docker/locales/translated/ja_JP.json | 147 - plugins/docker/locales/translated/ko_KR.json | 147 - plugins/docker/locales/translated/nl_NL.json | 147 - plugins/docker/locales/translated/no_NO.json | 147 - plugins/docker/locales/translated/pl_PL.json | 147 - plugins/docker/locales/translated/pt_BR.json | 147 - plugins/docker/locales/translated/pt_PT.json | 147 - plugins/docker/locales/translated/ro_RO.json | 147 - plugins/docker/locales/translated/ru_RU.json | 147 - plugins/docker/locales/translated/sr_SP.json | 147 - plugins/docker/locales/translated/sv_SE.json | 147 - plugins/docker/locales/translated/th_TH.json | 147 - plugins/docker/locales/translated/tr_TR.json | 147 - plugins/docker/locales/translated/uk_UA.json | 147 - plugins/docker/locales/translated/vi_VN.json | 147 - plugins/docker/locales/translated/zh_CN.json | 147 - plugins/docker/locales/translated/zh_TW.json | 147 - plugins/docker/manifest.json | 102 - plugins/docker/package.json | 61 - plugins/docker/src/backend/connect.ts | 400 -- plugins/docker/src/backend/console.ts | 344 -- .../docker/src/backend/container-runtime.ts | 24 - plugins/docker/src/backend/container-state.ts | 84 - plugins/docker/src/backend/helpers.ts | 154 - plugins/docker/src/backend/host-settings.ts | 70 - plugins/docker/src/backend/index.ts | 33 - plugins/docker/src/backend/routes.ts | 1000 ---- plugins/docker/src/backend/services.ts | 256 -- plugins/docker/src/backend/sessions.ts | 183 - plugins/docker/src/frontend/DockerApp.tsx | 53 - plugins/docker/src/frontend/DockerManager.tsx | 714 --- plugins/docker/src/frontend/DockerWidget.tsx | 61 - .../src/frontend/DockerWidgetEditForm.tsx | 37 - .../frontend/components/ConsoleTerminal.tsx | 601 --- .../src/frontend/components/ContainerCard.tsx | 304 -- .../frontend/components/ContainerDetail.tsx | 164 - .../src/frontend/components/ContainerList.tsx | 83 - .../frontend/components/ContainerStats.tsx | 257 -- .../frontend/components/ContainerTable.tsx | 264 -- .../src/frontend/components/LogViewer.tsx | 256 -- .../src/frontend/components/console-look.ts | 63 - plugins/docker/src/frontend/docker-api.ts | 171 - plugins/docker/src/frontend/error-message.ts | 6 - plugins/docker/src/frontend/homepage.ts | 36 - plugins/docker/src/frontend/index.tsx | 69 - plugins/docker/src/frontend/types.ts | 97 - plugins/docker/tests/backend/console.test.ts | 170 - plugins/docker/tests/backend/fake-ssh.ts | 84 - .../tests/backend/host-settings.test.ts | 143 - plugins/docker/tests/backend/routes.test.ts | 316 -- plugins/docker/tests/backend/server.ts | 116 - plugins/docker/tests/backend/services.test.ts | 105 - .../tests/frontend/DockerManager.test.tsx | 139 - .../docker/tests/frontend/activate.test.tsx | 58 - .../frontend/components/console-look.test.tsx | 16 - plugins/docker/tsconfig.json | 4 - plugins/docker/vitest.config.ts | 3 - plugins/file-manager/.commitlintrc.json | 21 - plugins/file-manager/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/file-manager/.gitignore | 9 - plugins/file-manager/.husky/commit-msg | 1 - plugins/file-manager/.husky/pre-commit | 1 - plugins/file-manager/.prettierignore | 4 - plugins/file-manager/.prettierrc | 9 - plugins/file-manager/CHANGELOG.json | 34 - plugins/file-manager/CONTRIBUTING.md | 28 - plugins/file-manager/README.md | 48 - plugins/file-manager/locales/en.json | 555 --- .../locales/translated/af_ZA.json | 555 --- .../locales/translated/ar_SA.json | 555 --- .../locales/translated/bg_BG.json | 555 --- .../locales/translated/bn_BD.json | 555 --- .../locales/translated/ca_ES.json | 555 --- .../locales/translated/cs_CZ.json | 555 --- .../locales/translated/da_DK.json | 555 --- .../locales/translated/de_DE.json | 555 --- .../locales/translated/el_GR.json | 555 --- .../locales/translated/es_ES.json | 555 --- .../locales/translated/fi_FI.json | 555 --- .../locales/translated/fr_FR.json | 555 --- .../locales/translated/he_IL.json | 555 --- .../locales/translated/hi_IN.json | 555 --- .../locales/translated/hu_HU.json | 555 --- .../locales/translated/id_ID.json | 555 --- .../locales/translated/it_IT.json | 555 --- .../locales/translated/ja_JP.json | 555 --- .../locales/translated/ko_KR.json | 555 --- .../locales/translated/nl_NL.json | 555 --- .../locales/translated/no_NO.json | 555 --- .../locales/translated/pl_PL.json | 555 --- .../locales/translated/pt_BR.json | 555 --- .../locales/translated/pt_PT.json | 555 --- .../locales/translated/ro_RO.json | 555 --- .../locales/translated/ru_RU.json | 555 --- .../locales/translated/sr_SP.json | 555 --- .../locales/translated/sv_SE.json | 555 --- .../locales/translated/th_TH.json | 555 --- .../locales/translated/tr_TR.json | 555 --- .../locales/translated/uk_UA.json | 555 --- .../locales/translated/vi_VN.json | 555 --- .../locales/translated/zh_CN.json | 555 --- .../locales/translated/zh_TW.json | 555 --- plugins/file-manager/manifest.json | 135 - .../mysql/0001_adopt_file_manager_tables.sql | 49 - .../0001_adopt_file_manager_tables.sql | 53 - plugins/file-manager/migrations/snapshot.json | 161 - .../sqlite/0001_adopt_file_manager_tables.sql | 53 - plugins/file-manager/package.json | 74 - .../file-manager/src/backend/action-routes.ts | 483 -- .../src/backend/bookmark-routes.ts | 645 --- .../src/backend/content-routes.ts | 1550 ------- .../src/backend/direct-transfer-routing.ts | 72 - .../src/backend/download-routes.ts | 320 -- .../src/backend/download-stream.ts | 68 - .../file-manager/src/backend/error-message.ts | 6 - .../src/backend/home-directory.ts | 35 - .../file-manager/src/backend/host-identity.ts | 119 - plugins/file-manager/src/backend/index.ts | 2374 ---------- .../file-manager/src/backend/list-routes.ts | 476 -- .../src/backend/operation-commands.ts | 39 - .../src/backend/operation-routes.ts | 965 ---- .../file-manager/src/backend/plugin-ctx.ts | 13 - plugins/file-manager/src/backend/service.ts | 147 - plugins/file-manager/src/backend/session.ts | 282 -- .../src/backend/sftp-promisify.ts | 120 - plugins/file-manager/src/backend/ssh.ts | 79 - plugins/file-manager/src/backend/tables.ts | 105 - .../src/backend/transfer-engine.ts | 3411 -------------- .../src/backend/transfer-errors.ts | 52 - .../src/backend/transfer-host-utils.ts | 59 - .../src/backend/transfer-integrity.ts | 54 - .../src/backend/transfer-paths.ts | 151 - .../src/backend/transfer-routing.ts | 205 - .../file-manager/src/backend/transfer-scan.ts | 89 - .../src/backend/transfer-segment-copy.ts | 40 - .../src/backend/transfer-sftp-dir.ts | 74 - .../src/backend/transfer-stats.ts | 157 - .../src/backend/transfer-tuning.ts | 389 -- .../file-manager/src/backend/trash-service.ts | 255 -- plugins/file-manager/src/backend/utils.ts | 164 - .../file-manager/src/frontend/FileManager.tsx | 3775 --------------- .../src/frontend/FileManagerApp.tsx | 53 - .../src/frontend/FileManagerContextMenu.tsx | 622 --- .../src/frontend/FileManagerDialogs.tsx | 136 - .../src/frontend/FileManagerGrid.tsx | 1813 -------- .../src/frontend/FileManagerSidebar.tsx | 931 ---- .../src/frontend/FileManagerToolbar.tsx | 536 --- .../src/frontend/FileManagerTrashDialog.tsx | 214 - .../src/frontend/LocalFileContextMenu.tsx | 322 -- .../src/frontend/LocalFilePane.tsx | 1132 ----- .../src/frontend/SftpTransferTab.tsx | 1207 ----- .../src/frontend/SudoPasswordDialog.tsx | 98 - .../src/frontend/TransferMonitor.tsx | 91 - .../file-manager/src/frontend/api/client.ts | 125 - .../src/frontend/api/file-manager-data-api.ts | 156 - .../file-manager/src/frontend/api/hosts.ts | 6 - .../src/frontend/api/local-transfer-api.ts | 181 - .../frontend/api/ssh-file-operations-api.ts | 995 ---- .../src/frontend/api/transfer-api.ts | 384 -- .../src/frontend/components/AudioPreview.tsx | 109 - .../src/frontend/components/CodeEditor.tsx | 164 - .../components/ColumnResizeHandle.tsx | 43 - .../components/ColumnVisibilityMenu.tsx | 105 - .../frontend/components/CompressDialog.tsx | 170 - .../src/frontend/components/DiffViewer.tsx | 347 -- .../src/frontend/components/DiffWindow.tsx | 75 - .../components/DownloadProgressToast.tsx | 72 - .../frontend/components/DraggableWindow.tsx | 381 -- .../src/frontend/components/FileViewer.tsx | 962 ---- .../src/frontend/components/FileWindow.tsx | 605 --- .../src/frontend/components/FolderTree.tsx | 668 --- .../src/frontend/components/ImagePreview.tsx | 114 - .../components/LocalTransferProgressToast.tsx | 110 - .../frontend/components/MarkdownRenderer.tsx | 134 - .../frontend/components/PaneResizeHandle.tsx | 43 - .../src/frontend/components/PdfPreview.tsx | 148 - .../frontend/components/PermissionsDialog.tsx | 264 -- .../frontend/components/TerminalWindow.tsx | 137 - .../components/TransferProgressBar.tsx | 42 - .../components/TransferProgressToast.tsx | 150 - .../components/TransferToHostDialog.tsx | 1085 ----- .../src/frontend/components/WindowManager.tsx | 144 - .../file-manager/src/frontend/disk-info.ts | 38 - .../src/frontend/file-manager-types.ts | 37 - .../src/frontend/file-manager-utils.ts | 35 - .../src/frontend/file-preview-preload.ts | 118 - .../frontend/homepage/FileManagerWidget.tsx | 47 - .../homepage/FileManagerWidgetEditForm.tsx | 42 - .../src/frontend/homepage/homepage.ts | 36 - .../src/frontend/hooks/useDragAndDrop.ts | 178 - .../src/frontend/hooks/useDragToDesktop.ts | 294 -- .../frontend/hooks/useDragToSystemDesktop.ts | 304 -- .../src/frontend/hooks/useFileSelection.ts | 92 - .../src/frontend/hooks/useLocalTransfers.ts | 590 --- .../src/frontend/hooks/usePaneWidth.ts | 106 - .../src/frontend/hooks/useResizableColumns.ts | 239 - .../src/frontend/host-settings.ts | 14 - .../file-manager/src/frontend/host-types.ts | 59 - plugins/file-manager/src/frontend/index.tsx | 173 - .../src/frontend/lib/error-message.ts | 14 - .../lib/file-content-request-cache.ts | 38 - .../frontend/lib/file-list-request-cache.ts | 43 - .../src/frontend/lib/http-error.ts | 22 - .../src/frontend/lib/keyed-request-cache.ts | 82 - .../src/frontend/lib/local-files.ts | 105 - .../src/frontend/local-transfer-utils.ts | 407 -- .../src/frontend/optimistic-file-list.ts | 53 - .../src/frontend/sftp-transfer-utils.ts | 26 - .../src/frontend/transferMetricsFormat.ts | 92 - .../src/frontend/transferNotificationStore.ts | 52 - .../src/frontend/transferProgressMonitor.tsx | 350 -- .../tests/backend/download-routes.test.ts | 126 - .../backend/download-stream-sftp.test.ts | 74 - .../tests/backend/download-stream.test.ts | 137 - plugins/file-manager/tests/backend/helpers.ts | 91 - .../tests/backend/home-directory.test.ts | 99 - .../tests/backend/migrations.test.ts | 167 - .../tests/backend/route-paths.test.ts | 43 - .../file-manager/tests/backend/routes.test.ts | 68 - .../backend/transfer-profile-store.test.ts | 57 - .../tests/backend/trash-service.test.ts | 95 - .../tests/backend/upload-cancellation.test.ts | 141 - .../tests/backend/write-content.test.ts | 21 - .../tests/frontend/FileManagerGrid.test.tsx | 141 - .../frontend/FileManagerToolbar.test.tsx | 101 - .../tests/frontend/LocalFilePane.test.tsx | 503 -- .../tests/frontend/SftpTransferTab.test.tsx | 277 -- .../tests/frontend/activate.test.tsx | 84 - .../api/ssh-file-operations-api.test.ts | 146 - .../components/MarkdownRenderer.test.tsx | 43 - .../components/TransferProgressBar.test.tsx | 61 - .../components/download-progress.test.tsx | 180 - .../tests/frontend/disk-info.test.ts | 35 - .../tests/frontend/file-manager-utils.test.ts | 53 - .../frontend/file-preview-preload.test.ts | 18 - .../frontend/hooks/useDragAndDrop.test.ts | 92 - .../frontend/hooks/useFileSelection.test.ts | 69 - .../tests/frontend/hooks/usePaneWidth.test.ts | 50 - .../hooks/useResizableColumns.test.ts | 103 - .../lib/file-content-request-cache.test.ts | 49 - .../frontend/lib/keyed-request-cache.test.ts | 84 - .../frontend/local-transfer-utils.test.ts | 386 -- .../frontend/optimistic-file-list.test.ts | 49 - .../frontend/progress-toast-context.test.tsx | 54 - .../frontend/sftp-transfer-utils.test.ts | 27 - .../frontend/transfer-progress-toast.test.ts | 40 - plugins/file-manager/tsconfig.json | 4 - plugins/file-manager/vitest.config.ts | 3 - plugins/fleets/.commitlintrc.json | 21 - plugins/fleets/.github/funding.yml | 1 - .../fleets/.github/pull_request_template.md | 34 - plugins/fleets/.gitignore | 9 - plugins/fleets/.husky/commit-msg | 1 - plugins/fleets/.husky/pre-commit | 1 - plugins/fleets/.prettierignore | 4 - plugins/fleets/.prettierrc | 9 - plugins/fleets/CHANGELOG.json | 34 - plugins/fleets/CONTRIBUTING.md | 17 - plugins/fleets/README.md | 48 - plugins/fleets/locales/en.json | 150 - plugins/fleets/locales/translated/af_ZA.json | 150 - plugins/fleets/locales/translated/ar_SA.json | 150 - plugins/fleets/locales/translated/bg_BG.json | 150 - plugins/fleets/locales/translated/bn_BD.json | 150 - plugins/fleets/locales/translated/ca_ES.json | 150 - plugins/fleets/locales/translated/cs_CZ.json | 150 - plugins/fleets/locales/translated/da_DK.json | 150 - plugins/fleets/locales/translated/de_DE.json | 150 - plugins/fleets/locales/translated/el_GR.json | 150 - plugins/fleets/locales/translated/es_ES.json | 150 - plugins/fleets/locales/translated/fi_FI.json | 150 - plugins/fleets/locales/translated/fr_FR.json | 150 - plugins/fleets/locales/translated/he_IL.json | 150 - plugins/fleets/locales/translated/hi_IN.json | 150 - plugins/fleets/locales/translated/hu_HU.json | 150 - plugins/fleets/locales/translated/id_ID.json | 150 - plugins/fleets/locales/translated/it_IT.json | 150 - plugins/fleets/locales/translated/ja_JP.json | 150 - plugins/fleets/locales/translated/ko_KR.json | 150 - plugins/fleets/locales/translated/nl_NL.json | 150 - plugins/fleets/locales/translated/no_NO.json | 150 - plugins/fleets/locales/translated/pl_PL.json | 150 - plugins/fleets/locales/translated/pt_BR.json | 150 - plugins/fleets/locales/translated/pt_PT.json | 150 - plugins/fleets/locales/translated/ro_RO.json | 150 - plugins/fleets/locales/translated/ru_RU.json | 150 - plugins/fleets/locales/translated/sr_SP.json | 150 - plugins/fleets/locales/translated/sv_SE.json | 150 - plugins/fleets/locales/translated/th_TH.json | 150 - plugins/fleets/locales/translated/tr_TR.json | 150 - plugins/fleets/locales/translated/uk_UA.json | 150 - plugins/fleets/locales/translated/vi_VN.json | 150 - plugins/fleets/locales/translated/zh_CN.json | 150 - plugins/fleets/locales/translated/zh_TW.json | 150 - plugins/fleets/manifest.json | 77 - .../migrations/mysql/0001_adopt_fleets.sql | 42 - .../migrations/postgres/0001_adopt_fleets.sql | 46 - plugins/fleets/migrations/snapshot.json | 141 - .../migrations/sqlite/0001_adopt_fleets.sql | 46 - plugins/fleets/package.json | 49 - plugins/fleets/src/backend/index.ts | 87 - plugins/fleets/src/backend/repository.ts | 370 -- plugins/fleets/src/backend/routes.ts | 1426 ------ plugins/fleets/src/backend/tables.ts | 98 - .../fleets/src/frontend/FleetInventoryTab.tsx | 417 -- plugins/fleets/src/frontend/FleetsPanel.tsx | 1635 ------- plugins/fleets/src/frontend/fleets-api.ts | 215 - plugins/fleets/src/frontend/helpers.ts | 23 - plugins/fleets/src/frontend/index.tsx | 59 - .../tests/backend/command-variables.test.ts | 30 - plugins/fleets/tests/backend/helpers.ts | 159 - .../fleets/tests/backend/migrations.test.ts | 191 - .../fleets/tests/backend/repository.test.ts | 209 - plugins/fleets/tests/backend/routes.test.ts | 176 - plugins/fleets/tests/backend/service.test.ts | 37 - .../tests/backend/sudo-capability.test.ts | 17 - .../fleets/tests/frontend/activate.test.tsx | 53 - plugins/fleets/tsconfig.json | 4 - plugins/fleets/vitest.config.ts | 3 - plugins/homepage/.commitlintrc.json | 21 - plugins/homepage/.github/funding.yml | 1 - .../homepage/.github/pull_request_template.md | 34 - plugins/homepage/.gitignore | 9 - plugins/homepage/.husky/commit-msg | 1 - plugins/homepage/.husky/pre-commit | 1 - plugins/homepage/.prettierignore | 4 - plugins/homepage/.prettierrc | 9 - plugins/homepage/CHANGELOG.json | 30 - plugins/homepage/CONTRIBUTING.md | 22 - plugins/homepage/README.md | 47 - plugins/homepage/locales/en.json | 325 -- .../homepage/locales/translated/af_ZA.json | 325 -- .../homepage/locales/translated/ar_SA.json | 325 -- .../homepage/locales/translated/bg_BG.json | 325 -- .../homepage/locales/translated/bn_BD.json | 325 -- .../homepage/locales/translated/ca_ES.json | 325 -- .../homepage/locales/translated/cs_CZ.json | 325 -- .../homepage/locales/translated/da_DK.json | 325 -- .../homepage/locales/translated/de_DE.json | 325 -- .../homepage/locales/translated/el_GR.json | 325 -- .../homepage/locales/translated/es_ES.json | 325 -- .../homepage/locales/translated/fi_FI.json | 325 -- .../homepage/locales/translated/fr_FR.json | 325 -- .../homepage/locales/translated/he_IL.json | 325 -- .../homepage/locales/translated/hi_IN.json | 325 -- .../homepage/locales/translated/hu_HU.json | 325 -- .../homepage/locales/translated/id_ID.json | 325 -- .../homepage/locales/translated/it_IT.json | 325 -- .../homepage/locales/translated/ja_JP.json | 325 -- .../homepage/locales/translated/ko_KR.json | 325 -- .../homepage/locales/translated/nl_NL.json | 325 -- .../homepage/locales/translated/no_NO.json | 325 -- .../homepage/locales/translated/pl_PL.json | 325 -- .../homepage/locales/translated/pt_BR.json | 325 -- .../homepage/locales/translated/pt_PT.json | 325 -- .../homepage/locales/translated/ro_RO.json | 325 -- .../homepage/locales/translated/ru_RU.json | 325 -- .../homepage/locales/translated/sr_SP.json | 325 -- .../homepage/locales/translated/sv_SE.json | 325 -- .../homepage/locales/translated/th_TH.json | 325 -- .../homepage/locales/translated/tr_TR.json | 325 -- .../homepage/locales/translated/uk_UA.json | 325 -- .../homepage/locales/translated/vi_VN.json | 325 -- .../homepage/locales/translated/zh_CN.json | 325 -- .../homepage/locales/translated/zh_TW.json | 325 -- plugins/homepage/manifest.json | 84 - .../mysql/0001_adopt_homepage_tables.sql | 36 - .../postgres/0001_adopt_homepage_tables.sql | 38 - plugins/homepage/migrations/snapshot.json | 132 - .../sqlite/0001_adopt_homepage_tables.sql | 38 - plugins/homepage/package.json | 45 - plugins/homepage/src/backend/index.ts | 77 - .../homepage/src/backend/layout-item-ids.ts | 22 - .../homepage/src/backend/outbound-routes.ts | 462 -- plugins/homepage/src/backend/repository.ts | 288 -- plugins/homepage/src/backend/routes.ts | 519 --- plugins/homepage/src/backend/services.ts | 27 - plugins/homepage/src/backend/tables.ts | 68 - plugins/homepage/src/backend/url.ts | 16 - .../src/frontend/DashboardHomepageView.tsx | 62 - .../homepage/src/frontend/HomepageCanvas.tsx | 568 --- .../src/frontend/HomepagePreviewCard.tsx | 31 - .../src/frontend/ServiceLinksCard.tsx | 146 - plugins/homepage/src/frontend/api.ts | 57 - .../frontend/canvas/CanvasDotBackground.tsx | 63 - .../src/frontend/canvas/canvasGeometry.ts | 39 - .../src/frontend/canvas/snapToGrid.ts | 9 - .../src/frontend/canvas/useBoxDrag.ts | 66 - .../src/frontend/canvas/useBoxResize.ts | 81 - .../src/frontend/canvas/useCanvasInput.ts | 85 - .../homepage/src/frontend/clock-timezone.ts | 28 - .../src/frontend/dialogs/AddWidgetMenu.tsx | 164 - .../frontend/dialogs/BookmarkListEditForm.tsx | 65 - .../src/frontend/dialogs/CalendarEditForm.tsx | 38 - .../src/frontend/dialogs/ClockEditForm.tsx | 60 - .../frontend/dialogs/CountdownEditForm.tsx | 54 - .../frontend/dialogs/CustomApiEditForm.tsx | 137 - .../dialogs/DashboardLinksEditForm.tsx | 61 - .../src/frontend/dialogs/FolderEditForm.tsx | 44 - .../src/frontend/dialogs/HostGridEditForm.tsx | 82 - .../frontend/dialogs/HostStatusEditForm.tsx | 85 - .../src/frontend/dialogs/IframeEditForm.tsx | 35 - .../frontend/dialogs/ImageWidgetEditForm.tsx | 68 - .../src/frontend/dialogs/LinkTreeEditForm.tsx | 134 - .../dialogs/MarkdownNotesEditForm.tsx | 73 - .../src/frontend/dialogs/NotesEditForm.tsx | 25 - .../frontend/dialogs/PingStatusEditForm.tsx | 96 - .../frontend/dialogs/QuickConnectEditForm.tsx | 132 - .../dialogs/RecentActivityEditForm.tsx | 83 - .../src/frontend/dialogs/RssFeedEditForm.tsx | 53 - .../frontend/dialogs/SearchBarEditForm.tsx | 82 - .../frontend/dialogs/SearchLinksEditForm.tsx | 83 - .../frontend/dialogs/ServiceGridEditForm.tsx | 114 - .../frontend/dialogs/ServiceLinkEditForm.tsx | 98 - .../frontend/dialogs/SingleHostEditForm.tsx | 42 - .../frontend/dialogs/SshTerminalEditForm.tsx | 30 - .../dialogs/SystemOverviewEditForm.tsx | 46 - .../frontend/dialogs/TermixUptimeEditForm.tsx | 24 - .../frontend/dialogs/TextBannerEditForm.tsx | 142 - .../src/frontend/dialogs/WeatherEditForm.tsx | 56 - .../src/frontend/dialogs/WidgetEditDialog.tsx | 353 -- plugins/homepage/src/frontend/index.tsx | 106 - .../src/frontend/quick-connect-targets.ts | 39 - .../src/frontend/toolbar/HomepageToolbar.tsx | 127 - plugins/homepage/src/frontend/types.ts | 404 -- plugins/homepage/src/frontend/url.ts | 16 - .../src/frontend/use-page-visible-interval.ts | 67 - .../frontend/widgets/BookmarkListWidget.tsx | 74 - .../src/frontend/widgets/CalendarWidget.tsx | 161 - .../src/frontend/widgets/ClockWidget.tsx | 78 - .../src/frontend/widgets/CountdownWidget.tsx | 169 - .../src/frontend/widgets/CustomApiWidget.tsx | 200 - .../frontend/widgets/DashboardLinksWidget.tsx | 151 - .../src/frontend/widgets/FolderWidget.tsx | 46 - .../src/frontend/widgets/HostGridWidget.tsx | 123 - .../src/frontend/widgets/HostStatusWidget.tsx | 119 - .../src/frontend/widgets/IframeWidget.tsx | 55 - .../src/frontend/widgets/ImageWidget.tsx | 88 - .../src/frontend/widgets/LinkTreeWidget.tsx | 127 - .../frontend/widgets/MarkdownNotesWidget.tsx | 124 - .../src/frontend/widgets/NotesWidget.tsx | 46 - .../src/frontend/widgets/PingStatusWidget.tsx | 132 - .../frontend/widgets/QuickConnectWidget.tsx | 132 - .../frontend/widgets/RecentActivityWidget.tsx | 126 - .../src/frontend/widgets/RssFeedWidget.tsx | 154 - .../src/frontend/widgets/SearchBarWidget.tsx | 103 - .../frontend/widgets/SearchLinksWidget.tsx | 129 - .../frontend/widgets/ServiceGridWidget.tsx | 162 - .../frontend/widgets/ServiceLinkWidget.tsx | 128 - .../frontend/widgets/SshTerminalWidget.tsx | 141 - .../frontend/widgets/SystemOverviewWidget.tsx | 142 - .../frontend/widgets/TermixUptimeWidget.tsx | 143 - .../src/frontend/widgets/TextBannerWidget.tsx | 59 - .../src/frontend/widgets/WeatherWidget.tsx | 200 - .../src/frontend/widgets/WidgetRegistry.ts | 71 - .../src/frontend/widgets/WidgetShell.tsx | 169 - plugins/homepage/tests/backend/helpers.ts | 101 - .../tests/backend/layout-sync.test.ts | 84 - .../homepage/tests/backend/migrations.test.ts | 176 - .../tests/backend/outbound-routes.test.ts | 195 - plugins/homepage/tests/backend/routes.test.ts | 124 - .../homepage/tests/backend/services.test.ts | 40 - plugins/homepage/tests/backend/url.test.ts | 28 - .../tests/frontend/ClockTimezone.test.tsx | 186 - .../frontend/SystemOverviewWidget.test.tsx | 125 - .../frontend/WidgetEditDialogClock.test.tsx | 107 - .../homepage/tests/frontend/activate.test.tsx | 66 - .../frontend/canvas/canvasGeometry.test.ts | 80 - .../tests/frontend/canvas/snapToGrid.test.ts | 42 - plugins/homepage/tsconfig.json | 4 - plugins/homepage/vitest.config.ts | 3 - plugins/host-metrics/.commitlintrc.json | 21 - plugins/host-metrics/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/host-metrics/.gitignore | 9 - plugins/host-metrics/.husky/commit-msg | 1 - plugins/host-metrics/.husky/pre-commit | 1 - plugins/host-metrics/.prettierignore | 4 - plugins/host-metrics/.prettierrc | 9 - plugins/host-metrics/CHANGELOG.json | 34 - plugins/host-metrics/CONTRIBUTING.md | 34 - plugins/host-metrics/README.md | 48 - plugins/host-metrics/locales/en.json | 315 -- .../locales/translated/af_ZA.json | 315 -- .../locales/translated/ar_SA.json | 315 -- .../locales/translated/bg_BG.json | 315 -- .../locales/translated/bn_BD.json | 315 -- .../locales/translated/ca_ES.json | 315 -- .../locales/translated/cs_CZ.json | 315 -- .../locales/translated/da_DK.json | 315 -- .../locales/translated/de_DE.json | 315 -- .../locales/translated/el_GR.json | 315 -- .../locales/translated/es_ES.json | 315 -- .../locales/translated/fi_FI.json | 315 -- .../locales/translated/fr_FR.json | 315 -- .../locales/translated/he_IL.json | 315 -- .../locales/translated/hi_IN.json | 315 -- .../locales/translated/hu_HU.json | 315 -- .../locales/translated/id_ID.json | 315 -- .../locales/translated/it_IT.json | 315 -- .../locales/translated/ja_JP.json | 315 -- .../locales/translated/ko_KR.json | 315 -- .../locales/translated/nl_NL.json | 315 -- .../locales/translated/no_NO.json | 315 -- .../locales/translated/pl_PL.json | 315 -- .../locales/translated/pt_BR.json | 315 -- .../locales/translated/pt_PT.json | 315 -- .../locales/translated/ro_RO.json | 315 -- .../locales/translated/ru_RU.json | 315 -- .../locales/translated/sr_SP.json | 315 -- .../locales/translated/sv_SE.json | 315 -- .../locales/translated/th_TH.json | 315 -- .../locales/translated/tr_TR.json | 315 -- .../locales/translated/uk_UA.json | 315 -- .../locales/translated/vi_VN.json | 315 -- .../locales/translated/zh_CN.json | 315 -- .../locales/translated/zh_TW.json | 315 -- plugins/host-metrics/manifest.json | 166 - .../mysql/0001_adopt_host_metrics_tables.sql | 51 - .../0001_adopt_host_metrics_tables.sql | 55 - plugins/host-metrics/migrations/snapshot.json | 175 - .../sqlite/0001_adopt_host_metrics_tables.sql | 55 - plugins/host-metrics/package.json | 48 - plugins/host-metrics/src/backend/collect.ts | 206 - .../host-metrics/src/backend/collectors.ts | 48 - plugins/host-metrics/src/backend/helpers.ts | 50 - .../host-metrics/src/backend/host-import.ts | 61 - plugins/host-metrics/src/backend/index.ts | 123 - .../host-metrics/src/backend/interactive.ts | 412 -- plugins/host-metrics/src/backend/log.ts | 38 - .../host-metrics/src/backend/managers/cron.ts | 170 - .../src/backend/managers/firewall.ts | 192 - .../src/backend/managers/health.ts | 269 -- .../src/backend/managers/index.ts | 63 - .../host-metrics/src/backend/managers/logs.ts | 132 - .../src/backend/managers/packages.ts | 116 - .../src/backend/managers/processes.ts | 130 - .../src/backend/managers/route-helpers.ts | 60 - .../src/backend/managers/services.ts | 135 - .../src/backend/managers/simple-reads.ts | 190 - .../host-metrics/src/backend/managers/ssl.ts | 377 -- .../src/backend/managers/types.ts | 43 - .../src/backend/managers/users.ts | 193 - .../src/backend/managers/validation.ts | 127 - .../src/backend/managers/wireguard.ts | 213 - plugins/host-metrics/src/backend/poller.ts | 454 -- .../host-metrics/src/backend/repository.ts | 233 - plugins/host-metrics/src/backend/routes.ts | 686 --- plugins/host-metrics/src/backend/sessions.ts | 140 - plugins/host-metrics/src/backend/state.ts | 463 -- plugins/host-metrics/src/backend/tables.ts | 120 - plugins/host-metrics/src/backend/util.ts | 17 - .../src/backend/widgets/cpu-collector.ts | 223 - .../src/backend/widgets/disk-collector.ts | 531 --- .../src/backend/widgets/firewall-collector.ts | 254 -- .../src/backend/widgets/gpu-collector.ts | 187 - .../backend/widgets/login-stats-collector.ts | 149 - .../src/backend/widgets/memory-collector.ts | 150 - .../src/backend/widgets/network-collector.ts | 416 -- .../src/backend/widgets/ports-collector.ts | 154 - .../backend/widgets/processes-collector.ts | 68 - .../src/backend/widgets/system-collector.ts | 69 - .../backend/widgets/temperature-collector.ts | 125 - .../src/backend/widgets/uptime-collector.ts | 105 - .../src/frontend/HostEditorStatsTab.tsx | 266 -- .../src/frontend/HostMetricsApp.tsx | 51 - .../src/frontend/HostMetricsTab.tsx | 655 --- .../src/frontend/MetricsChartEditForm.tsx | 107 - .../src/frontend/MetricsChartWidget.tsx | 243 - .../src/frontend/SummaryViews.tsx | 269 -- .../src/frontend/TerminalMetricsStatus.tsx | 198 - .../src/frontend/cards/CardTimeTabs.tsx | 34 - .../src/frontend/cards/CpuCard.tsx | 128 - .../src/frontend/cards/DiskCard.tsx | 170 - .../src/frontend/cards/FilesystemPicker.tsx | 82 - .../src/frontend/cards/FirewallCard.tsx | 56 - .../src/frontend/cards/GpuCard.tsx | 210 - .../src/frontend/cards/LoginStatsCard.tsx | 62 - .../src/frontend/cards/MemoryCard.tsx | 130 - .../src/frontend/cards/NetworkCard.tsx | 207 - .../src/frontend/cards/PortsCard.tsx | 104 - .../src/frontend/cards/ProcessesCard.tsx | 53 - .../src/frontend/cards/SystemCard.tsx | 36 - .../src/frontend/cards/TemperatureCard.tsx | 100 - .../src/frontend/cards/UptimeCard.tsx | 28 - .../src/frontend/cards/gpu-history.ts | 30 - .../host-metrics/src/frontend/cards/index.tsx | 252 -- .../cards/managers/CronManagerCard.tsx | 163 - .../cards/managers/FirewallManagerCard.tsx | 231 - .../cards/managers/HealthCheckCard.tsx | 314 -- .../frontend/cards/managers/LogViewerCard.tsx | 199 - .../cards/managers/PackageManagerCard.tsx | 116 - .../cards/managers/ProcessInspectorCard.tsx | 195 - .../cards/managers/ServiceManagerCard.tsx | 155 - .../cards/managers/SimpleManagerCards.tsx | 165 - .../cards/managers/SslManagerCard.tsx | 302 -- .../cards/managers/UserManagerCard.tsx | 146 - .../cards/managers/WireGuardManagerCard.tsx | 211 - .../frontend/cards/managers/useManagerData.ts | 121 - .../hooks/useHostMetricsPreferences.ts | 92 - .../src/frontend/host-metrics-api.ts | 226 - plugins/host-metrics/src/frontend/index.tsx | 147 - .../src/frontend/metrics-change-key.ts | 23 - .../src/frontend/summary-store.ts | 115 - .../src/frontend/temperature-preference.ts | 30 - plugins/host-metrics/src/shared/homepage.ts | 43 - .../host-metrics/src/shared/host-metrics.ts | 145 - plugins/host-metrics/src/shared/metrics.ts | 136 - .../host-metrics/src/shared/stats-widgets.ts | 161 - .../tests/backend/activate.test.ts | 140 - .../tests/backend/helpers.test.ts | 79 - .../backend/host-metrics-preferences.test.ts | 94 - .../tests/backend/host-payload-legacy.test.ts | 32 - .../backend/interactive-jump-totp.test.ts | 60 - .../tests/backend/managers/managers.test.ts | 438 -- .../tests/backend/migrations.test.ts | 193 - .../host-metrics/tests/backend/poller.test.ts | 252 -- .../host-metrics/tests/backend/routes.test.ts | 229 - plugins/host-metrics/tests/backend/server.ts | 132 - .../host-metrics/tests/backend/state.test.ts | 279 -- .../backend/widgets/cpu-collector.test.ts | 45 - .../backend/widgets/disk-collector.test.ts | 348 -- .../backend/widgets/gpu-collector.test.ts | 179 - .../backend/widgets/network-collector.test.ts | 92 - .../widgets/temperature-collector.test.ts | 47 - .../frontend/HostEditorStatsTab.test.tsx | 47 - .../tests/frontend/HostMetricsTab.test.tsx | 113 - .../frontend/TerminalMetricsStatus.test.tsx | 108 - .../tests/frontend/activate.test.tsx | 65 - .../tests/frontend/cards/GpuCard.test.tsx | 120 - .../tests/frontend/cards/gpu-history.test.ts | 66 - .../tests/frontend/host-metrics-api.test.ts | 73 - .../tests/frontend/metrics-change-key.test.ts | 69 - .../tests/frontend/summary-store.test.ts | 56 - .../frontend/temperature-preference.test.ts | 28 - plugins/host-metrics/tsconfig.json | 4 - plugins/host-metrics/vitest.config.ts | 3 - plugins/ldap/.commitlintrc.json | 21 - plugins/ldap/.github/funding.yml | 1 - plugins/ldap/.github/pull_request_template.md | 34 - plugins/ldap/.gitignore | 9 - plugins/ldap/.husky/commit-msg | 1 - plugins/ldap/.husky/pre-commit | 1 - plugins/ldap/.prettierignore | 4 - plugins/ldap/.prettierrc | 9 - plugins/ldap/CHANGELOG.json | 26 - plugins/ldap/CONTRIBUTING.md | 21 - plugins/ldap/README.md | 38 - plugins/ldap/locales/en.json | 60 - plugins/ldap/locales/translated/af_ZA.json | 60 - plugins/ldap/locales/translated/ar_SA.json | 60 - plugins/ldap/locales/translated/bg_BG.json | 60 - plugins/ldap/locales/translated/bn_BD.json | 60 - plugins/ldap/locales/translated/ca_ES.json | 60 - plugins/ldap/locales/translated/cs_CZ.json | 60 - plugins/ldap/locales/translated/da_DK.json | 60 - plugins/ldap/locales/translated/de_DE.json | 60 - plugins/ldap/locales/translated/el_GR.json | 60 - plugins/ldap/locales/translated/es_ES.json | 60 - plugins/ldap/locales/translated/fi_FI.json | 60 - plugins/ldap/locales/translated/fr_FR.json | 60 - plugins/ldap/locales/translated/he_IL.json | 60 - plugins/ldap/locales/translated/hi_IN.json | 60 - plugins/ldap/locales/translated/hu_HU.json | 60 - plugins/ldap/locales/translated/id_ID.json | 60 - plugins/ldap/locales/translated/it_IT.json | 60 - plugins/ldap/locales/translated/ja_JP.json | 60 - plugins/ldap/locales/translated/ko_KR.json | 60 - plugins/ldap/locales/translated/nl_NL.json | 60 - plugins/ldap/locales/translated/no_NO.json | 60 - plugins/ldap/locales/translated/pl_PL.json | 60 - plugins/ldap/locales/translated/pt_BR.json | 60 - plugins/ldap/locales/translated/pt_PT.json | 60 - plugins/ldap/locales/translated/ro_RO.json | 60 - plugins/ldap/locales/translated/ru_RU.json | 60 - plugins/ldap/locales/translated/sr_SP.json | 60 - plugins/ldap/locales/translated/sv_SE.json | 60 - plugins/ldap/locales/translated/th_TH.json | 60 - plugins/ldap/locales/translated/tr_TR.json | 60 - plugins/ldap/locales/translated/uk_UA.json | 60 - plugins/ldap/locales/translated/vi_VN.json | 60 - plugins/ldap/locales/translated/zh_CN.json | 60 - plugins/ldap/locales/translated/zh_TW.json | 60 - plugins/ldap/manifest.json | 50 - .../mysql/0001_create_providers.sql | 12 - .../postgres/0001_create_providers.sql | 12 - plugins/ldap/migrations/snapshot.json | 43 - .../sqlite/0001_create_providers.sql | 12 - plugins/ldap/package.json | 47 - plugins/ldap/src/backend/index.ts | 34 - plugins/ldap/src/backend/ldap.ts | 208 - plugins/ldap/src/backend/providers.ts | 141 - plugins/ldap/src/backend/routes.ts | 265 -- plugins/ldap/src/backend/tables.ts | 24 - plugins/ldap/src/backend/types.ts | 34 - plugins/ldap/src/frontend/LdapLoginForms.tsx | 101 - .../ldap/src/frontend/ProvidersSetting.tsx | 432 -- plugins/ldap/src/frontend/index.tsx | 12 - plugins/ldap/src/frontend/ldap-api.ts | 35 - plugins/ldap/tests/backend/helpers.ts | 125 - plugins/ldap/tests/backend/ldap.test.ts | 330 -- plugins/ldap/tests/frontend/activate.test.tsx | 93 - plugins/ldap/tsconfig.json | 4 - plugins/ldap/vitest.config.ts | 3 - plugins/network-topology/.commitlintrc.json | 21 - plugins/network-topology/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/network-topology/.gitignore | 9 - plugins/network-topology/.husky/commit-msg | 1 - plugins/network-topology/.husky/pre-commit | 1 - plugins/network-topology/.prettierignore | 4 - plugins/network-topology/.prettierrc | 9 - plugins/network-topology/CHANGELOG.json | 30 - plugins/network-topology/CONTRIBUTING.md | 15 - plugins/network-topology/README.md | 47 - plugins/network-topology/locales/en.json | 64 - .../locales/translated/af_ZA.json | 64 - .../locales/translated/ar_SA.json | 64 - .../locales/translated/bg_BG.json | 64 - .../locales/translated/bn_BD.json | 64 - .../locales/translated/ca_ES.json | 64 - .../locales/translated/cs_CZ.json | 64 - .../locales/translated/da_DK.json | 64 - .../locales/translated/de_DE.json | 64 - .../locales/translated/el_GR.json | 64 - .../locales/translated/es_ES.json | 64 - .../locales/translated/fi_FI.json | 64 - .../locales/translated/fr_FR.json | 64 - .../locales/translated/he_IL.json | 64 - .../locales/translated/hi_IN.json | 64 - .../locales/translated/hu_HU.json | 64 - .../locales/translated/id_ID.json | 64 - .../locales/translated/it_IT.json | 64 - .../locales/translated/ja_JP.json | 64 - .../locales/translated/ko_KR.json | 64 - .../locales/translated/nl_NL.json | 64 - .../locales/translated/no_NO.json | 64 - .../locales/translated/pl_PL.json | 64 - .../locales/translated/pt_BR.json | 64 - .../locales/translated/pt_PT.json | 64 - .../locales/translated/ro_RO.json | 64 - .../locales/translated/ru_RU.json | 64 - .../locales/translated/sr_SP.json | 64 - .../locales/translated/sv_SE.json | 64 - .../locales/translated/th_TH.json | 64 - .../locales/translated/tr_TR.json | 64 - .../locales/translated/uk_UA.json | 64 - .../locales/translated/vi_VN.json | 64 - .../locales/translated/zh_CN.json | 64 - .../locales/translated/zh_TW.json | 64 - plugins/network-topology/manifest.json | 53 - .../mysql/0001_adopt_network_topology.sql | 12 - .../postgres/0001_adopt_network_topology.sql | 12 - .../network-topology/migrations/snapshot.json | 34 - .../sqlite/0001_adopt_network_topology.sql | 12 - plugins/network-topology/package.json | 45 - plugins/network-topology/src/backend/index.ts | 72 - .../src/backend/repository.ts | 58 - .../network-topology/src/backend/routes.ts | 106 - .../network-topology/src/backend/tables.ts | 27 - .../src/backend/topology-host-ids.ts | 72 - .../src/frontend/NetworkGraphCard.tsx | 1424 ------ .../network-topology/src/frontend/index.tsx | 58 - .../src/frontend/network-topology-api.ts | 55 - .../tests/backend/activate.test.ts | 81 - .../network-topology/tests/backend/helpers.ts | 91 - .../tests/backend/migrations.test.ts | 95 - .../tests/backend/repository.test.ts | 55 - .../tests/backend/routes.test.ts | 81 - .../tests/frontend/activate.test.tsx | 53 - plugins/network-topology/tsconfig.json | 4 - plugins/network-topology/vitest.config.ts | 3 - plugins/opkssh/.commitlintrc.json | 21 - plugins/opkssh/.github/funding.yml | 1 - .../opkssh/.github/pull_request_template.md | 34 - plugins/opkssh/.gitignore | 9 - plugins/opkssh/.husky/commit-msg | 1 - plugins/opkssh/.husky/pre-commit | 1 - plugins/opkssh/.prettierignore | 4 - plugins/opkssh/.prettierrc | 9 - plugins/opkssh/CHANGELOG.json | 26 - plugins/opkssh/CONTRIBUTING.md | 18 - plugins/opkssh/README.md | 38 - plugins/opkssh/locales/en.json | 35 - plugins/opkssh/locales/translated/af_ZA.json | 35 - plugins/opkssh/locales/translated/ar_SA.json | 35 - plugins/opkssh/locales/translated/bg_BG.json | 35 - plugins/opkssh/locales/translated/bn_BD.json | 35 - plugins/opkssh/locales/translated/ca_ES.json | 35 - plugins/opkssh/locales/translated/cs_CZ.json | 35 - plugins/opkssh/locales/translated/da_DK.json | 35 - plugins/opkssh/locales/translated/de_DE.json | 35 - plugins/opkssh/locales/translated/el_GR.json | 35 - plugins/opkssh/locales/translated/es_ES.json | 35 - plugins/opkssh/locales/translated/fi_FI.json | 35 - plugins/opkssh/locales/translated/fr_FR.json | 35 - plugins/opkssh/locales/translated/he_IL.json | 35 - plugins/opkssh/locales/translated/hi_IN.json | 35 - plugins/opkssh/locales/translated/hu_HU.json | 35 - plugins/opkssh/locales/translated/id_ID.json | 35 - plugins/opkssh/locales/translated/it_IT.json | 35 - plugins/opkssh/locales/translated/ja_JP.json | 35 - plugins/opkssh/locales/translated/ko_KR.json | 35 - plugins/opkssh/locales/translated/nl_NL.json | 35 - plugins/opkssh/locales/translated/no_NO.json | 35 - plugins/opkssh/locales/translated/pl_PL.json | 35 - plugins/opkssh/locales/translated/pt_BR.json | 35 - plugins/opkssh/locales/translated/pt_PT.json | 35 - plugins/opkssh/locales/translated/ro_RO.json | 35 - plugins/opkssh/locales/translated/ru_RU.json | 35 - plugins/opkssh/locales/translated/sr_SP.json | 35 - plugins/opkssh/locales/translated/sv_SE.json | 35 - plugins/opkssh/locales/translated/th_TH.json | 35 - plugins/opkssh/locales/translated/tr_TR.json | 35 - plugins/opkssh/locales/translated/uk_UA.json | 35 - plugins/opkssh/locales/translated/vi_VN.json | 35 - plugins/opkssh/locales/translated/zh_CN.json | 35 - plugins/opkssh/locales/translated/zh_TW.json | 35 - plugins/opkssh/manifest.json | 60 - .../mysql/0001_adopt_opkssh_tokens.sql | 20 - .../postgres/0001_adopt_opkssh_tokens.sql | 21 - plugins/opkssh/migrations/snapshot.json | 64 - .../sqlite/0001_adopt_opkssh_tokens.sql | 21 - plugins/opkssh/package.json | 48 - plugins/opkssh/src/backend/auth-session.ts | 447 -- plugins/opkssh/src/backend/binary.ts | 83 - plugins/opkssh/src/backend/config.ts | 161 - plugins/opkssh/src/backend/html.ts | 251 - plugins/opkssh/src/backend/index.ts | 47 - plugins/opkssh/src/backend/provider.ts | 88 - plugins/opkssh/src/backend/routes.ts | 622 --- plugins/opkssh/src/backend/tables.ts | 48 - plugins/opkssh/src/backend/token-store.ts | 142 - .../opkssh/src/frontend/OpksshAuthEditor.tsx | 28 - plugins/opkssh/src/frontend/OpksshDialog.tsx | 150 - plugins/opkssh/src/frontend/OpksshOverlay.tsx | 202 - .../src/frontend/RedirectUriSetting.tsx | 54 - plugins/opkssh/src/frontend/index.tsx | 25 - .../opkssh/tests/backend/helpers-pure.test.ts | 122 - plugins/opkssh/tests/backend/helpers.ts | 193 - .../opkssh/tests/backend/legacy-urls.test.ts | 19 - plugins/opkssh/tests/backend/opkssh.test.ts | 341 -- .../opkssh/tests/frontend/activate.test.tsx | 128 - plugins/opkssh/tsconfig.json | 4 - plugins/opkssh/vitest.config.ts | 3 - plugins/package.json | 3 - plugins/proxmox/.commitlintrc.json | 21 - plugins/proxmox/.github/funding.yml | 1 - .../proxmox/.github/pull_request_template.md | 34 - plugins/proxmox/.gitignore | 9 - plugins/proxmox/.husky/commit-msg | 1 - plugins/proxmox/.husky/pre-commit | 1 - plugins/proxmox/.prettierignore | 4 - plugins/proxmox/.prettierrc | 9 - plugins/proxmox/CHANGELOG.json | 26 - plugins/proxmox/CONTRIBUTING.md | 25 - plugins/proxmox/README.md | 38 - plugins/proxmox/locales/en.json | 121 - plugins/proxmox/locales/translated/af_ZA.json | 121 - plugins/proxmox/locales/translated/ar_SA.json | 121 - plugins/proxmox/locales/translated/bg_BG.json | 121 - plugins/proxmox/locales/translated/bn_BD.json | 121 - plugins/proxmox/locales/translated/ca_ES.json | 121 - plugins/proxmox/locales/translated/cs_CZ.json | 121 - plugins/proxmox/locales/translated/da_DK.json | 121 - plugins/proxmox/locales/translated/de_DE.json | 121 - plugins/proxmox/locales/translated/el_GR.json | 121 - plugins/proxmox/locales/translated/es_ES.json | 121 - plugins/proxmox/locales/translated/fi_FI.json | 121 - plugins/proxmox/locales/translated/fr_FR.json | 121 - plugins/proxmox/locales/translated/he_IL.json | 121 - plugins/proxmox/locales/translated/hi_IN.json | 121 - plugins/proxmox/locales/translated/hu_HU.json | 121 - plugins/proxmox/locales/translated/id_ID.json | 121 - plugins/proxmox/locales/translated/it_IT.json | 121 - plugins/proxmox/locales/translated/ja_JP.json | 121 - plugins/proxmox/locales/translated/ko_KR.json | 121 - plugins/proxmox/locales/translated/nl_NL.json | 121 - plugins/proxmox/locales/translated/no_NO.json | 121 - plugins/proxmox/locales/translated/pl_PL.json | 121 - plugins/proxmox/locales/translated/pt_BR.json | 121 - plugins/proxmox/locales/translated/pt_PT.json | 121 - plugins/proxmox/locales/translated/ro_RO.json | 121 - plugins/proxmox/locales/translated/ru_RU.json | 121 - plugins/proxmox/locales/translated/sr_SP.json | 121 - plugins/proxmox/locales/translated/sv_SE.json | 121 - plugins/proxmox/locales/translated/th_TH.json | 121 - plugins/proxmox/locales/translated/tr_TR.json | 121 - plugins/proxmox/locales/translated/uk_UA.json | 121 - plugins/proxmox/locales/translated/vi_VN.json | 121 - plugins/proxmox/locales/translated/zh_CN.json | 121 - plugins/proxmox/locales/translated/zh_TW.json | 121 - plugins/proxmox/manifest.json | 70 - .../mysql/0001_adopt_proxmox_stats.sql | 26 - .../postgres/0001_adopt_proxmox_stats.sql | 27 - plugins/proxmox/migrations/snapshot.json | 80 - .../sqlite/0001_adopt_proxmox_stats.sql | 27 - plugins/proxmox/package.json | 45 - plugins/proxmox/src/backend/guest-sync.ts | 78 - plugins/proxmox/src/backend/index.ts | 36 - plugins/proxmox/src/backend/plugin-ctx.ts | 13 - .../src/backend/proxmox-import-auth.ts | 44 - .../proxmox/src/backend/proxmox-jump-hosts.ts | 12 - .../proxmox-node-history-repository.ts | 79 - plugins/proxmox/src/backend/proxmox-shared.ts | 7 - .../src/backend/proxmox-stats-polling.ts | 334 -- .../proxmox/cluster-health-collector.ts | 67 - .../backend/proxmox/collect-proxmox-stats.ts | 86 - .../src/backend/proxmox/common-utils.ts | 98 - .../src/backend/proxmox/guests-collector.ts | 113 - .../backend/proxmox/node-network-collector.ts | 145 - .../backend/proxmox/node-status-collector.ts | 142 - .../src/backend/proxmox/storage-collector.ts | 76 - plugins/proxmox/src/backend/routes.ts | 1037 ----- plugins/proxmox/src/backend/ssh.ts | 36 - plugins/proxmox/src/backend/stats-service.ts | 366 -- plugins/proxmox/src/backend/tables.ts | 64 - .../proxmox/src/frontend/HostProxmoxTab.tsx | 263 -- .../src/frontend/ProxmoxDiscoverDialog.tsx | 434 -- plugins/proxmox/src/frontend/index.tsx | 217 - plugins/proxmox/src/frontend/proxmox-api.ts | 121 - .../src/frontend/proxmox-import-auth.ts | 36 - .../proxmox/src/frontend/stats/GuestTable.tsx | 261 -- .../frontend/stats/HostProxmoxStatsTab.tsx | 85 - .../src/frontend/stats/NodeSummaryStrip.tsx | 147 - .../src/frontend/stats/ProxmoxStatsApp.tsx | 51 - .../src/frontend/stats/ProxmoxStatsTab.tsx | 387 -- .../stats/cards/ClusterHealthCard.tsx | 67 - .../frontend/stats/cards/NodeNetworkCard.tsx | 36 - .../frontend/stats/cards/StoragePoolsCard.tsx | 60 - .../src/frontend/stats/proxmox-stats-api.ts | 80 - plugins/proxmox/src/frontend/types.ts | 153 - .../proxmox/tests/backend/guest-sync.test.ts | 61 - plugins/proxmox/tests/backend/helpers.ts | 3 - .../proxmox/tests/backend/migrations.test.ts | 99 - .../tests/backend/proxmox-import-auth.test.ts | 58 - .../tests/backend/proxmox-jump-hosts.test.ts | 14 - .../backend/proxmox-stats-polling.test.ts | 148 - .../tests/backend/proxmox-sudo.test.ts | 50 - .../proxmox/cluster-health-collector.test.ts | 66 - .../proxmox/collect-proxmox-stats.test.ts | 70 - .../backend/proxmox/guests-collector.test.ts | 144 - .../proxmox/node-network-collector.test.ts | 62 - .../proxmox/node-status-collector.test.ts | 80 - .../backend/proxmox/storage-collector.test.ts | 78 - .../proxmox-node-history-repository.test.ts | 84 - plugins/proxmox/tests/backend/routes.test.ts | 12 - .../tests/backend/sudo-capability.test.ts | 17 - .../proxmox/tests/frontend/activate.test.tsx | 72 - .../frontend/proxmox-import-auth.test.ts | 59 - .../frontend/stats/ProxmoxStatsTab.test.tsx | 45 - plugins/proxmox/tsconfig.json | 4 - plugins/proxmox/vitest.config.ts | 3 - plugins/remote-desktop/.commitlintrc.json | 21 - plugins/remote-desktop/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/remote-desktop/.gitignore | 9 - plugins/remote-desktop/.husky/commit-msg | 1 - plugins/remote-desktop/.husky/pre-commit | 1 - plugins/remote-desktop/.prettierignore | 4 - plugins/remote-desktop/.prettierrc | 9 - plugins/remote-desktop/CHANGELOG.json | 34 - plugins/remote-desktop/CONTRIBUTING.md | 29 - plugins/remote-desktop/README.md | 53 - plugins/remote-desktop/locales/en.json | 374 -- .../locales/translated/af_ZA.json | 374 -- .../locales/translated/ar_SA.json | 374 -- .../locales/translated/bg_BG.json | 374 -- .../locales/translated/bn_BD.json | 374 -- .../locales/translated/ca_ES.json | 374 -- .../locales/translated/cs_CZ.json | 374 -- .../locales/translated/da_DK.json | 374 -- .../locales/translated/de_DE.json | 374 -- .../locales/translated/el_GR.json | 374 -- .../locales/translated/es_ES.json | 374 -- .../locales/translated/fi_FI.json | 374 -- .../locales/translated/fr_FR.json | 374 -- .../locales/translated/he_IL.json | 374 -- .../locales/translated/hi_IN.json | 374 -- .../locales/translated/hu_HU.json | 374 -- .../locales/translated/id_ID.json | 374 -- .../locales/translated/it_IT.json | 374 -- .../locales/translated/ja_JP.json | 374 -- .../locales/translated/ko_KR.json | 374 -- .../locales/translated/nl_NL.json | 374 -- .../locales/translated/no_NO.json | 374 -- .../locales/translated/pl_PL.json | 374 -- .../locales/translated/pt_BR.json | 374 -- .../locales/translated/pt_PT.json | 374 -- .../locales/translated/ro_RO.json | 374 -- .../locales/translated/ru_RU.json | 374 -- .../locales/translated/sr_SP.json | 374 -- .../locales/translated/sv_SE.json | 374 -- .../locales/translated/th_TH.json | 374 -- .../locales/translated/tr_TR.json | 374 -- .../locales/translated/uk_UA.json | 374 -- .../locales/translated/vi_VN.json | 374 -- .../locales/translated/zh_CN.json | 374 -- .../locales/translated/zh_TW.json | 374 -- plugins/remote-desktop/manifest.json | 739 --- plugins/remote-desktop/package.json | 53 - .../remote-desktop/patches/guacamole-lite.cjs | 396 -- .../src/backend/drive-settings.ts | 29 - .../src/backend/guacamole-server.ts | 335 -- .../src/backend/guacd-config.ts | 86 - .../src/backend/host-settings.ts | 236 - plugins/remote-desktop/src/backend/index.ts | 122 - .../src/backend/jump-tunnel-endpoint.ts | 15 - .../remote-desktop/src/backend/jump-tunnel.ts | 145 - .../src/backend/live-sessions.ts | 65 - plugins/remote-desktop/src/backend/log.ts | 30 - .../src/backend/macos-vnc-proxy.ts | 97 - .../src/backend/rdp-settings.ts | 36 - .../src/backend/recording-settings.ts | 22 - plugins/remote-desktop/src/backend/routes.ts | 655 --- .../remote-desktop/src/backend/sessions.ts | 116 - .../src/backend/token-service.ts | 318 -- .../src/frontend/GuacamoleApp.tsx | 653 --- .../src/frontend/GuacamoleDisplay.tsx | 924 ---- .../src/frontend/GuacamoleFileBrowser.tsx | 245 - .../src/frontend/GuacamoleToolbar.tsx | 633 --- .../src/frontend/HostEditorGuacamoleTabs.tsx | 1743 ------- .../src/frontend/guacamole-api.ts | 309 -- .../src/frontend/guacamole-clipboard.ts | 39 - .../src/frontend/guacamole-common-js.d.ts | 225 - .../src/frontend/guacamole-config.ts | 64 - .../src/frontend/guacamole-display-size.ts | 48 - .../src/frontend/guacamole-file-drop.ts | 22 - .../src/frontend/guacamole-filesystem.ts | 223 - .../src/frontend/guacamole-meta-key.ts | 82 - .../src/frontend/guacamole-pointer.ts | 65 - .../src/frontend/guacamole-scroll.ts | 131 - .../src/frontend/guacamole-session-id.ts | 31 - .../src/frontend/guacamole-state.ts | 26 - .../src/frontend/guacamole-websocket-url.ts | 39 - .../src/frontend/guacamole-zoom.ts | 11 - .../src/frontend/host-remote.ts | 147 - plugins/remote-desktop/src/frontend/index.tsx | 457 -- .../src/frontend/quick-connect-guac-host.ts | 28 - .../src/frontend/rdp-credential-prompt.ts | 17 - .../src/frontend/remote-form.ts | 183 - .../tests/backend/drive-settings.test.ts | 34 - .../tests/backend/host-settings.test.ts | 118 - .../backend/jump-tunnel-endpoint.test.ts | 27 - .../tests/backend/macos-vnc-proxy.test.ts | 115 - .../tests/backend/rdp-settings.test.ts | 54 - .../tests/backend/recording-settings.test.ts | 68 - .../tests/backend/routes.test.ts | 420 -- .../tests/backend/sessions.test.ts | 126 - .../tests/backend/token-security.test.ts | 97 - .../tests/backend/token-service.test.ts | 96 - .../tests/frontend/GuacamoleToolbar.test.tsx | 152 - .../tests/frontend/activate.test.tsx | 83 - .../tests/frontend/guacamole-api.test.ts | 217 - .../frontend/guacamole-clipboard.test.ts | 63 - .../frontend/guacamole-display-size.test.ts | 67 - .../frontend/guacamole-file-drop.test.ts | 25 - .../frontend/guacamole-filesystem.test.ts | 20 - .../frontend/guacamole-key-release.test.ts | 106 - .../tests/frontend/guacamole-meta-key.test.ts | 48 - .../tests/frontend/guacamole-pointer.test.ts | 82 - .../tests/frontend/guacamole-scroll.test.ts | 81 - .../frontend/guacamole-session-id.test.ts | 46 - .../frontend/guacamole-websocket-url.test.ts | 60 - .../tests/frontend/guacamole-zoom.test.ts | 17 - .../frontend/quick-connect-guac-host.test.ts | 58 - .../frontend/rdp-credential-prompt.test.ts | 28 - .../tests/frontend/remote-form.test.ts | 91 - plugins/remote-desktop/tsconfig.json | 4 - plugins/remote-desktop/vitest.config.ts | 3 - plugins/secret-sources/.commitlintrc.json | 21 - plugins/secret-sources/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/secret-sources/.gitignore | 9 - plugins/secret-sources/.husky/commit-msg | 1 - plugins/secret-sources/.husky/pre-commit | 1 - plugins/secret-sources/.prettierignore | 4 - plugins/secret-sources/.prettierrc | 9 - plugins/secret-sources/CHANGELOG.json | 22 - plugins/secret-sources/CONTRIBUTING.md | 17 - plugins/secret-sources/README.md | 37 - plugins/secret-sources/locales/en.json | 30 - .../locales/translated/af_ZA.json | 30 - .../locales/translated/ar_SA.json | 30 - .../locales/translated/bg_BG.json | 30 - .../locales/translated/bn_BD.json | 30 - .../locales/translated/ca_ES.json | 30 - .../locales/translated/cs_CZ.json | 30 - .../locales/translated/da_DK.json | 30 - .../locales/translated/de_DE.json | 30 - .../locales/translated/el_GR.json | 30 - .../locales/translated/es_ES.json | 30 - .../locales/translated/fi_FI.json | 30 - .../locales/translated/fr_FR.json | 30 - .../locales/translated/he_IL.json | 30 - .../locales/translated/hi_IN.json | 30 - .../locales/translated/hu_HU.json | 30 - .../locales/translated/id_ID.json | 30 - .../locales/translated/it_IT.json | 30 - .../locales/translated/ja_JP.json | 30 - .../locales/translated/ko_KR.json | 30 - .../locales/translated/nl_NL.json | 30 - .../locales/translated/no_NO.json | 30 - .../locales/translated/pl_PL.json | 30 - .../locales/translated/pt_BR.json | 30 - .../locales/translated/pt_PT.json | 30 - .../locales/translated/ro_RO.json | 30 - .../locales/translated/ru_RU.json | 30 - .../locales/translated/sr_SP.json | 30 - .../locales/translated/sv_SE.json | 30 - .../locales/translated/th_TH.json | 30 - .../locales/translated/tr_TR.json | 30 - .../locales/translated/uk_UA.json | 30 - .../locales/translated/vi_VN.json | 30 - .../locales/translated/zh_CN.json | 30 - .../locales/translated/zh_TW.json | 30 - plugins/secret-sources/manifest.json | 44 - .../mysql/0001_adopt_secret_sources.sql | 15 - .../postgres/0001_adopt_secret_sources.sql | 16 - .../secret-sources/migrations/snapshot.json | 55 - .../sqlite/0001_adopt_secret_sources.sql | 16 - plugins/secret-sources/package.json | 46 - plugins/secret-sources/src/backend/egress.ts | 28 - plugins/secret-sources/src/backend/index.ts | 27 - .../src/backend/onepassword-connect.ts | 136 - .../secret-sources/src/backend/repository.ts | 116 - .../secret-sources/src/backend/resolver.ts | 61 - plugins/secret-sources/src/backend/routes.ts | 270 -- plugins/secret-sources/src/backend/tables.ts | 42 - .../secret-sources/src/backend/token-store.ts | 37 - .../src/frontend/SecretReferenceHint.tsx | 18 - .../src/frontend/SecretSourceManager.tsx | 277 -- plugins/secret-sources/src/frontend/index.tsx | 8 - .../src/frontend/secret-sources-api.ts | 51 - .../secret-sources/tests/backend/helpers.ts | 103 - .../tests/backend/onepassword-connect.test.ts | 117 - .../tests/backend/resolver.test.ts | 93 - .../tests/backend/routes.test.ts | 177 - .../tests/backend/token-store.test.ts | 46 - .../tests/frontend/activate.test.tsx | 61 - plugins/secret-sources/tsconfig.json | 4 - plugins/secret-sources/vitest.config.ts | 3 - plugins/serial/.commitlintrc.json | 21 - plugins/serial/.github/funding.yml | 1 - .../serial/.github/pull_request_template.md | 34 - plugins/serial/.gitignore | 9 - plugins/serial/.husky/commit-msg | 1 - plugins/serial/.husky/pre-commit | 1 - plugins/serial/.prettierignore | 4 - plugins/serial/.prettierrc | 9 - plugins/serial/CHANGELOG.json | 22 - plugins/serial/CONTRIBUTING.md | 11 - plugins/serial/README.md | 37 - plugins/serial/locales/en.json | 29 - plugins/serial/locales/translated/af_ZA.json | 29 - plugins/serial/locales/translated/ar_SA.json | 29 - plugins/serial/locales/translated/bg_BG.json | 29 - plugins/serial/locales/translated/bn_BD.json | 29 - plugins/serial/locales/translated/ca_ES.json | 29 - plugins/serial/locales/translated/cs_CZ.json | 29 - plugins/serial/locales/translated/da_DK.json | 29 - plugins/serial/locales/translated/de_DE.json | 29 - plugins/serial/locales/translated/el_GR.json | 29 - plugins/serial/locales/translated/es_ES.json | 29 - plugins/serial/locales/translated/fi_FI.json | 29 - plugins/serial/locales/translated/fr_FR.json | 29 - plugins/serial/locales/translated/he_IL.json | 29 - plugins/serial/locales/translated/hi_IN.json | 29 - plugins/serial/locales/translated/hu_HU.json | 29 - plugins/serial/locales/translated/id_ID.json | 29 - plugins/serial/locales/translated/it_IT.json | 29 - plugins/serial/locales/translated/ja_JP.json | 29 - plugins/serial/locales/translated/ko_KR.json | 29 - plugins/serial/locales/translated/nl_NL.json | 29 - plugins/serial/locales/translated/no_NO.json | 29 - plugins/serial/locales/translated/pl_PL.json | 29 - plugins/serial/locales/translated/pt_BR.json | 29 - plugins/serial/locales/translated/pt_PT.json | 29 - plugins/serial/locales/translated/ro_RO.json | 29 - plugins/serial/locales/translated/ru_RU.json | 29 - plugins/serial/locales/translated/sr_SP.json | 29 - plugins/serial/locales/translated/sv_SE.json | 29 - plugins/serial/locales/translated/th_TH.json | 29 - plugins/serial/locales/translated/tr_TR.json | 29 - plugins/serial/locales/translated/uk_UA.json | 29 - plugins/serial/locales/translated/vi_VN.json | 29 - plugins/serial/locales/translated/zh_CN.json | 29 - plugins/serial/locales/translated/zh_TW.json | 29 - plugins/serial/manifest.json | 40 - plugins/serial/package.json | 57 - plugins/serial/src/backend/index.ts | 12 - plugins/serial/src/backend/session.ts | 169 - plugins/serial/src/backend/ws-message.ts | 38 - plugins/serial/src/frontend/Serial.tsx | 358 -- plugins/serial/src/frontend/SerialPanel.tsx | 325 -- plugins/serial/src/frontend/electron.ts | 16 - plugins/serial/src/frontend/index.tsx | 84 - plugins/serial/src/frontend/transport.ts | 23 - plugins/serial/src/frontend/types.ts | 15 - plugins/serial/tests/backend/activate.test.ts | 37 - plugins/serial/tests/backend/session.test.ts | 187 - .../serial/tests/frontend/activate.test.tsx | 59 - plugins/serial/tsconfig.json | 4 - plugins/serial/vitest.config.ts | 3 - plugins/session-recording/.commitlintrc.json | 21 - plugins/session-recording/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/session-recording/.gitignore | 9 - plugins/session-recording/.husky/commit-msg | 1 - plugins/session-recording/.husky/pre-commit | 1 - plugins/session-recording/.prettierignore | 4 - plugins/session-recording/.prettierrc | 9 - plugins/session-recording/CHANGELOG.json | 26 - plugins/session-recording/CONTRIBUTING.md | 25 - plugins/session-recording/README.md | 46 - plugins/session-recording/locales/en.json | 63 - .../locales/translated/af_ZA.json | 63 - .../locales/translated/ar_SA.json | 63 - .../locales/translated/bg_BG.json | 63 - .../locales/translated/bn_BD.json | 63 - .../locales/translated/ca_ES.json | 63 - .../locales/translated/cs_CZ.json | 63 - .../locales/translated/da_DK.json | 63 - .../locales/translated/de_DE.json | 63 - .../locales/translated/el_GR.json | 63 - .../locales/translated/es_ES.json | 63 - .../locales/translated/fi_FI.json | 63 - .../locales/translated/fr_FR.json | 63 - .../locales/translated/he_IL.json | 63 - .../locales/translated/hi_IN.json | 63 - .../locales/translated/hu_HU.json | 63 - .../locales/translated/id_ID.json | 63 - .../locales/translated/it_IT.json | 63 - .../locales/translated/ja_JP.json | 63 - .../locales/translated/ko_KR.json | 63 - .../locales/translated/nl_NL.json | 63 - .../locales/translated/no_NO.json | 63 - .../locales/translated/pl_PL.json | 63 - .../locales/translated/pt_BR.json | 63 - .../locales/translated/pt_PT.json | 63 - .../locales/translated/ro_RO.json | 63 - .../locales/translated/ru_RU.json | 63 - .../locales/translated/sr_SP.json | 63 - .../locales/translated/sv_SE.json | 63 - .../locales/translated/th_TH.json | 63 - .../locales/translated/tr_TR.json | 63 - .../locales/translated/uk_UA.json | 63 - .../locales/translated/vi_VN.json | 63 - .../locales/translated/zh_CN.json | 63 - .../locales/translated/zh_TW.json | 63 - plugins/session-recording/manifest.json | 80 - .../mysql/0001_adopt_session_recordings.sql | 22 - .../0001_adopt_session_recordings.sql | 24 - .../migrations/snapshot.json | 76 - .../sqlite/0001_adopt_session_recordings.sql | 24 - plugins/session-recording/package.json | 52 - .../session-recording/src/backend/files.ts | 39 - .../session-recording/src/backend/index.ts | 42 - .../src/backend/repository.ts | 240 - .../src/backend/retention.ts | 64 - .../session-recording/src/backend/routes.ts | 253 -- .../session-recording/src/backend/service.ts | 133 - .../session-recording/src/backend/tables.ts | 52 - .../src/frontend/RecordingStatus.tsx | 21 - .../src/frontend/SessionLogsPanel.tsx | 632 --- .../src/frontend/SessionRecordingPlayer.tsx | 375 -- .../src/frontend/asciicast.ts | 78 - .../session-recording/src/frontend/index.tsx | 42 - .../src/frontend/session-recording-api.ts | 44 - .../tests/backend/helpers.ts | 128 - .../tests/backend/migrations.test.ts | 141 - .../tests/backend/repository.test.ts | 149 - .../tests/backend/routes.test.ts | 179 - .../tests/backend/service.test.ts | 267 -- .../tests/frontend/activate.test.tsx | 71 - .../tests/frontend/asciicast.test.ts | 20 - plugins/session-recording/tsconfig.json | 4 - plugins/session-recording/vitest.config.ts | 3 - plugins/session-sharing/.commitlintrc.json | 21 - plugins/session-sharing/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/session-sharing/.gitignore | 9 - plugins/session-sharing/.husky/commit-msg | 1 - plugins/session-sharing/.husky/pre-commit | 1 - plugins/session-sharing/.prettierignore | 4 - plugins/session-sharing/.prettierrc | 9 - plugins/session-sharing/CHANGELOG.json | 34 - plugins/session-sharing/CONTRIBUTING.md | 25 - plugins/session-sharing/README.md | 52 - plugins/session-sharing/locales/en.json | 189 - .../locales/translated/af_ZA.json | 189 - .../locales/translated/ar_SA.json | 189 - .../locales/translated/bg_BG.json | 189 - .../locales/translated/bn_BD.json | 189 - .../locales/translated/ca_ES.json | 189 - .../locales/translated/cs_CZ.json | 189 - .../locales/translated/da_DK.json | 189 - .../locales/translated/de_DE.json | 189 - .../locales/translated/el_GR.json | 189 - .../locales/translated/es_ES.json | 189 - .../locales/translated/fi_FI.json | 189 - .../locales/translated/fr_FR.json | 189 - .../locales/translated/he_IL.json | 189 - .../locales/translated/hi_IN.json | 189 - .../locales/translated/hu_HU.json | 189 - .../locales/translated/id_ID.json | 189 - .../locales/translated/it_IT.json | 189 - .../locales/translated/ja_JP.json | 189 - .../locales/translated/ko_KR.json | 189 - .../locales/translated/nl_NL.json | 189 - .../locales/translated/no_NO.json | 189 - .../locales/translated/pl_PL.json | 189 - .../locales/translated/pt_BR.json | 189 - .../locales/translated/pt_PT.json | 189 - .../locales/translated/ro_RO.json | 189 - .../locales/translated/ru_RU.json | 189 - .../locales/translated/sr_SP.json | 189 - .../locales/translated/sv_SE.json | 189 - .../locales/translated/th_TH.json | 189 - .../locales/translated/tr_TR.json | 189 - .../locales/translated/uk_UA.json | 189 - .../locales/translated/vi_VN.json | 189 - .../locales/translated/zh_CN.json | 189 - .../locales/translated/zh_TW.json | 189 - plugins/session-sharing/manifest.json | 111 - .../mysql/0001_adopt_session_sharing.sql | 67 - .../postgres/0001_adopt_session_sharing.sql | 73 - .../session-sharing/migrations/snapshot.json | 223 - .../sqlite/0001_adopt_session_sharing.sql | 73 - plugins/session-sharing/package.json | 61 - plugins/session-sharing/src/backend/deps.ts | 51 - plugins/session-sharing/src/backend/index.ts | 80 - plugins/session-sharing/src/backend/live.ts | 205 - .../session-sharing/src/backend/rate-limit.ts | 37 - .../src/backend/repositories.ts | 591 --- .../session-sharing/src/backend/room-hub.ts | 118 - .../src/backend/room-routes.ts | 1487 ------ .../src/backend/runtime-store.ts | 289 -- .../session-sharing/src/backend/services.ts | 162 - .../src/backend/share-routes.ts | 523 --- plugins/session-sharing/src/backend/tables.ts | 128 - .../src/frontend/CollabGuestView.tsx | 105 - .../src/frontend/CollabMembersSidebar.tsx | 325 -- .../src/frontend/CollabPanel.tsx | 186 - .../src/frontend/CollabRoomTab.tsx | 1051 ----- .../src/frontend/ParticipantsOverlay.tsx | 41 - .../src/frontend/ShareSessionModal.tsx | 450 -- .../src/frontend/SharedSessionView.tsx | 374 -- .../src/frontend/SharedWithMeSection.tsx | 160 - plugins/session-sharing/src/frontend/api.ts | 286 -- .../session-sharing/src/frontend/index.tsx | 369 -- .../src/frontend/meeting-api.ts | 216 - .../src/frontend/meeting-backend.ts | 63 - .../src/frontend/share-dialog.tsx | 69 - .../session-sharing/src/frontend/shared.tsx | 102 - .../tests/backend/activate.test.ts | 69 - .../session-sharing/tests/backend/helpers.ts | 196 - .../tests/backend/migrations.test.ts | 222 - .../tests/backend/rooms.test.ts | 324 -- .../tests/backend/security.test.ts | 425 -- .../tests/backend/shares.test.ts | 225 - .../tests/frontend/SharedSessionView.test.tsx | 113 - .../frontend/SharedWithMeSection.test.tsx | 103 - .../tests/frontend/activate.test.tsx | 233 - .../frontend/guest-terminal-size.test.tsx | 110 - .../tests/frontend/meeting-backend.test.ts | 80 - .../tests/frontend/meeting-transport.test.ts | 29 - .../tests/frontend/presenter-takeover.test.ts | 105 - .../tests/frontend/share-link-url.test.ts | 38 - plugins/session-sharing/tsconfig.json | 4 - plugins/session-sharing/vitest.config.ts | 3 - plugins/snippets/.commitlintrc.json | 21 - plugins/snippets/.github/funding.yml | 1 - .../snippets/.github/pull_request_template.md | 34 - plugins/snippets/.gitignore | 9 - plugins/snippets/.husky/commit-msg | 1 - plugins/snippets/.husky/pre-commit | 1 - plugins/snippets/.prettierignore | 4 - plugins/snippets/.prettierrc | 9 - plugins/snippets/CHANGELOG.json | 34 - plugins/snippets/CONTRIBUTING.md | 27 - plugins/snippets/README.md | 48 - plugins/snippets/locales/en.json | 238 - .../snippets/locales/translated/af_ZA.json | 238 - .../snippets/locales/translated/ar_SA.json | 238 - .../snippets/locales/translated/bg_BG.json | 238 - .../snippets/locales/translated/bn_BD.json | 238 - .../snippets/locales/translated/ca_ES.json | 238 - .../snippets/locales/translated/cs_CZ.json | 238 - .../snippets/locales/translated/da_DK.json | 238 - .../snippets/locales/translated/de_DE.json | 238 - .../snippets/locales/translated/el_GR.json | 238 - .../snippets/locales/translated/es_ES.json | 238 - .../snippets/locales/translated/fi_FI.json | 238 - .../snippets/locales/translated/fr_FR.json | 238 - .../snippets/locales/translated/he_IL.json | 238 - .../snippets/locales/translated/hi_IN.json | 238 - .../snippets/locales/translated/hu_HU.json | 238 - .../snippets/locales/translated/id_ID.json | 238 - .../snippets/locales/translated/it_IT.json | 238 - .../snippets/locales/translated/ja_JP.json | 238 - .../snippets/locales/translated/ko_KR.json | 238 - .../snippets/locales/translated/nl_NL.json | 238 - .../snippets/locales/translated/no_NO.json | 238 - .../snippets/locales/translated/pl_PL.json | 238 - .../snippets/locales/translated/pt_BR.json | 238 - .../snippets/locales/translated/pt_PT.json | 238 - .../snippets/locales/translated/ro_RO.json | 238 - .../snippets/locales/translated/ru_RU.json | 238 - .../snippets/locales/translated/sr_SP.json | 238 - .../snippets/locales/translated/sv_SE.json | 238 - .../snippets/locales/translated/th_TH.json | 238 - .../snippets/locales/translated/tr_TR.json | 238 - .../snippets/locales/translated/uk_UA.json | 238 - .../snippets/locales/translated/vi_VN.json | 238 - .../snippets/locales/translated/zh_CN.json | 238 - .../snippets/locales/translated/zh_TW.json | 238 - plugins/snippets/manifest.json | 154 - .../mysql/0001_adopt_snippets_tables.sql | 43 - .../postgres/0001_adopt_snippets_tables.sql | 47 - plugins/snippets/migrations/snapshot.json | 162 - .../sqlite/0001_adopt_snippets_tables.sql | 47 - plugins/snippets/package.json | 47 - plugins/snippets/src/backend/execution.ts | 32 - plugins/snippets/src/backend/host-import.ts | 117 - plugins/snippets/src/backend/index.ts | 191 - plugins/snippets/src/backend/reorder.ts | 29 - plugins/snippets/src/backend/repository.ts | 917 ---- plugins/snippets/src/backend/routes.ts | 1260 ------ plugins/snippets/src/backend/tables.ts | 73 - .../src/frontend/AdminUserSnippets.tsx | 237 - .../src/frontend/ExecutionResultsDialog.tsx | 85 - .../src/frontend/HostSnippetsSection.tsx | 187 - .../src/frontend/QuickActionButtons.tsx | 147 - .../snippets/src/frontend/SnippetEditor.tsx | 288 -- .../src/frontend/SnippetFolderDialog.tsx | 141 - .../snippets/src/frontend/SnippetOverlay.tsx | 32 - .../snippets/src/frontend/SnippetSettings.tsx | 77 - .../src/frontend/SnippetShareView.tsx | 429 -- plugins/snippets/src/frontend/SnippetTree.tsx | 470 -- .../src/frontend/SnippetVariablesDialog.tsx | 107 - .../snippets/src/frontend/SnippetsPanel.tsx | 831 ---- .../snippets/src/frontend/TargetTerminals.tsx | 148 - .../snippets/src/frontend/folder-icons.tsx | 41 - plugins/snippets/src/frontend/index.tsx | 182 - plugins/snippets/src/frontend/keybinding.tsx | 89 - plugins/snippets/src/frontend/prompt-store.ts | 72 - plugins/snippets/src/frontend/run-flows.ts | 121 - plugins/snippets/src/frontend/settings.ts | 15 - plugins/snippets/src/frontend/snippets-api.ts | 140 - plugins/snippets/src/frontend/types.ts | 84 - .../src/frontend/use-snippet-options.ts | 53 - .../src/frontend/use-snippet-runner.tsx | 283 -- plugins/snippets/src/shared/host-settings.ts | 48 - plugins/snippets/src/shared/variables.ts | 81 - .../snippets/tests/backend/activate.test.ts | 155 - .../snippets/tests/backend/execute.test.ts | 129 - plugins/snippets/tests/backend/helpers.ts | 98 - .../tests/backend/host-import.test.ts | 79 - .../snippets/tests/backend/migrations.test.ts | 186 - .../snippets/tests/backend/repository.test.ts | 186 - plugins/snippets/tests/backend/routes.test.ts | 235 - .../tests/backend/share-targets.test.ts | 43 - .../snippets/tests/backend/variables.test.ts | 79 - .../snippets/tests/frontend/activate.test.tsx | 235 - .../frontend/admin-user-snippets.test.tsx | 86 - .../snippets/tests/frontend/settings.test.ts | 26 - .../tests/frontend/snippets-panel.test.tsx | 246 - plugins/snippets/tests/frontend/types.test.ts | 16 - plugins/snippets/tsconfig.json | 4 - plugins/snippets/vitest.config.ts | 3 - plugins/ssh-terminal/.commitlintrc.json | 21 - plugins/ssh-terminal/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/ssh-terminal/.gitignore | 9 - plugins/ssh-terminal/.husky/commit-msg | 1 - plugins/ssh-terminal/.husky/pre-commit | 1 - plugins/ssh-terminal/.prettierignore | 4 - plugins/ssh-terminal/.prettierrc | 9 - plugins/ssh-terminal/CHANGELOG.json | 50 - plugins/ssh-terminal/CONTRIBUTING.md | 37 - plugins/ssh-terminal/README.md | 59 - plugins/ssh-terminal/locales/en.json | 708 --- .../locales/translated/af_ZA.json | 708 --- .../locales/translated/ar_SA.json | 708 --- .../locales/translated/bg_BG.json | 708 --- .../locales/translated/bn_BD.json | 708 --- .../locales/translated/ca_ES.json | 708 --- .../locales/translated/cs_CZ.json | 708 --- .../locales/translated/da_DK.json | 708 --- .../locales/translated/de_DE.json | 708 --- .../locales/translated/el_GR.json | 708 --- .../locales/translated/es_ES.json | 708 --- .../locales/translated/fi_FI.json | 708 --- .../locales/translated/fr_FR.json | 708 --- .../locales/translated/he_IL.json | 708 --- .../locales/translated/hi_IN.json | 708 --- .../locales/translated/hu_HU.json | 708 --- .../locales/translated/id_ID.json | 708 --- .../locales/translated/it_IT.json | 708 --- .../locales/translated/ja_JP.json | 708 --- .../locales/translated/ko_KR.json | 708 --- .../locales/translated/nl_NL.json | 708 --- .../locales/translated/no_NO.json | 708 --- .../locales/translated/pl_PL.json | 708 --- .../locales/translated/pt_BR.json | 708 --- .../locales/translated/pt_PT.json | 708 --- .../locales/translated/ro_RO.json | 708 --- .../locales/translated/ru_RU.json | 708 --- .../locales/translated/sr_SP.json | 708 --- .../locales/translated/sv_SE.json | 708 --- .../locales/translated/th_TH.json | 708 --- .../locales/translated/tr_TR.json | 708 --- .../locales/translated/uk_UA.json | 708 --- .../locales/translated/vi_VN.json | 708 --- .../locales/translated/zh_CN.json | 708 --- .../locales/translated/zh_TW.json | 708 --- plugins/ssh-terminal/manifest.json | 832 ---- .../mysql/0001_adopt_command_history.sql | 13 - .../postgres/0001_adopt_command_history.sql | 14 - plugins/ssh-terminal/migrations/snapshot.json | 39 - .../sqlite/0001_adopt_command_history.sql | 14 - plugins/ssh-terminal/package.json | 70 - plugins/ssh-terminal/src/backend/helpers.ts | 295 -- .../src/backend/history-repository.ts | 91 - .../ssh-terminal/src/backend/host-import.ts | 50 - .../backend/images/image-storage-settings.ts | 262 -- .../src/backend/images/image-storage.ts | 675 --- .../src/backend/images/image-utils.ts | 100 - plugins/ssh-terminal/src/backend/index.ts | 187 - .../src/backend/keyboard-prompt.ts | 310 -- plugins/ssh-terminal/src/backend/routes.ts | 895 ---- plugins/ssh-terminal/src/backend/services.ts | 159 - .../src/backend/session-manager.ts | 993 ---- .../src/backend/settings-validation.ts | 67 - plugins/ssh-terminal/src/backend/settings.ts | 113 - plugins/ssh-terminal/src/backend/tables.ts | 39 - .../src/backend/terminal-socket.ts | 2971 ------------ .../src/frontend/TerminalTabWithRegistry.tsx | 65 - .../src/frontend/TerminalView.tsx | 38 - .../src/frontend/history/HistoryPanel.tsx | 167 - plugins/ssh-terminal/src/frontend/index.tsx | 345 -- .../src/frontend/lib/keybinding-dispatch.ts | 149 - .../src/frontend/lib/keybinding-editors.tsx | 81 - .../src/frontend/lib/mac-line-navigation.ts | 15 - .../src/frontend/lib/terminal-local-echo.ts | 104 - .../lib/terminal-syntax-highlighter.ts | 507 --- .../frontend/local-terminal/LocalTerminal.tsx | 203 - .../src/frontend/look/TerminalPreview.tsx | 129 - .../src/frontend/look/look-actions.ts | 83 - .../src/frontend/look/terminal-font-zoom.ts | 37 - .../frontend/look/terminal-global-styles.ts | 107 - .../src/frontend/look/terminal-theme.ts | 55 - .../src/frontend/look/terminal-themes.ts | 831 ---- .../src/frontend/look/theme-preview.ts | 30 - .../src/frontend/macros/MacrosPanel.tsx | 864 ---- .../src/frontend/macros/terminal-macros.ts | 341 -- .../src/frontend/session-registry.ts | 88 - .../frontend/settings/HostTerminalSection.tsx | 23 - .../settings/HostTerminalSettings.tsx | 866 ---- .../frontend/settings/ImageStorageTest.tsx | 100 - .../frontend/settings/TouchInputSettings.tsx | 156 - .../settings/local-preferences-migration.ts | 89 - .../src/frontend/ssh-tools/SshToolsPanel.tsx | 387 -- .../ssh-terminal/src/frontend/terminal-api.ts | 82 - .../src/frontend/terminal-settings.ts | 130 - .../terminal/MobileTerminalKeyboard.tsx | 525 --- .../src/frontend/terminal/Terminal.tsx | 4027 ----------------- .../src/frontend/terminal/TerminalApp.tsx | 63 - .../frontend/terminal/TerminalTabContent.tsx | 142 - .../src/frontend/terminal/TerminalToolbar.tsx | 807 ---- .../frontend/terminal/TmuxSessionPicker.tsx | 129 - .../terminal/android-hardware-keyboard.ts | 30 - .../command-history/CommandAutocomplete.tsx | 116 - .../command-history/CommandHistoryContext.tsx | 86 - .../command-history/useCommandTracker.ts | 132 - .../frontend/terminal/font-size-storage.ts | 40 - .../terminal/search/TerminalSearchBar.tsx | 249 - .../frontend/terminal/terminal-buffer-text.ts | 47 - .../terminal/terminal-clipboard-actions.ts | 26 - .../frontend/terminal/terminal-clipboard.ts | 152 - .../src/frontend/terminal/terminal-helpers.ts | 6 - .../frontend/terminal/terminal-image-path.ts | 10 - .../terminal/terminal-image-upload.ts | 15 - .../frontend/terminal/terminal-key-event.ts | 13 - .../src/frontend/terminal/terminal-slots.ts | 90 - .../src/frontend/terminal/terminal-types.ts | 37 - .../src/frontend/terminal/toolbar-geometry.ts | 128 - .../src/frontend/terminal/toolbar-settings.ts | 82 - .../terminal/touch-input-settings-store.ts | 31 - .../terminal/touch-wheel-coordinator.ts | 335 -- plugins/ssh-terminal/src/frontend/types.ts | 33 - .../src/shared/terminal-settings.ts | 399 -- .../src/shared/touch-input-settings.ts | 127 - .../tests/backend/activate.test.ts | 110 - .../tests/backend/guest-socket.test.ts | 126 - plugins/ssh-terminal/tests/backend/helpers.ts | 98 - .../tests/backend/host-import.test.ts | 70 - .../images/image-storage-settings.test.ts | 191 - .../backend/images/image-storage.test.ts | 571 --- .../tests/backend/images/image-utils.test.ts | 76 - .../tests/backend/keyboard-prompt.test.ts | 298 -- .../tests/backend/memory-agent.test.ts | 105 - .../tests/backend/migrations.test.ts | 110 - .../ssh-terminal/tests/backend/routes.test.ts | 203 - .../tests/backend/session-manager.test.ts | 584 --- .../tests/backend/settings-validation.test.ts | 49 - .../tests/backend/ssh-dns.test.ts | 22 - .../tests/backend/terminal-settings.test.ts | 77 - .../tests/frontend/activate.test.tsx | 197 - .../frontend/lib/keybinding-dispatch.test.ts | 127 - .../frontend/lib/mac-line-navigation.test.ts | 50 - .../frontend/lib/terminal-local-echo.test.ts | 101 - .../lib/terminal-local-echo.xterm.test.ts | 53 - .../lib/terminal-syntax-highlighter.test.ts | 441 -- .../frontend/look/terminal-font-zoom.test.ts | 74 - .../look/terminal-global-styles.test.ts | 89 - .../frontend/look/terminal-theme.test.ts | 21 - .../frontend/macros/terminal-macros.test.ts | 232 - .../settings/HostTerminalSettings.test.tsx | 83 - .../settings/TouchInputSettings.test.tsx | 52 - .../local-preferences-migration.test.ts | 62 - .../frontend/ssh-tools/SshToolsPanel.test.tsx | 67 - .../terminal/TerminalTabContent.test.tsx | 66 - .../terminal/TerminalToolbar.test.tsx | 1083 ----- .../android-hardware-keyboard.test.ts | 50 - .../frontend/terminal/android-ime.test.ts | 76 - .../frontend/terminal/auto-reconnect.test.ts | 64 - .../terminal/font-size-storage.test.ts | 32 - .../terminal/ios-rapid-typing.test.ts | 60 - .../terminal/reconnect-disconnected.test.ts | 72 - .../terminal/shared-terminal-size.test.ts | 110 - .../terminal/terminal-buffer-text.test.ts | 99 - .../terminal-clipboard-actions.test.ts | 47 - .../terminal/terminal-clipboard.test.ts | 332 -- .../terminal/terminal-image-path.test.ts | 20 - .../terminal-image-upload-metadata.test.ts | 40 - .../terminal/terminal-key-event.test.ts | 47 - .../terminal/terminal-unmount.test.ts | 76 - .../terminal/toolbar-settings.test.ts | 74 - .../terminal/touch-wheel-coordinator.test.ts | 417 -- .../touch-wheel-coordinator.xterm.test.ts | 140 - plugins/ssh-terminal/tsconfig.json | 4 - plugins/ssh-terminal/vitest.config.ts | 3 - plugins/sso/.commitlintrc.json | 21 - plugins/sso/.github/funding.yml | 1 - plugins/sso/.github/pull_request_template.md | 34 - plugins/sso/.gitignore | 9 - plugins/sso/.husky/commit-msg | 1 - plugins/sso/.husky/pre-commit | 1 - plugins/sso/.prettierignore | 4 - plugins/sso/.prettierrc | 9 - plugins/sso/CHANGELOG.json | 30 - plugins/sso/CONTRIBUTING.md | 22 - plugins/sso/README.md | 39 - plugins/sso/locales/en.json | 74 - plugins/sso/locales/translated/af_ZA.json | 74 - plugins/sso/locales/translated/ar_SA.json | 74 - plugins/sso/locales/translated/bg_BG.json | 74 - plugins/sso/locales/translated/bn_BD.json | 74 - plugins/sso/locales/translated/ca_ES.json | 74 - plugins/sso/locales/translated/cs_CZ.json | 74 - plugins/sso/locales/translated/da_DK.json | 74 - plugins/sso/locales/translated/de_DE.json | 74 - plugins/sso/locales/translated/el_GR.json | 74 - plugins/sso/locales/translated/es_ES.json | 74 - plugins/sso/locales/translated/fi_FI.json | 74 - plugins/sso/locales/translated/fr_FR.json | 74 - plugins/sso/locales/translated/he_IL.json | 74 - plugins/sso/locales/translated/hi_IN.json | 74 - plugins/sso/locales/translated/hu_HU.json | 74 - plugins/sso/locales/translated/id_ID.json | 74 - plugins/sso/locales/translated/it_IT.json | 74 - plugins/sso/locales/translated/ja_JP.json | 74 - plugins/sso/locales/translated/ko_KR.json | 74 - plugins/sso/locales/translated/nl_NL.json | 74 - plugins/sso/locales/translated/no_NO.json | 74 - plugins/sso/locales/translated/pl_PL.json | 74 - plugins/sso/locales/translated/pt_BR.json | 74 - plugins/sso/locales/translated/pt_PT.json | 74 - plugins/sso/locales/translated/ro_RO.json | 74 - plugins/sso/locales/translated/ru_RU.json | 74 - plugins/sso/locales/translated/sr_SP.json | 74 - plugins/sso/locales/translated/sv_SE.json | 74 - plugins/sso/locales/translated/th_TH.json | 74 - plugins/sso/locales/translated/tr_TR.json | 74 - plugins/sso/locales/translated/uk_UA.json | 74 - plugins/sso/locales/translated/vi_VN.json | 74 - plugins/sso/locales/translated/zh_CN.json | 74 - plugins/sso/locales/translated/zh_TW.json | 74 - plugins/sso/manifest.json | 77 - .../mysql/0001_adopt_sso_providers.sql | 14 - .../migrations/mysql/0002_legacy_callback.sql | 7 - .../postgres/0001_adopt_sso_providers.sql | 14 - .../postgres/0002_legacy_callback.sql | 7 - plugins/sso/migrations/snapshot.json | 53 - .../sqlite/0001_adopt_sso_providers.sql | 14 - .../sqlite/0002_legacy_callback.sql | 7 - plugins/sso/package.json | 48 - plugins/sso/src/backend/index.ts | 52 - plugins/sso/src/backend/login.ts | 486 -- plugins/sso/src/backend/oidc-protocol.ts | 463 -- plugins/sso/src/backend/providers.ts | 278 -- plugins/sso/src/backend/routes.ts | 502 -- plugins/sso/src/backend/tables.ts | 32 - plugins/sso/src/backend/types.ts | 46 - plugins/sso/src/frontend/OidcLoginButtons.tsx | 39 - plugins/sso/src/frontend/ProvidersSetting.tsx | 568 --- plugins/sso/src/frontend/index.tsx | 12 - plugins/sso/src/frontend/sso-api.ts | 45 - plugins/sso/tests/backend/helpers.ts | 222 - plugins/sso/tests/backend/legacy-urls.test.ts | 35 - .../sso/tests/backend/oidc-protocol.test.ts | 533 --- plugins/sso/tests/backend/sso.test.ts | 462 -- plugins/sso/tests/frontend/activate.test.tsx | 112 - plugins/sso/tsconfig.json | 4 - plugins/sso/vitest.config.ts | 3 - plugins/step-ca/.commitlintrc.json | 21 - plugins/step-ca/.github/funding.yml | 1 - .../step-ca/.github/pull_request_template.md | 34 - plugins/step-ca/.gitignore | 9 - plugins/step-ca/.husky/commit-msg | 1 - plugins/step-ca/.husky/pre-commit | 1 - plugins/step-ca/.prettierignore | 4 - plugins/step-ca/.prettierrc | 9 - plugins/step-ca/CHANGELOG.json | 26 - plugins/step-ca/CONTRIBUTING.md | 20 - plugins/step-ca/README.md | 38 - plugins/step-ca/locales/en.json | 51 - plugins/step-ca/locales/translated/af_ZA.json | 51 - plugins/step-ca/locales/translated/ar_SA.json | 51 - plugins/step-ca/locales/translated/bg_BG.json | 51 - plugins/step-ca/locales/translated/bn_BD.json | 51 - plugins/step-ca/locales/translated/ca_ES.json | 51 - plugins/step-ca/locales/translated/cs_CZ.json | 51 - plugins/step-ca/locales/translated/da_DK.json | 51 - plugins/step-ca/locales/translated/de_DE.json | 51 - plugins/step-ca/locales/translated/el_GR.json | 51 - plugins/step-ca/locales/translated/es_ES.json | 51 - plugins/step-ca/locales/translated/fi_FI.json | 51 - plugins/step-ca/locales/translated/fr_FR.json | 51 - plugins/step-ca/locales/translated/he_IL.json | 51 - plugins/step-ca/locales/translated/hi_IN.json | 51 - plugins/step-ca/locales/translated/hu_HU.json | 51 - plugins/step-ca/locales/translated/id_ID.json | 51 - plugins/step-ca/locales/translated/it_IT.json | 51 - plugins/step-ca/locales/translated/ja_JP.json | 51 - plugins/step-ca/locales/translated/ko_KR.json | 51 - plugins/step-ca/locales/translated/nl_NL.json | 51 - plugins/step-ca/locales/translated/no_NO.json | 51 - plugins/step-ca/locales/translated/pl_PL.json | 51 - plugins/step-ca/locales/translated/pt_BR.json | 51 - plugins/step-ca/locales/translated/pt_PT.json | 51 - plugins/step-ca/locales/translated/ro_RO.json | 51 - plugins/step-ca/locales/translated/ru_RU.json | 51 - plugins/step-ca/locales/translated/sr_SP.json | 51 - plugins/step-ca/locales/translated/sv_SE.json | 51 - plugins/step-ca/locales/translated/th_TH.json | 51 - plugins/step-ca/locales/translated/tr_TR.json | 51 - plugins/step-ca/locales/translated/uk_UA.json | 51 - plugins/step-ca/locales/translated/vi_VN.json | 51 - plugins/step-ca/locales/translated/zh_CN.json | 51 - plugins/step-ca/locales/translated/zh_TW.json | 51 - plugins/step-ca/manifest.json | 86 - .../migrations/mysql/0001_create_certs.sql | 16 - .../migrations/postgres/0001_create_certs.sql | 16 - plugins/step-ca/migrations/snapshot.json | 51 - .../migrations/sqlite/0001_create_certs.sql | 16 - plugins/step-ca/package.json | 47 - plugins/step-ca/src/backend/auth-session.ts | 378 -- plugins/step-ca/src/backend/cert-store.ts | 96 - plugins/step-ca/src/backend/client.ts | 417 -- plugins/step-ca/src/backend/index.ts | 30 - plugins/step-ca/src/backend/provider.ts | 88 - plugins/step-ca/src/backend/routes.ts | 65 - plugins/step-ca/src/backend/runtime.ts | 164 - plugins/step-ca/src/backend/tables.ts | 34 - .../src/frontend/RedirectUriSetting.tsx | 54 - .../step-ca/src/frontend/StepCaAuthEditor.tsx | 29 - plugins/step-ca/src/frontend/StepCaDialog.tsx | 102 - .../step-ca/src/frontend/StepCaOverlay.tsx | 187 - plugins/step-ca/src/frontend/index.tsx | 29 - plugins/step-ca/tests/backend/client.test.ts | 136 - plugins/step-ca/tests/backend/fake-ca.ts | 160 - plugins/step-ca/tests/backend/helpers.ts | 105 - .../step-ca/tests/backend/legacy-urls.test.ts | 19 - .../tests/backend/settings-validation.test.ts | 25 - plugins/step-ca/tests/backend/step-ca.test.ts | 295 -- .../step-ca/tests/frontend/activate.test.tsx | 137 - plugins/step-ca/tsconfig.json | 4 - plugins/step-ca/vitest.config.ts | 3 - plugins/tailscale/.commitlintrc.json | 21 - plugins/tailscale/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/tailscale/.gitignore | 9 - plugins/tailscale/.husky/commit-msg | 1 - plugins/tailscale/.husky/pre-commit | 1 - plugins/tailscale/.prettierignore | 4 - plugins/tailscale/.prettierrc | 9 - plugins/tailscale/CHANGELOG.json | 26 - plugins/tailscale/CONTRIBUTING.md | 23 - plugins/tailscale/README.md | 38 - plugins/tailscale/locales/en.json | 89 - .../tailscale/locales/translated/af_ZA.json | 89 - .../tailscale/locales/translated/ar_SA.json | 89 - .../tailscale/locales/translated/bg_BG.json | 89 - .../tailscale/locales/translated/bn_BD.json | 89 - .../tailscale/locales/translated/ca_ES.json | 89 - .../tailscale/locales/translated/cs_CZ.json | 89 - .../tailscale/locales/translated/da_DK.json | 89 - .../tailscale/locales/translated/de_DE.json | 89 - .../tailscale/locales/translated/el_GR.json | 89 - .../tailscale/locales/translated/es_ES.json | 89 - .../tailscale/locales/translated/fi_FI.json | 89 - .../tailscale/locales/translated/fr_FR.json | 89 - .../tailscale/locales/translated/he_IL.json | 89 - .../tailscale/locales/translated/hi_IN.json | 89 - .../tailscale/locales/translated/hu_HU.json | 89 - .../tailscale/locales/translated/id_ID.json | 89 - .../tailscale/locales/translated/it_IT.json | 89 - .../tailscale/locales/translated/ja_JP.json | 89 - .../tailscale/locales/translated/ko_KR.json | 89 - .../tailscale/locales/translated/nl_NL.json | 89 - .../tailscale/locales/translated/no_NO.json | 89 - .../tailscale/locales/translated/pl_PL.json | 89 - .../tailscale/locales/translated/pt_BR.json | 89 - .../tailscale/locales/translated/pt_PT.json | 89 - .../tailscale/locales/translated/ro_RO.json | 89 - .../tailscale/locales/translated/ru_RU.json | 89 - .../tailscale/locales/translated/sr_SP.json | 89 - .../tailscale/locales/translated/sv_SE.json | 89 - .../tailscale/locales/translated/th_TH.json | 89 - .../tailscale/locales/translated/tr_TR.json | 89 - .../tailscale/locales/translated/uk_UA.json | 89 - .../tailscale/locales/translated/vi_VN.json | 89 - .../tailscale/locales/translated/zh_CN.json | 89 - .../tailscale/locales/translated/zh_TW.json | 89 - plugins/tailscale/manifest.json | 81 - plugins/tailscale/package.json | 46 - .../src/backend/host-metrics-manager.ts | 259 -- plugins/tailscale/src/backend/index.ts | 20 - plugins/tailscale/src/backend/routes.ts | 151 - .../src/backend/ssh-auth-provider.ts | 76 - plugins/tailscale/src/backend/ssh.ts | 27 - .../tailscale/src/backend/tailscale-check.ts | 45 - .../src/frontend/TailscaleAuthEditor.tsx | 122 - .../src/frontend/TailscaleCheckDialog.tsx | 89 - .../src/frontend/TailscaleCheckOverlay.tsx | 112 - .../src/frontend/TailscaleDevicesPanel.tsx | 223 - .../src/frontend/TailscaleDevicesStatus.tsx | 86 - .../src/frontend/TailscaleManagerCard.tsx | 161 - plugins/tailscale/src/frontend/index.tsx | 71 - .../tailscale/src/frontend/tailscale-api.ts | 46 - .../src/frontend/useTailscaleManager.ts | 105 - .../backend/host-metrics-manager.test.ts | 55 - .../tests/backend/permissions.test.ts | 54 - .../tailscale/tests/backend/settings.test.ts | 88 - .../tests/backend/ssh-auth-provider.test.ts | 181 - .../tests/backend/tailscale-check.test.ts | 76 - .../frontend/TailscaleDevicesPanel.test.tsx | 148 - .../tests/frontend/activate.test.tsx | 53 - .../tests/frontend/tailscale-api.test.ts | 55 - plugins/tailscale/tsconfig.json | 4 - plugins/tailscale/vitest.config.ts | 3 - plugins/telemetry/.commitlintrc.json | 21 - plugins/telemetry/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/telemetry/.gitignore | 9 - plugins/telemetry/.husky/commit-msg | 1 - plugins/telemetry/.husky/pre-commit | 1 - plugins/telemetry/.prettierignore | 4 - plugins/telemetry/.prettierrc | 9 - plugins/telemetry/CHANGELOG.json | 22 - plugins/telemetry/CONTRIBUTING.md | 26 - plugins/telemetry/README.md | 37 - plugins/telemetry/locales/en.json | 61 - .../telemetry/locales/translated/af_ZA.json | 61 - .../telemetry/locales/translated/ar_SA.json | 61 - .../telemetry/locales/translated/bg_BG.json | 61 - .../telemetry/locales/translated/bn_BD.json | 61 - .../telemetry/locales/translated/ca_ES.json | 61 - .../telemetry/locales/translated/cs_CZ.json | 61 - .../telemetry/locales/translated/da_DK.json | 61 - .../telemetry/locales/translated/de_DE.json | 61 - .../telemetry/locales/translated/el_GR.json | 61 - .../telemetry/locales/translated/es_ES.json | 61 - .../telemetry/locales/translated/fi_FI.json | 61 - .../telemetry/locales/translated/fr_FR.json | 61 - .../telemetry/locales/translated/he_IL.json | 61 - .../telemetry/locales/translated/hi_IN.json | 61 - .../telemetry/locales/translated/hu_HU.json | 61 - .../telemetry/locales/translated/id_ID.json | 61 - .../telemetry/locales/translated/it_IT.json | 61 - .../telemetry/locales/translated/ja_JP.json | 61 - .../telemetry/locales/translated/ko_KR.json | 61 - .../telemetry/locales/translated/nl_NL.json | 61 - .../telemetry/locales/translated/no_NO.json | 61 - .../telemetry/locales/translated/pl_PL.json | 61 - .../telemetry/locales/translated/pt_BR.json | 61 - .../telemetry/locales/translated/pt_PT.json | 61 - .../telemetry/locales/translated/ro_RO.json | 61 - .../telemetry/locales/translated/ru_RU.json | 61 - .../telemetry/locales/translated/sr_SP.json | 61 - .../telemetry/locales/translated/sv_SE.json | 61 - .../telemetry/locales/translated/th_TH.json | 61 - .../telemetry/locales/translated/tr_TR.json | 61 - .../telemetry/locales/translated/uk_UA.json | 61 - .../telemetry/locales/translated/vi_VN.json | 61 - .../telemetry/locales/translated/zh_CN.json | 61 - .../telemetry/locales/translated/zh_TW.json | 61 - plugins/telemetry/manifest.json | 97 - plugins/telemetry/package.json | 44 - plugins/telemetry/src/backend/collect.ts | 140 - plugins/telemetry/src/backend/config.ts | 26 - plugins/telemetry/src/backend/index.ts | 65 - plugins/telemetry/src/backend/reporter.ts | 117 - plugins/telemetry/src/backend/routes.ts | 148 - plugins/telemetry/src/backend/usage.ts | 75 - .../src/frontend/TelemetryStatusSetting.tsx | 191 - plugins/telemetry/src/frontend/index.tsx | 12 - plugins/telemetry/src/frontend/tracker.ts | 109 - .../telemetry/tests/backend/telemetry.test.ts | 378 -- .../tests/frontend/telemetry.test.tsx | 193 - plugins/telemetry/tsconfig.json | 4 - plugins/telemetry/vitest.config.ts | 3 - plugins/termix-identity/.commitlintrc.json | 21 - plugins/termix-identity/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/termix-identity/.gitignore | 9 - plugins/termix-identity/.husky/commit-msg | 1 - plugins/termix-identity/.husky/pre-commit | 1 - plugins/termix-identity/.prettierignore | 4 - plugins/termix-identity/.prettierrc | 9 - plugins/termix-identity/CHANGELOG.json | 26 - plugins/termix-identity/CONTRIBUTING.md | 15 - plugins/termix-identity/README.md | 38 - plugins/termix-identity/locales/en.json | 86 - .../locales/translated/af_ZA.json | 86 - .../locales/translated/ar_SA.json | 86 - .../locales/translated/bg_BG.json | 86 - .../locales/translated/bn_BD.json | 86 - .../locales/translated/ca_ES.json | 86 - .../locales/translated/cs_CZ.json | 86 - .../locales/translated/da_DK.json | 86 - .../locales/translated/de_DE.json | 86 - .../locales/translated/el_GR.json | 86 - .../locales/translated/es_ES.json | 86 - .../locales/translated/fi_FI.json | 86 - .../locales/translated/fr_FR.json | 86 - .../locales/translated/he_IL.json | 86 - .../locales/translated/hi_IN.json | 86 - .../locales/translated/hu_HU.json | 86 - .../locales/translated/id_ID.json | 86 - .../locales/translated/it_IT.json | 86 - .../locales/translated/ja_JP.json | 86 - .../locales/translated/ko_KR.json | 86 - .../locales/translated/nl_NL.json | 86 - .../locales/translated/no_NO.json | 86 - .../locales/translated/pl_PL.json | 86 - .../locales/translated/pt_BR.json | 86 - .../locales/translated/pt_PT.json | 86 - .../locales/translated/ro_RO.json | 86 - .../locales/translated/ru_RU.json | 86 - .../locales/translated/sr_SP.json | 86 - .../locales/translated/sv_SE.json | 86 - .../locales/translated/th_TH.json | 86 - .../locales/translated/tr_TR.json | 86 - .../locales/translated/uk_UA.json | 86 - .../locales/translated/vi_VN.json | 86 - .../locales/translated/zh_CN.json | 86 - .../locales/translated/zh_TW.json | 86 - plugins/termix-identity/manifest.json | 54 - .../0001_adopt_termix_identity_tables.sql | 46 - .../0001_adopt_termix_identity_tables.sql | 47 - .../termix-identity/migrations/snapshot.json | 146 - .../0001_adopt_termix_identity_tables.sql | 47 - plugins/termix-identity/package.json | 47 - .../src/backend/certificate.ts | 145 - .../termix-identity/src/backend/handles.ts | 69 - plugins/termix-identity/src/backend/index.ts | 20 - plugins/termix-identity/src/backend/keys.ts | 89 - .../termix-identity/src/backend/resolver.ts | 173 - plugins/termix-identity/src/backend/routes.ts | 987 ---- plugins/termix-identity/src/backend/store.ts | 292 -- plugins/termix-identity/src/backend/tables.ts | 78 - .../src/frontend/TermixIdPanel.tsx | 953 ---- plugins/termix-identity/src/frontend/api.ts | 57 - .../termix-identity/src/frontend/index.tsx | 101 - .../src/frontend/linked-store.ts | 48 - plugins/termix-identity/src/frontend/types.ts | 64 - .../tests/backend/certificate.test.ts | 127 - .../termix-identity/tests/backend/helpers.ts | 144 - .../tests/backend/keys.test.ts | 95 - .../tests/backend/legacy-urls.test.ts | 20 - .../tests/backend/migrations.test.ts | 175 - .../tests/backend/termix-identity.test.ts | 598 --- .../tests/frontend/activate.test.tsx | 236 - plugins/termix-identity/tsconfig.json | 4 - plugins/termix-identity/vitest.config.ts | 3 - plugins/tmux-monitor/.commitlintrc.json | 21 - plugins/tmux-monitor/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/tmux-monitor/.gitignore | 9 - plugins/tmux-monitor/.husky/commit-msg | 1 - plugins/tmux-monitor/.husky/pre-commit | 1 - plugins/tmux-monitor/.prettierignore | 4 - plugins/tmux-monitor/.prettierrc | 9 - plugins/tmux-monitor/CHANGELOG.json | 34 - plugins/tmux-monitor/CONTRIBUTING.md | 22 - plugins/tmux-monitor/README.md | 48 - plugins/tmux-monitor/locales/en.json | 117 - .../locales/translated/af_ZA.json | 117 - .../locales/translated/ar_SA.json | 117 - .../locales/translated/bg_BG.json | 117 - .../locales/translated/bn_BD.json | 117 - .../locales/translated/ca_ES.json | 117 - .../locales/translated/cs_CZ.json | 117 - .../locales/translated/da_DK.json | 117 - .../locales/translated/de_DE.json | 117 - .../locales/translated/el_GR.json | 117 - .../locales/translated/es_ES.json | 117 - .../locales/translated/fi_FI.json | 117 - .../locales/translated/fr_FR.json | 117 - .../locales/translated/he_IL.json | 117 - .../locales/translated/hi_IN.json | 117 - .../locales/translated/hu_HU.json | 117 - .../locales/translated/id_ID.json | 117 - .../locales/translated/it_IT.json | 117 - .../locales/translated/ja_JP.json | 117 - .../locales/translated/ko_KR.json | 117 - .../locales/translated/nl_NL.json | 117 - .../locales/translated/no_NO.json | 117 - .../locales/translated/pl_PL.json | 117 - .../locales/translated/pt_BR.json | 117 - .../locales/translated/pt_PT.json | 117 - .../locales/translated/ro_RO.json | 117 - .../locales/translated/ru_RU.json | 117 - .../locales/translated/sr_SP.json | 117 - .../locales/translated/sv_SE.json | 117 - .../locales/translated/th_TH.json | 117 - .../locales/translated/tr_TR.json | 117 - .../locales/translated/uk_UA.json | 117 - .../locales/translated/vi_VN.json | 117 - .../locales/translated/zh_CN.json | 117 - .../locales/translated/zh_TW.json | 117 - plugins/tmux-monitor/manifest.json | 73 - .../mysql/0001_adopt_tmux_session_tags.sql | 14 - .../postgres/0001_adopt_tmux_session_tags.sql | 14 - plugins/tmux-monitor/migrations/snapshot.json | 38 - .../sqlite/0001_adopt_tmux_session_tags.sql | 14 - plugins/tmux-monitor/package.json | 46 - plugins/tmux-monitor/src/backend/index.ts | 23 - .../src/backend/monitor-helpers.ts | 250 - .../tmux-monitor/src/backend/repository.ts | 95 - plugins/tmux-monitor/src/backend/routes.ts | 872 ---- plugins/tmux-monitor/src/backend/service.ts | 57 - plugins/tmux-monitor/src/backend/tables.ts | 32 - .../tmux-monitor/src/backend/tmux-commands.ts | 164 - .../src/frontend/HostOverview.tsx | 108 - .../src/frontend/HostTmuxMonitorSection.tsx | 78 - .../tmux-monitor/src/frontend/PanePreview.tsx | 169 - .../src/frontend/SearchResults.tsx | 86 - .../tmux-monitor/src/frontend/SessionTree.tsx | 473 -- .../tmux-monitor/src/frontend/TmuxMonitor.tsx | 1687 ------- .../src/frontend/TmuxMonitorApp.tsx | 16 - plugins/tmux-monitor/src/frontend/api.ts | 185 - plugins/tmux-monitor/src/frontend/format.ts | 22 - .../src/frontend/host-tmux-monitor.ts | 12 - plugins/tmux-monitor/src/frontend/index.tsx | 69 - .../src/frontend/monitor-storage.ts | 36 - .../tmux-monitor/src/frontend/poll-queue.ts | 19 - plugins/tmux-monitor/src/frontend/types.ts | 5 - .../src/frontend/use-adaptive-polling.ts | 63 - plugins/tmux-monitor/tests/backend/helpers.ts | 120 - .../tests/backend/migrations.test.ts | 91 - .../tests/backend/monitor-helpers.test.ts | 272 -- .../tests/backend/repository.test.ts | 96 - .../tmux-monitor/tests/backend/routes.test.ts | 90 - .../tests/backend/service.test.ts | 122 - .../tests/backend/tmux-commands.test.ts | 160 - .../tests/frontend/activate.test.tsx | 108 - .../tests/frontend/host-tmux-monitor.test.ts | 44 - .../tests/frontend/monitor-storage.test.ts | 32 - .../frontend/multi-host-monitor.test.tsx | 351 -- .../tests/frontend/poll-queue.test.ts | 24 - .../frontend/session-tree-selection.test.tsx | 50 - plugins/tmux-monitor/tsconfig.json | 4 - plugins/tmux-monitor/vitest.config.ts | 3 - plugins/totp/.commitlintrc.json | 21 - plugins/totp/.github/funding.yml | 1 - plugins/totp/.github/pull_request_template.md | 34 - plugins/totp/.gitignore | 9 - plugins/totp/.husky/commit-msg | 1 - plugins/totp/.husky/pre-commit | 1 - plugins/totp/.prettierignore | 4 - plugins/totp/.prettierrc | 9 - plugins/totp/CHANGELOG.json | 22 - plugins/totp/CONTRIBUTING.md | 11 - plugins/totp/README.md | 37 - plugins/totp/locales/en.json | 42 - plugins/totp/locales/translated/af_ZA.json | 42 - plugins/totp/locales/translated/ar_SA.json | 42 - plugins/totp/locales/translated/bg_BG.json | 42 - plugins/totp/locales/translated/bn_BD.json | 42 - plugins/totp/locales/translated/ca_ES.json | 42 - plugins/totp/locales/translated/cs_CZ.json | 42 - plugins/totp/locales/translated/da_DK.json | 42 - plugins/totp/locales/translated/de_DE.json | 42 - plugins/totp/locales/translated/el_GR.json | 42 - plugins/totp/locales/translated/es_ES.json | 42 - plugins/totp/locales/translated/fi_FI.json | 42 - plugins/totp/locales/translated/fr_FR.json | 42 - plugins/totp/locales/translated/he_IL.json | 42 - plugins/totp/locales/translated/hi_IN.json | 42 - plugins/totp/locales/translated/hu_HU.json | 42 - plugins/totp/locales/translated/id_ID.json | 42 - plugins/totp/locales/translated/it_IT.json | 42 - plugins/totp/locales/translated/ja_JP.json | 42 - plugins/totp/locales/translated/ko_KR.json | 42 - plugins/totp/locales/translated/nl_NL.json | 42 - plugins/totp/locales/translated/no_NO.json | 42 - plugins/totp/locales/translated/pl_PL.json | 42 - plugins/totp/locales/translated/pt_BR.json | 42 - plugins/totp/locales/translated/pt_PT.json | 42 - plugins/totp/locales/translated/ro_RO.json | 42 - plugins/totp/locales/translated/ru_RU.json | 42 - plugins/totp/locales/translated/sr_SP.json | 42 - plugins/totp/locales/translated/sv_SE.json | 42 - plugins/totp/locales/translated/th_TH.json | 42 - plugins/totp/locales/translated/tr_TR.json | 42 - plugins/totp/locales/translated/uk_UA.json | 42 - plugins/totp/locales/translated/vi_VN.json | 42 - plugins/totp/locales/translated/zh_CN.json | 42 - plugins/totp/locales/translated/zh_TW.json | 42 - plugins/totp/manifest.json | 33 - .../mysql/0001_create_enrollments.sql | 12 - .../postgres/0001_create_enrollments.sql | 12 - plugins/totp/migrations/snapshot.json | 35 - .../sqlite/0001_create_enrollments.sql | 12 - plugins/totp/package.json | 48 - plugins/totp/src/backend/index.ts | 41 - plugins/totp/src/backend/repository.ts | 69 - plugins/totp/src/backend/routes.ts | 268 -- plugins/totp/src/backend/tables.ts | 24 - plugins/totp/src/backend/totp.ts | 136 - plugins/totp/src/frontend/TotpChallenge.tsx | 82 - plugins/totp/src/frontend/TotpEnrollment.tsx | 404 -- plugins/totp/src/frontend/index.tsx | 12 - plugins/totp/src/frontend/totp-input.ts | 17 - plugins/totp/tests/backend/helpers.ts | 95 - plugins/totp/tests/backend/totp.test.ts | 258 -- plugins/totp/tests/frontend/activate.test.tsx | 90 - plugins/totp/tsconfig.json | 4 - plugins/totp/vitest.config.ts | 3 - plugins/tunnels/.commitlintrc.json | 21 - plugins/tunnels/.github/funding.yml | 1 - .../tunnels/.github/pull_request_template.md | 34 - plugins/tunnels/.gitignore | 9 - plugins/tunnels/.husky/commit-msg | 1 - plugins/tunnels/.husky/pre-commit | 1 - plugins/tunnels/.prettierignore | 4 - plugins/tunnels/.prettierrc | 9 - plugins/tunnels/CHANGELOG.json | 30 - plugins/tunnels/CONTRIBUTING.md | 22 - plugins/tunnels/README.md | 47 - plugins/tunnels/locales/en.json | 175 - plugins/tunnels/locales/translated/af_ZA.json | 175 - plugins/tunnels/locales/translated/ar_SA.json | 175 - plugins/tunnels/locales/translated/bg_BG.json | 175 - plugins/tunnels/locales/translated/bn_BD.json | 175 - plugins/tunnels/locales/translated/ca_ES.json | 175 - plugins/tunnels/locales/translated/cs_CZ.json | 175 - plugins/tunnels/locales/translated/da_DK.json | 175 - plugins/tunnels/locales/translated/de_DE.json | 175 - plugins/tunnels/locales/translated/el_GR.json | 175 - plugins/tunnels/locales/translated/es_ES.json | 175 - plugins/tunnels/locales/translated/fi_FI.json | 175 - plugins/tunnels/locales/translated/fr_FR.json | 175 - plugins/tunnels/locales/translated/he_IL.json | 175 - plugins/tunnels/locales/translated/hi_IN.json | 175 - plugins/tunnels/locales/translated/hu_HU.json | 175 - plugins/tunnels/locales/translated/id_ID.json | 175 - plugins/tunnels/locales/translated/it_IT.json | 175 - plugins/tunnels/locales/translated/ja_JP.json | 175 - plugins/tunnels/locales/translated/ko_KR.json | 175 - plugins/tunnels/locales/translated/nl_NL.json | 175 - plugins/tunnels/locales/translated/no_NO.json | 175 - plugins/tunnels/locales/translated/pl_PL.json | 175 - plugins/tunnels/locales/translated/pt_BR.json | 175 - plugins/tunnels/locales/translated/pt_PT.json | 175 - plugins/tunnels/locales/translated/ro_RO.json | 175 - plugins/tunnels/locales/translated/ru_RU.json | 175 - plugins/tunnels/locales/translated/sr_SP.json | 175 - plugins/tunnels/locales/translated/sv_SE.json | 175 - plugins/tunnels/locales/translated/th_TH.json | 175 - plugins/tunnels/locales/translated/tr_TR.json | 175 - plugins/tunnels/locales/translated/uk_UA.json | 175 - plugins/tunnels/locales/translated/vi_VN.json | 175 - plugins/tunnels/locales/translated/zh_CN.json | 175 - plugins/tunnels/locales/translated/zh_TW.json | 175 - plugins/tunnels/manifest.json | 97 - .../mysql/0001_adopt_c2s_tunnel_presets.sql | 15 - .../0001_adopt_c2s_tunnel_presets.sql | 15 - plugins/tunnels/migrations/snapshot.json | 45 - .../sqlite/0001_adopt_c2s_tunnel_presets.sql | 15 - plugins/tunnels/package.json | 48 - plugins/tunnels/src/backend/authorize.ts | 29 - plugins/tunnels/src/backend/autostart.ts | 94 - plugins/tunnels/src/backend/c2s-auth.ts | 53 - .../tunnels/src/backend/c2s-connections.ts | 94 - plugins/tunnels/src/backend/c2s-relay.ts | 456 -- plugins/tunnels/src/backend/config.ts | 172 - plugins/tunnels/src/backend/index.ts | 50 - plugins/tunnels/src/backend/manager.ts | 839 ---- plugins/tunnels/src/backend/repository.ts | 133 - plugins/tunnels/src/backend/routes.ts | 588 --- plugins/tunnels/src/backend/service.ts | 260 -- plugins/tunnels/src/backend/socks5-relay.ts | 107 - plugins/tunnels/src/backend/ssh-primitives.ts | 78 - plugins/tunnels/src/backend/tables.ts | 32 - plugins/tunnels/src/backend/types.ts | 60 - plugins/tunnels/src/backend/utils.ts | 265 -- .../src/frontend/ActiveTunnelsCounter.tsx | 42 - .../src/frontend/C2STunnelPresetManager.tsx | 1282 ------ .../src/frontend/HostTunnelsSection.tsx | 372 -- .../src/frontend/TunnelAuthPrompts.tsx | 135 - plugins/tunnels/src/frontend/TunnelTab.tsx | 373 -- plugins/tunnels/src/frontend/api.ts | 263 -- .../src/frontend/c2s-tunnel-config-utils.ts | 70 - plugins/tunnels/src/frontend/host-tunnels.ts | 65 - plugins/tunnels/src/frontend/index.tsx | 124 - .../tunnels/src/frontend/tunnel-form-utils.ts | 64 - plugins/tunnels/src/frontend/views.tsx | 151 - plugins/tunnels/src/shared/tunnel-naming.ts | 76 - plugins/tunnels/src/shared/types.ts | 88 - .../tunnels/tests/backend/activate.test.ts | 85 - .../tunnels/tests/backend/authorize.test.ts | 53 - .../tunnels/tests/backend/autostart.test.ts | 78 - .../tunnels/tests/backend/c2s-auth.test.ts | 155 - .../tests/backend/c2s-relay-auth.test.ts | 108 - .../tests/backend/c2s-source-host.test.ts | 31 - plugins/tunnels/tests/backend/helpers.ts | 148 - plugins/tunnels/tests/backend/manager.test.ts | 258 -- .../tunnels/tests/backend/migrations.test.ts | 112 - .../tests/backend/reserved-names.test.ts | 59 - plugins/tunnels/tests/backend/routes.test.ts | 220 - plugins/tunnels/tests/backend/service.test.ts | 175 - .../tunnels/tests/frontend/activate.test.tsx | 170 - plugins/tunnels/tests/frontend/api.test.ts | 101 - .../tests/frontend/auth-prompts.test.tsx | 84 - .../frontend/c2s-tunnel-config-utils.test.ts | 151 - .../tests/frontend/tunnel-form-utils.test.ts | 62 - .../tests/frontend/tunnel-naming.test.ts | 52 - plugins/tunnels/tsconfig.json | 4 - plugins/tunnels/vitest.config.ts | 3 - plugins/vault/.commitlintrc.json | 21 - plugins/vault/.github/funding.yml | 1 - .../vault/.github/pull_request_template.md | 34 - plugins/vault/.gitignore | 9 - plugins/vault/.husky/commit-msg | 1 - plugins/vault/.husky/pre-commit | 1 - plugins/vault/.prettierignore | 4 - plugins/vault/.prettierrc | 9 - plugins/vault/CHANGELOG.json | 26 - plugins/vault/CONTRIBUTING.md | 27 - plugins/vault/README.md | 38 - plugins/vault/locales/en.json | 72 - plugins/vault/locales/translated/af_ZA.json | 72 - plugins/vault/locales/translated/ar_SA.json | 72 - plugins/vault/locales/translated/bg_BG.json | 72 - plugins/vault/locales/translated/bn_BD.json | 72 - plugins/vault/locales/translated/ca_ES.json | 72 - plugins/vault/locales/translated/cs_CZ.json | 72 - plugins/vault/locales/translated/da_DK.json | 72 - plugins/vault/locales/translated/de_DE.json | 72 - plugins/vault/locales/translated/el_GR.json | 72 - plugins/vault/locales/translated/es_ES.json | 72 - plugins/vault/locales/translated/fi_FI.json | 72 - plugins/vault/locales/translated/fr_FR.json | 72 - plugins/vault/locales/translated/he_IL.json | 72 - plugins/vault/locales/translated/hi_IN.json | 72 - plugins/vault/locales/translated/hu_HU.json | 72 - plugins/vault/locales/translated/id_ID.json | 72 - plugins/vault/locales/translated/it_IT.json | 72 - plugins/vault/locales/translated/ja_JP.json | 72 - plugins/vault/locales/translated/ko_KR.json | 72 - plugins/vault/locales/translated/nl_NL.json | 72 - plugins/vault/locales/translated/no_NO.json | 72 - plugins/vault/locales/translated/pl_PL.json | 72 - plugins/vault/locales/translated/pt_BR.json | 72 - plugins/vault/locales/translated/pt_PT.json | 72 - plugins/vault/locales/translated/ro_RO.json | 72 - plugins/vault/locales/translated/ru_RU.json | 72 - plugins/vault/locales/translated/sr_SP.json | 72 - plugins/vault/locales/translated/sv_SE.json | 72 - plugins/vault/locales/translated/th_TH.json | 72 - plugins/vault/locales/translated/tr_TR.json | 72 - plugins/vault/locales/translated/uk_UA.json | 72 - plugins/vault/locales/translated/vi_VN.json | 72 - plugins/vault/locales/translated/zh_CN.json | 72 - plugins/vault/locales/translated/zh_TW.json | 72 - plugins/vault/manifest.json | 84 - .../mysql/0001_adopt_vault_tables.sql | 37 - .../postgres/0001_adopt_vault_tables.sql | 38 - plugins/vault/migrations/snapshot.json | 127 - .../sqlite/0001_adopt_vault_tables.sql | 38 - plugins/vault/package.json | 47 - plugins/vault/src/backend/auth-session.ts | 206 - plugins/vault/src/backend/index.ts | 54 - plugins/vault/src/backend/profile-store.ts | 139 - plugins/vault/src/backend/provider.ts | 124 - plugins/vault/src/backend/routes.ts | 337 -- plugins/vault/src/backend/tables.ts | 74 - plugins/vault/src/backend/token-store.ts | 114 - plugins/vault/src/backend/vault-client.ts | 286 -- .../vault/src/frontend/RedirectUriSetting.tsx | 54 - .../vault/src/frontend/VaultAuthEditor.tsx | 108 - plugins/vault/src/frontend/VaultOverlay.tsx | 245 - .../src/frontend/VaultProfileManager.tsx | 286 -- plugins/vault/src/frontend/index.tsx | 29 - plugins/vault/src/frontend/types.ts | 34 - plugins/vault/tests/backend/helpers.ts | 174 - .../vault/tests/backend/legacy-urls.test.ts | 19 - .../vault/tests/backend/migrations.test.ts | 121 - .../vault/tests/backend/vault-client.test.ts | 216 - plugins/vault/tests/backend/vault.test.ts | 463 -- .../vault/tests/frontend/activate.test.tsx | 268 -- plugins/vault/tsconfig.json | 4 - plugins/vault/vitest.config.ts | 3 - plugins/wake-on-lan/.commitlintrc.json | 21 - plugins/wake-on-lan/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/wake-on-lan/.gitignore | 9 - plugins/wake-on-lan/.husky/commit-msg | 1 - plugins/wake-on-lan/.husky/pre-commit | 1 - plugins/wake-on-lan/.prettierignore | 4 - plugins/wake-on-lan/.prettierrc | 9 - plugins/wake-on-lan/CHANGELOG.json | 22 - plugins/wake-on-lan/CONTRIBUTING.md | 22 - plugins/wake-on-lan/README.md | 45 - plugins/wake-on-lan/locales/en.json | 27 - .../wake-on-lan/locales/translated/af_ZA.json | 27 - .../wake-on-lan/locales/translated/ar_SA.json | 27 - .../wake-on-lan/locales/translated/bg_BG.json | 27 - .../wake-on-lan/locales/translated/bn_BD.json | 27 - .../wake-on-lan/locales/translated/ca_ES.json | 27 - .../wake-on-lan/locales/translated/cs_CZ.json | 27 - .../wake-on-lan/locales/translated/da_DK.json | 27 - .../wake-on-lan/locales/translated/de_DE.json | 27 - .../wake-on-lan/locales/translated/el_GR.json | 27 - .../wake-on-lan/locales/translated/es_ES.json | 27 - .../wake-on-lan/locales/translated/fi_FI.json | 27 - .../wake-on-lan/locales/translated/fr_FR.json | 27 - .../wake-on-lan/locales/translated/he_IL.json | 27 - .../wake-on-lan/locales/translated/hi_IN.json | 27 - .../wake-on-lan/locales/translated/hu_HU.json | 27 - .../wake-on-lan/locales/translated/id_ID.json | 27 - .../wake-on-lan/locales/translated/it_IT.json | 27 - .../wake-on-lan/locales/translated/ja_JP.json | 27 - .../wake-on-lan/locales/translated/ko_KR.json | 27 - .../wake-on-lan/locales/translated/nl_NL.json | 27 - .../wake-on-lan/locales/translated/no_NO.json | 27 - .../wake-on-lan/locales/translated/pl_PL.json | 27 - .../wake-on-lan/locales/translated/pt_BR.json | 27 - .../wake-on-lan/locales/translated/pt_PT.json | 27 - .../wake-on-lan/locales/translated/ro_RO.json | 27 - .../wake-on-lan/locales/translated/ru_RU.json | 27 - .../wake-on-lan/locales/translated/sr_SP.json | 27 - .../wake-on-lan/locales/translated/sv_SE.json | 27 - .../wake-on-lan/locales/translated/th_TH.json | 27 - .../wake-on-lan/locales/translated/tr_TR.json | 27 - .../wake-on-lan/locales/translated/uk_UA.json | 27 - .../wake-on-lan/locales/translated/vi_VN.json | 27 - .../wake-on-lan/locales/translated/zh_CN.json | 27 - .../wake-on-lan/locales/translated/zh_TW.json | 27 - plugins/wake-on-lan/manifest.json | 65 - plugins/wake-on-lan/package.json | 41 - plugins/wake-on-lan/src/backend/index.ts | 15 - .../wake-on-lan/src/backend/magic-packet.ts | 49 - plugins/wake-on-lan/src/backend/routes.ts | 47 - plugins/wake-on-lan/src/backend/service.ts | 33 - .../wake-on-lan/src/frontend/host-action.ts | 40 - plugins/wake-on-lan/src/frontend/index.tsx | 19 - plugins/wake-on-lan/tests/backend/helpers.ts | 77 - .../tests/backend/magic-packet.test.ts | 120 - .../wake-on-lan/tests/backend/routes.test.ts | 65 - .../wake-on-lan/tests/backend/service.test.ts | 103 - .../tests/frontend/activate.test.tsx | 33 - .../tests/frontend/host-action.test.ts | 98 - plugins/wake-on-lan/tsconfig.json | 4 - plugins/wake-on-lan/vitest.config.ts | 3 - plugins/warpgate/.commitlintrc.json | 21 - plugins/warpgate/.github/funding.yml | 1 - .../warpgate/.github/pull_request_template.md | 34 - plugins/warpgate/.gitignore | 9 - plugins/warpgate/.husky/commit-msg | 1 - plugins/warpgate/.husky/pre-commit | 1 - plugins/warpgate/.prettierignore | 4 - plugins/warpgate/.prettierrc | 9 - plugins/warpgate/CHANGELOG.json | 18 - plugins/warpgate/CONTRIBUTING.md | 17 - plugins/warpgate/README.md | 36 - plugins/warpgate/locales/en.json | 10 - .../warpgate/locales/translated/af_ZA.json | 10 - .../warpgate/locales/translated/ar_SA.json | 10 - .../warpgate/locales/translated/bg_BG.json | 10 - .../warpgate/locales/translated/bn_BD.json | 10 - .../warpgate/locales/translated/ca_ES.json | 10 - .../warpgate/locales/translated/cs_CZ.json | 10 - .../warpgate/locales/translated/da_DK.json | 10 - .../warpgate/locales/translated/de_DE.json | 10 - .../warpgate/locales/translated/el_GR.json | 10 - .../warpgate/locales/translated/es_ES.json | 10 - .../warpgate/locales/translated/fi_FI.json | 10 - .../warpgate/locales/translated/fr_FR.json | 10 - .../warpgate/locales/translated/he_IL.json | 10 - .../warpgate/locales/translated/hi_IN.json | 10 - .../warpgate/locales/translated/hu_HU.json | 10 - .../warpgate/locales/translated/id_ID.json | 10 - .../warpgate/locales/translated/it_IT.json | 10 - .../warpgate/locales/translated/ja_JP.json | 10 - .../warpgate/locales/translated/ko_KR.json | 10 - .../warpgate/locales/translated/nl_NL.json | 10 - .../warpgate/locales/translated/no_NO.json | 10 - .../warpgate/locales/translated/pl_PL.json | 10 - .../warpgate/locales/translated/pt_BR.json | 10 - .../warpgate/locales/translated/pt_PT.json | 10 - .../warpgate/locales/translated/ro_RO.json | 10 - .../warpgate/locales/translated/ru_RU.json | 10 - .../warpgate/locales/translated/sr_SP.json | 10 - .../warpgate/locales/translated/sv_SE.json | 10 - .../warpgate/locales/translated/th_TH.json | 10 - .../warpgate/locales/translated/tr_TR.json | 10 - .../warpgate/locales/translated/uk_UA.json | 10 - .../warpgate/locales/translated/vi_VN.json | 10 - .../warpgate/locales/translated/zh_CN.json | 10 - .../warpgate/locales/translated/zh_TW.json | 10 - plugins/warpgate/manifest.json | 40 - plugins/warpgate/package.json | 35 - plugins/warpgate/src/backend/detect.ts | 37 - plugins/warpgate/src/backend/host-import.ts | 14 - plugins/warpgate/src/backend/index.ts | 22 - plugins/warpgate/src/frontend/index.tsx | 5 - .../warpgate/tests/backend/warpgate.test.ts | 113 - plugins/warpgate/tsconfig.json | 4 - plugins/warpgate/vitest.config.ts | 3 - plugins/web-endpoint/.commitlintrc.json | 21 - plugins/web-endpoint/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/web-endpoint/.gitignore | 9 - plugins/web-endpoint/.husky/commit-msg | 1 - plugins/web-endpoint/.husky/pre-commit | 1 - plugins/web-endpoint/.prettierignore | 4 - plugins/web-endpoint/.prettierrc | 9 - plugins/web-endpoint/CHANGELOG.json | 22 - plugins/web-endpoint/CONTRIBUTING.md | 18 - plugins/web-endpoint/README.md | 45 - plugins/web-endpoint/locales/en.json | 55 - .../locales/translated/af_ZA.json | 55 - .../locales/translated/ar_SA.json | 55 - .../locales/translated/bg_BG.json | 55 - .../locales/translated/bn_BD.json | 55 - .../locales/translated/ca_ES.json | 55 - .../locales/translated/cs_CZ.json | 55 - .../locales/translated/da_DK.json | 55 - .../locales/translated/de_DE.json | 55 - .../locales/translated/el_GR.json | 55 - .../locales/translated/es_ES.json | 55 - .../locales/translated/fi_FI.json | 55 - .../locales/translated/fr_FR.json | 55 - .../locales/translated/he_IL.json | 55 - .../locales/translated/hi_IN.json | 55 - .../locales/translated/hu_HU.json | 55 - .../locales/translated/id_ID.json | 55 - .../locales/translated/it_IT.json | 55 - .../locales/translated/ja_JP.json | 55 - .../locales/translated/ko_KR.json | 55 - .../locales/translated/nl_NL.json | 55 - .../locales/translated/no_NO.json | 55 - .../locales/translated/pl_PL.json | 55 - .../locales/translated/pt_BR.json | 55 - .../locales/translated/pt_PT.json | 55 - .../locales/translated/ro_RO.json | 55 - .../locales/translated/ru_RU.json | 55 - .../locales/translated/sr_SP.json | 55 - .../locales/translated/sv_SE.json | 55 - .../locales/translated/th_TH.json | 55 - .../locales/translated/tr_TR.json | 55 - .../locales/translated/uk_UA.json | 55 - .../locales/translated/vi_VN.json | 55 - .../locales/translated/zh_CN.json | 55 - .../locales/translated/zh_TW.json | 55 - plugins/web-endpoint/manifest.json | 65 - plugins/web-endpoint/package.json | 47 - .../web-endpoint/src/backend/host-import.ts | 19 - plugins/web-endpoint/src/backend/index.ts | 25 - plugins/web-endpoint/src/backend/routes.ts | 301 -- .../src/frontend/HostEditorWebUiSection.tsx | 523 --- .../src/frontend/WebEndpointTab.tsx | 231 - plugins/web-endpoint/src/frontend/index.tsx | 131 - .../src/frontend/web-endpoint-api.ts | 155 - .../src/frontend/web-endpoint-url.ts | 211 - .../src/frontend/web-endpoint-validation.ts | 119 - .../src/shared/web-endpoint-config.ts | 253 -- .../tests/backend/activate.test.ts | 54 - .../tests/backend/host-import.test.ts | 61 - .../web-endpoint/tests/backend/routes.test.ts | 304 -- .../tests/backend/web-endpoint-config.test.ts | 273 -- .../tests/frontend/WebEndpointTab.test.tsx | 458 -- .../tests/frontend/activate.test.tsx | 158 - .../tests/frontend/web-endpoint-api.test.ts | 161 - .../tests/frontend/web-endpoint-url.test.ts | 367 -- .../frontend/web-endpoint-validation.test.ts | 137 - plugins/web-endpoint/tsconfig.json | 4 - plugins/web-endpoint/vitest.config.ts | 3 - plugins/webauthn/.commitlintrc.json | 21 - plugins/webauthn/.github/funding.yml | 1 - .../webauthn/.github/pull_request_template.md | 34 - plugins/webauthn/.gitignore | 9 - plugins/webauthn/.husky/commit-msg | 1 - plugins/webauthn/.husky/pre-commit | 1 - plugins/webauthn/.prettierignore | 4 - plugins/webauthn/.prettierrc | 9 - plugins/webauthn/CHANGELOG.json | 22 - plugins/webauthn/CONTRIBUTING.md | 11 - plugins/webauthn/README.md | 37 - plugins/webauthn/locales/en.json | 22 - .../webauthn/locales/translated/af_ZA.json | 22 - .../webauthn/locales/translated/ar_SA.json | 22 - .../webauthn/locales/translated/bg_BG.json | 22 - .../webauthn/locales/translated/bn_BD.json | 22 - .../webauthn/locales/translated/ca_ES.json | 22 - .../webauthn/locales/translated/cs_CZ.json | 22 - .../webauthn/locales/translated/da_DK.json | 22 - .../webauthn/locales/translated/de_DE.json | 22 - .../webauthn/locales/translated/el_GR.json | 22 - .../webauthn/locales/translated/es_ES.json | 22 - .../webauthn/locales/translated/fi_FI.json | 22 - .../webauthn/locales/translated/fr_FR.json | 22 - .../webauthn/locales/translated/he_IL.json | 22 - .../webauthn/locales/translated/hi_IN.json | 22 - .../webauthn/locales/translated/hu_HU.json | 22 - .../webauthn/locales/translated/id_ID.json | 22 - .../webauthn/locales/translated/it_IT.json | 22 - .../webauthn/locales/translated/ja_JP.json | 22 - .../webauthn/locales/translated/ko_KR.json | 22 - .../webauthn/locales/translated/nl_NL.json | 22 - .../webauthn/locales/translated/no_NO.json | 22 - .../webauthn/locales/translated/pl_PL.json | 22 - .../webauthn/locales/translated/pt_BR.json | 22 - .../webauthn/locales/translated/pt_PT.json | 22 - .../webauthn/locales/translated/ro_RO.json | 22 - .../webauthn/locales/translated/ru_RU.json | 22 - .../webauthn/locales/translated/sr_SP.json | 22 - .../webauthn/locales/translated/sv_SE.json | 22 - .../webauthn/locales/translated/th_TH.json | 22 - .../webauthn/locales/translated/tr_TR.json | 22 - .../webauthn/locales/translated/uk_UA.json | 22 - .../webauthn/locales/translated/vi_VN.json | 22 - .../webauthn/locales/translated/zh_CN.json | 22 - .../webauthn/locales/translated/zh_TW.json | 22 - plugins/webauthn/manifest.json | 33 - .../mysql/0001_adopt_webauthn_credentials.sql | 19 - .../0001_adopt_webauthn_credentials.sql | 19 - plugins/webauthn/migrations/snapshot.json | 65 - .../0001_adopt_webauthn_credentials.sql | 19 - plugins/webauthn/package.json | 48 - plugins/webauthn/src/backend/index.ts | 33 - plugins/webauthn/src/backend/passkeys.ts | 298 -- plugins/webauthn/src/backend/repository.ts | 83 - plugins/webauthn/src/backend/routes.ts | 193 - plugins/webauthn/src/backend/tables.ts | 34 - .../src/frontend/PasskeyEnrollment.tsx | 137 - .../src/frontend/PasskeyLoginButton.tsx | 59 - plugins/webauthn/src/frontend/index.tsx | 13 - plugins/webauthn/src/frontend/webauthn-api.ts | 82 - plugins/webauthn/tests/backend/helpers.ts | 96 - .../webauthn/tests/backend/passkeys.test.ts | 371 -- .../webauthn/tests/frontend/activate.test.tsx | 129 - plugins/webauthn/tsconfig.json | 4 - plugins/webauthn/vitest.config.ts | 3 - plugins/workspaces/.commitlintrc.json | 21 - plugins/workspaces/.github/funding.yml | 1 - .../.github/pull_request_template.md | 34 - plugins/workspaces/.gitignore | 9 - plugins/workspaces/.husky/commit-msg | 1 - plugins/workspaces/.husky/pre-commit | 1 - plugins/workspaces/.prettierignore | 4 - plugins/workspaces/.prettierrc | 9 - plugins/workspaces/CHANGELOG.json | 26 - plugins/workspaces/CONTRIBUTING.md | 15 - plugins/workspaces/README.md | 46 - plugins/workspaces/locales/en.json | 70 - .../workspaces/locales/translated/af_ZA.json | 70 - .../workspaces/locales/translated/ar_SA.json | 70 - .../workspaces/locales/translated/bg_BG.json | 70 - .../workspaces/locales/translated/bn_BD.json | 70 - .../workspaces/locales/translated/ca_ES.json | 70 - .../workspaces/locales/translated/cs_CZ.json | 70 - .../workspaces/locales/translated/da_DK.json | 70 - .../workspaces/locales/translated/de_DE.json | 70 - .../workspaces/locales/translated/el_GR.json | 70 - .../workspaces/locales/translated/es_ES.json | 70 - .../workspaces/locales/translated/fi_FI.json | 70 - .../workspaces/locales/translated/fr_FR.json | 70 - .../workspaces/locales/translated/he_IL.json | 70 - .../workspaces/locales/translated/hi_IN.json | 70 - .../workspaces/locales/translated/hu_HU.json | 70 - .../workspaces/locales/translated/id_ID.json | 70 - .../workspaces/locales/translated/it_IT.json | 70 - .../workspaces/locales/translated/ja_JP.json | 70 - .../workspaces/locales/translated/ko_KR.json | 70 - .../workspaces/locales/translated/nl_NL.json | 70 - .../workspaces/locales/translated/no_NO.json | 70 - .../workspaces/locales/translated/pl_PL.json | 70 - .../workspaces/locales/translated/pt_BR.json | 70 - .../workspaces/locales/translated/pt_PT.json | 70 - .../workspaces/locales/translated/ro_RO.json | 70 - .../workspaces/locales/translated/ru_RU.json | 70 - .../workspaces/locales/translated/sr_SP.json | 70 - .../workspaces/locales/translated/sv_SE.json | 70 - .../workspaces/locales/translated/th_TH.json | 70 - .../workspaces/locales/translated/tr_TR.json | 70 - .../workspaces/locales/translated/uk_UA.json | 70 - .../workspaces/locales/translated/vi_VN.json | 70 - .../workspaces/locales/translated/zh_CN.json | 70 - .../workspaces/locales/translated/zh_TW.json | 70 - plugins/workspaces/manifest.json | 45 - .../mysql/0001_adopt_user_workspaces.sql | 19 - .../postgres/0001_adopt_user_workspaces.sql | 20 - plugins/workspaces/migrations/snapshot.json | 69 - .../sqlite/0001_adopt_user_workspaces.sql | 20 - plugins/workspaces/package.json | 46 - plugins/workspaces/src/backend/index.ts | 49 - plugins/workspaces/src/backend/repository.ts | 252 -- plugins/workspaces/src/backend/routes.ts | 576 --- plugins/workspaces/src/backend/tables.ts | 45 - .../src/frontend/WorkspacesPanel.tsx | 656 --- plugins/workspaces/src/frontend/index.tsx | 96 - plugins/workspaces/src/frontend/types.ts | 29 - .../workspaces/src/frontend/workspaces-api.ts | 38 - .../workspaces/tests/backend/activate.test.ts | 76 - plugins/workspaces/tests/backend/helpers.ts | 103 - .../tests/backend/migrations.test.ts | 141 - .../tests/backend/repository.test.ts | 64 - .../workspaces/tests/backend/routes.test.ts | 203 - .../tests/frontend/activate.test.tsx | 216 - .../tests/frontend/workspaces-api.test.ts | 57 - plugins/workspaces/tsconfig.json | 4 - plugins/workspaces/vitest.config.ts | 3 - scripts/apply-plugin-patches.cjs | 26 - scripts/build-plugins.cjs | 55 +- scripts/build-upgrade-fixture.ts | 159 - scripts/bundled-plugins.test.ts | 84 +- scripts/check-core-plugin-ids.cjs | 108 +- scripts/check-core-plugin-ids.test.ts | 36 +- scripts/check-plugin-locales.cjs | 228 - scripts/check-plugin-locales.test.ts | 127 - scripts/check-plugin-uses.cjs | 153 - scripts/check-plugin-uses.test.ts | 70 - scripts/check-test-locations.cjs | 11 +- scripts/check-test-locations.test.ts | 7 +- scripts/crowdin-pretranslate.cjs | 6 +- scripts/crowdin-pretranslate.test.ts | 8 +- scripts/install-native-plugin-deps.cjs | 40 - scripts/lib/bundled-plugins.cjs | 41 +- scripts/lib/upgrade-sql.mjs | 124 - scripts/patch-guacamole-lite.test.ts | 161 - scripts/plugin-cli-bootstrap.test.ts | 22 +- scripts/run-plugin-tests.cjs | 85 - scripts/upgrade-check.compose.yml | 35 - scripts/upgrade-check.sh | 62 - scripts/upgrade-sql.test.ts | 44 - scripts/validate-plugin-manifest.cjs | 94 - scripts/validate-plugin-manifest.test.ts | 97 - scripts/vite-plugin-termix-plugins.mjs | 29 +- src/backend/starter.ts | 20 +- src/backend/tests/fixtures/upgrade/db.sqlite | Bin 909312 -> 0 bytes .../d4-user-admin/d4-session.cast | 1 - .../session_recordings/guacamole/d4-rdp.guac | 1 - .../tests/fixtures/upgrade/fixture.env | 4 - src/backend/tests/fixtures/upgrade/rows.ts | 922 ---- .../tests/fixtures/upgrade/upgrade-mysql.sql | 1416 ------ .../fixtures/upgrade/upgrade-postgres.sql | 1482 ------ .../tests/hosts/connect/pipeline.test.ts | 12 - src/backend/tests/plugins/built-harness.ts | 225 - .../tests/plugins/built-plugins-boot.test.ts | 149 - .../tests/plugins/bundled-plugin.test.ts | 153 - .../tests/plugins/permissions-rbac.test.ts | 79 - src/backend/tests/plugins/remote-seed.ts | 146 - src/backend/tests/plugins/sdk-testing.test.ts | 2 +- .../tests/plugins/security/data.test.ts | 35 - .../tests/plugins/security/discovery.test.ts | 17 - .../plugins/upgrade-fixture-data.test.ts | 72 - .../tests/plugins/upgrade-fixture.test.ts | 741 --- .../upgrade/snippets-schema-migration.test.ts | 9 +- src/backend/utils/swagger.ts | 43 - src/ui/api/settings-api.ts | 3 - src/ui/plugin-host/loader.ts | 5 - src/ui/plugin-host/workspace-plugins.ts | 17 - .../tests/plugin-host/built-bundles.test.tsx | 140 - .../sidebar/host-recording-settings.test.tsx | 38 +- .../sidebar/host-sharing-settings.test.tsx | 21 +- tsconfig.json | 3 - tsconfig.plugins.json | 12 - 3167 files changed, 286 insertions(+), 440749 deletions(-) delete mode 100644 plugins/acme-ssl/.commitlintrc.json delete mode 100644 plugins/acme-ssl/.github/funding.yml delete mode 100644 plugins/acme-ssl/.github/pull_request_template.md delete mode 100644 plugins/acme-ssl/.gitignore delete mode 100644 plugins/acme-ssl/.husky/commit-msg delete mode 100644 plugins/acme-ssl/.husky/pre-commit delete mode 100644 plugins/acme-ssl/.prettierignore delete mode 100644 plugins/acme-ssl/.prettierrc delete mode 100644 plugins/acme-ssl/CHANGELOG.json delete mode 100644 plugins/acme-ssl/CONTRIBUTING.md delete mode 100644 plugins/acme-ssl/README.md delete mode 100644 plugins/acme-ssl/locales/en.json delete mode 100644 plugins/acme-ssl/locales/translated/af_ZA.json delete mode 100644 plugins/acme-ssl/locales/translated/ar_SA.json delete mode 100644 plugins/acme-ssl/locales/translated/bg_BG.json delete mode 100644 plugins/acme-ssl/locales/translated/bn_BD.json delete mode 100644 plugins/acme-ssl/locales/translated/ca_ES.json delete mode 100644 plugins/acme-ssl/locales/translated/cs_CZ.json delete mode 100644 plugins/acme-ssl/locales/translated/da_DK.json delete mode 100644 plugins/acme-ssl/locales/translated/de_DE.json delete mode 100644 plugins/acme-ssl/locales/translated/el_GR.json delete mode 100644 plugins/acme-ssl/locales/translated/es_ES.json delete mode 100644 plugins/acme-ssl/locales/translated/fi_FI.json delete mode 100644 plugins/acme-ssl/locales/translated/fr_FR.json delete mode 100644 plugins/acme-ssl/locales/translated/he_IL.json delete mode 100644 plugins/acme-ssl/locales/translated/hi_IN.json delete mode 100644 plugins/acme-ssl/locales/translated/hu_HU.json delete mode 100644 plugins/acme-ssl/locales/translated/id_ID.json delete mode 100644 plugins/acme-ssl/locales/translated/it_IT.json delete mode 100644 plugins/acme-ssl/locales/translated/ja_JP.json delete mode 100644 plugins/acme-ssl/locales/translated/ko_KR.json delete mode 100644 plugins/acme-ssl/locales/translated/nl_NL.json delete mode 100644 plugins/acme-ssl/locales/translated/no_NO.json delete mode 100644 plugins/acme-ssl/locales/translated/pl_PL.json delete mode 100644 plugins/acme-ssl/locales/translated/pt_BR.json delete mode 100644 plugins/acme-ssl/locales/translated/pt_PT.json delete mode 100644 plugins/acme-ssl/locales/translated/ro_RO.json delete mode 100644 plugins/acme-ssl/locales/translated/ru_RU.json delete mode 100644 plugins/acme-ssl/locales/translated/sr_SP.json delete mode 100644 plugins/acme-ssl/locales/translated/sv_SE.json delete mode 100644 plugins/acme-ssl/locales/translated/th_TH.json delete mode 100644 plugins/acme-ssl/locales/translated/tr_TR.json delete mode 100644 plugins/acme-ssl/locales/translated/uk_UA.json delete mode 100644 plugins/acme-ssl/locales/translated/vi_VN.json delete mode 100644 plugins/acme-ssl/locales/translated/zh_CN.json delete mode 100644 plugins/acme-ssl/locales/translated/zh_TW.json delete mode 100644 plugins/acme-ssl/manifest.json delete mode 100644 plugins/acme-ssl/package.json delete mode 100644 plugins/acme-ssl/src/backend/acme.ts delete mode 100644 plugins/acme-ssl/src/backend/cloudflare.ts delete mode 100644 plugins/acme-ssl/src/backend/index.ts delete mode 100644 plugins/acme-ssl/src/backend/renewal.ts delete mode 100644 plugins/acme-ssl/src/backend/routes.ts delete mode 100644 plugins/acme-ssl/src/backend/runner.ts delete mode 100644 plugins/acme-ssl/src/backend/settings.ts delete mode 100644 plugins/acme-ssl/src/frontend/AcmeStatusSetting.tsx delete mode 100644 plugins/acme-ssl/src/frontend/index.tsx delete mode 100644 plugins/acme-ssl/tests/backend/acme-ssl.test.ts delete mode 100644 plugins/acme-ssl/tests/backend/transport.test.ts delete mode 100644 plugins/acme-ssl/tests/frontend/activate.test.tsx delete mode 100644 plugins/acme-ssl/tsconfig.json delete mode 100644 plugins/acme-ssl/vitest.config.ts delete mode 100644 plugins/ai/.commitlintrc.json delete mode 100644 plugins/ai/.github/funding.yml delete mode 100644 plugins/ai/.github/pull_request_template.md delete mode 100644 plugins/ai/.gitignore delete mode 100644 plugins/ai/.husky/commit-msg delete mode 100644 plugins/ai/.husky/pre-commit delete mode 100644 plugins/ai/.prettierignore delete mode 100644 plugins/ai/.prettierrc delete mode 100644 plugins/ai/AGENTS-FEATURE.md delete mode 100644 plugins/ai/CHANGELOG.json delete mode 100644 plugins/ai/CONTRIBUTING.md delete mode 100644 plugins/ai/README.md delete mode 100644 plugins/ai/locales/en.json delete mode 100644 plugins/ai/locales/translated/af_ZA.json delete mode 100644 plugins/ai/locales/translated/ar_SA.json delete mode 100644 plugins/ai/locales/translated/bg_BG.json delete mode 100644 plugins/ai/locales/translated/bn_BD.json delete mode 100644 plugins/ai/locales/translated/ca_ES.json delete mode 100644 plugins/ai/locales/translated/cs_CZ.json delete mode 100644 plugins/ai/locales/translated/da_DK.json delete mode 100644 plugins/ai/locales/translated/de_DE.json delete mode 100644 plugins/ai/locales/translated/el_GR.json delete mode 100644 plugins/ai/locales/translated/es_ES.json delete mode 100644 plugins/ai/locales/translated/fi_FI.json delete mode 100644 plugins/ai/locales/translated/fr_FR.json delete mode 100644 plugins/ai/locales/translated/he_IL.json delete mode 100644 plugins/ai/locales/translated/hi_IN.json delete mode 100644 plugins/ai/locales/translated/hu_HU.json delete mode 100644 plugins/ai/locales/translated/id_ID.json delete mode 100644 plugins/ai/locales/translated/it_IT.json delete mode 100644 plugins/ai/locales/translated/ja_JP.json delete mode 100644 plugins/ai/locales/translated/ko_KR.json delete mode 100644 plugins/ai/locales/translated/nl_NL.json delete mode 100644 plugins/ai/locales/translated/no_NO.json delete mode 100644 plugins/ai/locales/translated/pl_PL.json delete mode 100644 plugins/ai/locales/translated/pt_BR.json delete mode 100644 plugins/ai/locales/translated/pt_PT.json delete mode 100644 plugins/ai/locales/translated/ro_RO.json delete mode 100644 plugins/ai/locales/translated/ru_RU.json delete mode 100644 plugins/ai/locales/translated/sr_SP.json delete mode 100644 plugins/ai/locales/translated/sv_SE.json delete mode 100644 plugins/ai/locales/translated/th_TH.json delete mode 100644 plugins/ai/locales/translated/tr_TR.json delete mode 100644 plugins/ai/locales/translated/uk_UA.json delete mode 100644 plugins/ai/locales/translated/vi_VN.json delete mode 100644 plugins/ai/locales/translated/zh_CN.json delete mode 100644 plugins/ai/locales/translated/zh_TW.json delete mode 100644 plugins/ai/manifest.json delete mode 100644 plugins/ai/migrations/mysql/0001_adopt_ai_tables.sql delete mode 100644 plugins/ai/migrations/mysql/0002_provider_sync.sql delete mode 100644 plugins/ai/migrations/mysql/0003_agent_sessions.sql delete mode 100644 plugins/ai/migrations/postgres/0001_adopt_ai_tables.sql delete mode 100644 plugins/ai/migrations/postgres/0002_provider_sync.sql delete mode 100644 plugins/ai/migrations/postgres/0003_agent_sessions.sql delete mode 100644 plugins/ai/migrations/snapshot.json delete mode 100644 plugins/ai/migrations/sqlite/0001_adopt_ai_tables.sql delete mode 100644 plugins/ai/migrations/sqlite/0002_provider_sync.sql delete mode 100644 plugins/ai/migrations/sqlite/0003_agent_sessions.sql delete mode 100644 plugins/ai/package.json delete mode 100644 plugins/ai/src/backend/agents/forwarding.ts delete mode 100644 plugins/ai/src/backend/agents/install.ts delete mode 100644 plugins/ai/src/backend/agents/remote-runner.ts delete mode 100644 plugins/ai/src/backend/agents/routes.ts delete mode 100644 plugins/ai/src/backend/agents/session-state.ts delete mode 100644 plugins/ai/src/backend/agents/store.ts delete mode 100644 plugins/ai/src/backend/agents/types.ts delete mode 100644 plugins/ai/src/backend/agents/workspace.ts delete mode 100644 plugins/ai/src/backend/context.ts delete mode 100644 plugins/ai/src/backend/egress.ts delete mode 100644 plugins/ai/src/backend/engine.ts delete mode 100644 plugins/ai/src/backend/errors.ts delete mode 100644 plugins/ai/src/backend/gating.ts delete mode 100644 plugins/ai/src/backend/history.ts delete mode 100644 plugins/ai/src/backend/host-import.ts delete mode 100644 plugins/ai/src/backend/index.ts delete mode 100644 plugins/ai/src/backend/providers/anthropic.ts delete mode 100644 plugins/ai/src/backend/providers/gemini.ts delete mode 100644 plugins/ai/src/backend/providers/http.ts delete mode 100644 plugins/ai/src/backend/providers/ollama.ts delete mode 100644 plugins/ai/src/backend/providers/openai.ts delete mode 100644 plugins/ai/src/backend/providers/registry.ts delete mode 100644 plugins/ai/src/backend/providers/types.ts delete mode 100644 plugins/ai/src/backend/redaction.ts delete mode 100644 plugins/ai/src/backend/repository.ts delete mode 100644 plugins/ai/src/backend/routes.ts delete mode 100644 plugins/ai/src/backend/services.ts delete mode 100644 plugins/ai/src/backend/sync.ts delete mode 100644 plugins/ai/src/backend/tables.ts delete mode 100644 plugins/ai/src/backend/tools/catalog.ts delete mode 100644 plugins/ai/src/backend/tools/command-allowlist.ts delete mode 100644 plugins/ai/src/backend/tools/executor.ts delete mode 100644 plugins/ai/src/backend/tools/propose-tools.ts delete mode 100644 plugins/ai/src/backend/tools/read-tools.ts delete mode 100644 plugins/ai/src/backend/tools/types.ts delete mode 100644 plugins/ai/src/frontend/AiAssistantStep.tsx delete mode 100644 plugins/ai/src/frontend/AiMessage.tsx delete mode 100644 plugins/ai/src/frontend/AiPanel.tsx delete mode 100644 plugins/ai/src/frontend/AiProviderSettings.tsx delete mode 100644 plugins/ai/src/frontend/AiToolCall.tsx delete mode 100644 plugins/ai/src/frontend/ProposalCard.tsx delete mode 100644 plugins/ai/src/frontend/agents/AgentComposer.tsx delete mode 100644 plugins/ai/src/frontend/agents/AgentPanel.tsx delete mode 100644 plugins/ai/src/frontend/agents/Attachments.tsx delete mode 100644 plugins/ai/src/frontend/agents/InstallRuntime.tsx delete mode 100644 plugins/ai/src/frontend/agents/SessionTools.tsx delete mode 100644 plugins/ai/src/frontend/agents/useAgentStream.ts delete mode 100644 plugins/ai/src/frontend/ai-api.ts delete mode 100644 plugins/ai/src/frontend/app-ref.ts delete mode 100644 plugins/ai/src/frontend/errors.ts delete mode 100644 plugins/ai/src/frontend/index.tsx delete mode 100644 plugins/ai/src/frontend/labels.ts delete mode 100644 plugins/ai/src/frontend/settings/ProvidersSetting.tsx delete mode 100644 plugins/ai/src/frontend/terminal/TerminalAiPanel.tsx delete mode 100644 plugins/ai/src/frontend/terminal/TerminalProposalCard.tsx delete mode 100644 plugins/ai/src/frontend/terminal/panel-geometry.ts delete mode 100644 plugins/ai/src/frontend/terminal/terminal-slot-types.ts delete mode 100644 plugins/ai/src/frontend/transcript.ts delete mode 100644 plugins/ai/src/frontend/use-ai-stream.ts delete mode 100644 plugins/ai/src/frontend/useMentions.ts delete mode 100644 plugins/ai/tests/backend/activate.test.ts delete mode 100644 plugins/ai/tests/backend/agents/access.test.ts delete mode 100644 plugins/ai/tests/backend/agents/agents.test.ts delete mode 100644 plugins/ai/tests/backend/agents/forwarding.test.ts delete mode 100644 plugins/ai/tests/backend/agents/install.test.ts delete mode 100644 plugins/ai/tests/backend/agents/queue.test.ts delete mode 100644 plugins/ai/tests/backend/agents/session-state.test.ts delete mode 100644 plugins/ai/tests/backend/agents/store.test.ts delete mode 100644 plugins/ai/tests/backend/agents/workspace.test.ts delete mode 100644 plugins/ai/tests/backend/command-history-service.test.ts delete mode 100644 plugins/ai/tests/backend/context.test.ts delete mode 100644 plugins/ai/tests/backend/egress.test.ts delete mode 100644 plugins/ai/tests/backend/engine.test.ts delete mode 100644 plugins/ai/tests/backend/gating.test.ts delete mode 100644 plugins/ai/tests/backend/helpers.ts delete mode 100644 plugins/ai/tests/backend/history.test.ts delete mode 100644 plugins/ai/tests/backend/migrations.test.ts delete mode 100644 plugins/ai/tests/backend/permissions.test.ts delete mode 100644 plugins/ai/tests/backend/providers/provider-http.test.ts delete mode 100644 plugins/ai/tests/backend/providers/providers.test.ts delete mode 100644 plugins/ai/tests/backend/redaction.test.ts delete mode 100644 plugins/ai/tests/backend/routes.test.ts delete mode 100644 plugins/ai/tests/backend/sync.test.ts delete mode 100644 plugins/ai/tests/backend/tools/command-allowlist.test.ts delete mode 100644 plugins/ai/tests/backend/tools/proposal-executor.test.ts delete mode 100644 plugins/ai/tests/backend/tools/readonly-command-tool.test.ts delete mode 100644 plugins/ai/tests/backend/tools/tool-catalog.test.ts delete mode 100644 plugins/ai/tests/frontend/AgentPanel.test.tsx delete mode 100644 plugins/ai/tests/frontend/AiProviderSettings.test.tsx delete mode 100644 plugins/ai/tests/frontend/activate.test.tsx delete mode 100644 plugins/ai/tests/frontend/agent-stream.test.tsx delete mode 100644 plugins/ai/tests/frontend/labels.test.ts delete mode 100644 plugins/ai/tests/frontend/transcript.test.ts delete mode 100644 plugins/ai/tsconfig.json delete mode 100644 plugins/ai/vitest.config.ts delete mode 100644 plugins/alerts/.commitlintrc.json delete mode 100644 plugins/alerts/.github/funding.yml delete mode 100644 plugins/alerts/.github/pull_request_template.md delete mode 100644 plugins/alerts/.gitignore delete mode 100644 plugins/alerts/.husky/commit-msg delete mode 100644 plugins/alerts/.husky/pre-commit delete mode 100644 plugins/alerts/.prettierignore delete mode 100644 plugins/alerts/.prettierrc delete mode 100644 plugins/alerts/CHANGELOG.json delete mode 100644 plugins/alerts/CONTRIBUTING.md delete mode 100644 plugins/alerts/README.md delete mode 100644 plugins/alerts/locales/en.json delete mode 100644 plugins/alerts/locales/translated/af_ZA.json delete mode 100644 plugins/alerts/locales/translated/ar_SA.json delete mode 100644 plugins/alerts/locales/translated/bg_BG.json delete mode 100644 plugins/alerts/locales/translated/bn_BD.json delete mode 100644 plugins/alerts/locales/translated/ca_ES.json delete mode 100644 plugins/alerts/locales/translated/cs_CZ.json delete mode 100644 plugins/alerts/locales/translated/da_DK.json delete mode 100644 plugins/alerts/locales/translated/de_DE.json delete mode 100644 plugins/alerts/locales/translated/el_GR.json delete mode 100644 plugins/alerts/locales/translated/es_ES.json delete mode 100644 plugins/alerts/locales/translated/fi_FI.json delete mode 100644 plugins/alerts/locales/translated/fr_FR.json delete mode 100644 plugins/alerts/locales/translated/he_IL.json delete mode 100644 plugins/alerts/locales/translated/hi_IN.json delete mode 100644 plugins/alerts/locales/translated/hu_HU.json delete mode 100644 plugins/alerts/locales/translated/id_ID.json delete mode 100644 plugins/alerts/locales/translated/it_IT.json delete mode 100644 plugins/alerts/locales/translated/ja_JP.json delete mode 100644 plugins/alerts/locales/translated/ko_KR.json delete mode 100644 plugins/alerts/locales/translated/nl_NL.json delete mode 100644 plugins/alerts/locales/translated/no_NO.json delete mode 100644 plugins/alerts/locales/translated/pl_PL.json delete mode 100644 plugins/alerts/locales/translated/pt_BR.json delete mode 100644 plugins/alerts/locales/translated/pt_PT.json delete mode 100644 plugins/alerts/locales/translated/ro_RO.json delete mode 100644 plugins/alerts/locales/translated/ru_RU.json delete mode 100644 plugins/alerts/locales/translated/sr_SP.json delete mode 100644 plugins/alerts/locales/translated/sv_SE.json delete mode 100644 plugins/alerts/locales/translated/th_TH.json delete mode 100644 plugins/alerts/locales/translated/tr_TR.json delete mode 100644 plugins/alerts/locales/translated/uk_UA.json delete mode 100644 plugins/alerts/locales/translated/vi_VN.json delete mode 100644 plugins/alerts/locales/translated/zh_CN.json delete mode 100644 plugins/alerts/locales/translated/zh_TW.json delete mode 100644 plugins/alerts/manifest.json delete mode 100644 plugins/alerts/migrations/mysql/0001_create_alerts.sql delete mode 100644 plugins/alerts/migrations/postgres/0001_create_alerts.sql delete mode 100644 plugins/alerts/migrations/snapshot.json delete mode 100644 plugins/alerts/migrations/sqlite/0001_create_alerts.sql delete mode 100644 plugins/alerts/package.json delete mode 100644 plugins/alerts/src/backend/announcements.ts delete mode 100644 plugins/alerts/src/backend/hub.ts delete mode 100644 plugins/alerts/src/backend/index.ts delete mode 100644 plugins/alerts/src/backend/mail.ts delete mode 100644 plugins/alerts/src/backend/repository.ts delete mode 100644 plugins/alerts/src/backend/routes.ts delete mode 100644 plugins/alerts/src/backend/senders.ts delete mode 100644 plugins/alerts/src/backend/stream.ts delete mode 100644 plugins/alerts/src/backend/tables.ts delete mode 100644 plugins/alerts/src/frontend/AlertFeedWidget.tsx delete mode 100644 plugins/alerts/src/frontend/AlertList.tsx delete mode 100644 plugins/alerts/src/frontend/AlertToaster.tsx delete mode 100644 plugins/alerts/src/frontend/AlertsView.tsx delete mode 100644 plugins/alerts/src/frontend/ChannelDialog.tsx delete mode 100644 plugins/alerts/src/frontend/ChannelsView.tsx delete mode 100644 plugins/alerts/src/frontend/InboxView.tsx delete mode 100644 plugins/alerts/src/frontend/RulesView.tsx delete mode 100644 plugins/alerts/src/frontend/api.ts delete mode 100644 plugins/alerts/src/frontend/channel-config.ts delete mode 100644 plugins/alerts/src/frontend/format.ts delete mode 100644 plugins/alerts/src/frontend/index.tsx delete mode 100644 plugins/alerts/src/frontend/popups.ts delete mode 100644 plugins/alerts/src/frontend/sections.ts delete mode 100644 plugins/alerts/src/frontend/store.ts delete mode 100644 plugins/alerts/src/types.ts delete mode 100644 plugins/alerts/tests/backend/helpers.ts delete mode 100644 plugins/alerts/tests/backend/hub.test.ts delete mode 100644 plugins/alerts/tests/backend/migrations.test.ts delete mode 100644 plugins/alerts/tests/backend/routes.test.ts delete mode 100644 plugins/alerts/tests/backend/senders.test.ts delete mode 100644 plugins/alerts/tests/frontend/activate.test.tsx delete mode 100644 plugins/alerts/tests/frontend/store.test.ts delete mode 100644 plugins/alerts/tsconfig.json delete mode 100644 plugins/alerts/vitest.config.ts delete mode 100644 plugins/automations/.commitlintrc.json delete mode 100644 plugins/automations/.github/funding.yml delete mode 100644 plugins/automations/.github/pull_request_template.md delete mode 100644 plugins/automations/.gitignore delete mode 100644 plugins/automations/.husky/commit-msg delete mode 100644 plugins/automations/.husky/pre-commit delete mode 100644 plugins/automations/.prettierignore delete mode 100644 plugins/automations/.prettierrc delete mode 100644 plugins/automations/CHANGELOG.json delete mode 100644 plugins/automations/CONTRIBUTING.md delete mode 100644 plugins/automations/README.md delete mode 100644 plugins/automations/locales/en.json delete mode 100644 plugins/automations/locales/translated/af_ZA.json delete mode 100644 plugins/automations/locales/translated/ar_SA.json delete mode 100644 plugins/automations/locales/translated/bg_BG.json delete mode 100644 plugins/automations/locales/translated/bn_BD.json delete mode 100644 plugins/automations/locales/translated/ca_ES.json delete mode 100644 plugins/automations/locales/translated/cs_CZ.json delete mode 100644 plugins/automations/locales/translated/da_DK.json delete mode 100644 plugins/automations/locales/translated/de_DE.json delete mode 100644 plugins/automations/locales/translated/el_GR.json delete mode 100644 plugins/automations/locales/translated/es_ES.json delete mode 100644 plugins/automations/locales/translated/fi_FI.json delete mode 100644 plugins/automations/locales/translated/fr_FR.json delete mode 100644 plugins/automations/locales/translated/he_IL.json delete mode 100644 plugins/automations/locales/translated/hi_IN.json delete mode 100644 plugins/automations/locales/translated/hu_HU.json delete mode 100644 plugins/automations/locales/translated/id_ID.json delete mode 100644 plugins/automations/locales/translated/it_IT.json delete mode 100644 plugins/automations/locales/translated/ja_JP.json delete mode 100644 plugins/automations/locales/translated/ko_KR.json delete mode 100644 plugins/automations/locales/translated/nl_NL.json delete mode 100644 plugins/automations/locales/translated/no_NO.json delete mode 100644 plugins/automations/locales/translated/pl_PL.json delete mode 100644 plugins/automations/locales/translated/pt_BR.json delete mode 100644 plugins/automations/locales/translated/pt_PT.json delete mode 100644 plugins/automations/locales/translated/ro_RO.json delete mode 100644 plugins/automations/locales/translated/ru_RU.json delete mode 100644 plugins/automations/locales/translated/sr_SP.json delete mode 100644 plugins/automations/locales/translated/sv_SE.json delete mode 100644 plugins/automations/locales/translated/th_TH.json delete mode 100644 plugins/automations/locales/translated/tr_TR.json delete mode 100644 plugins/automations/locales/translated/uk_UA.json delete mode 100644 plugins/automations/locales/translated/vi_VN.json delete mode 100644 plugins/automations/locales/translated/zh_CN.json delete mode 100644 plugins/automations/locales/translated/zh_TW.json delete mode 100644 plugins/automations/manifest.json delete mode 100644 plugins/automations/migrations/mysql/0001_adopt_automations.sql delete mode 100644 plugins/automations/migrations/mysql/0002_unlink_channels.sql delete mode 100644 plugins/automations/migrations/mysql/0003_host_maintenance.sql delete mode 100644 plugins/automations/migrations/mysql/0004_rename_maintenance_index.sql delete mode 100644 plugins/automations/migrations/postgres/0001_adopt_automations.sql delete mode 100644 plugins/automations/migrations/postgres/0002_unlink_channels.sql delete mode 100644 plugins/automations/migrations/postgres/0003_host_maintenance.sql delete mode 100644 plugins/automations/migrations/postgres/0004_rename_maintenance_index.sql delete mode 100644 plugins/automations/migrations/snapshot.json delete mode 100644 plugins/automations/migrations/sqlite/0001_adopt_automations.sql delete mode 100644 plugins/automations/migrations/sqlite/0002_unlink_channels.sql delete mode 100644 plugins/automations/migrations/sqlite/0003_host_maintenance.sql delete mode 100644 plugins/automations/migrations/sqlite/0004_rename_maintenance_index.sql delete mode 100644 plugins/automations/package.json delete mode 100644 plugins/automations/src/backend/actions/host-targets.ts delete mode 100644 plugins/automations/src/backend/actions/index.ts delete mode 100644 plugins/automations/src/backend/actions/types.ts delete mode 100644 plugins/automations/src/backend/conditions.ts delete mode 100644 plugins/automations/src/backend/cron.ts delete mode 100644 plugins/automations/src/backend/deps.ts delete mode 100644 plugins/automations/src/backend/docker-watcher.ts delete mode 100644 plugins/automations/src/backend/engine.ts delete mode 100644 plugins/automations/src/backend/headless-viewer.ts delete mode 100644 plugins/automations/src/backend/index.ts delete mode 100644 plugins/automations/src/backend/maintenance-repository.ts delete mode 100644 plugins/automations/src/backend/maintenance-routes.ts delete mode 100644 plugins/automations/src/backend/maintenance-service.ts delete mode 100644 plugins/automations/src/backend/maintenance-time.ts delete mode 100644 plugins/automations/src/backend/repository.ts delete mode 100644 plugins/automations/src/backend/requirements.ts delete mode 100644 plugins/automations/src/backend/routes.ts delete mode 100644 plugins/automations/src/backend/scheduler.ts delete mode 100644 plugins/automations/src/backend/service.ts delete mode 100644 plugins/automations/src/backend/tables.ts delete mode 100644 plugins/automations/src/backend/template.ts delete mode 100644 plugins/automations/src/backend/triggers.ts delete mode 100644 plugins/automations/src/backend/validate.ts delete mode 100644 plugins/automations/src/frontend/AutomationsPanel.tsx delete mode 100644 plugins/automations/src/frontend/HostMaintenance.tsx delete mode 100644 plugins/automations/src/frontend/automations-api.ts delete mode 100644 plugins/automations/src/frontend/automations/AutomationEditor.tsx delete mode 100644 plugins/automations/src/frontend/automations/HostSelectorField.tsx delete mode 100644 plugins/automations/src/frontend/automations/StepBlock.tsx delete mode 100644 plugins/automations/src/frontend/automations/TriggerCard.tsx delete mode 100644 plugins/automations/src/frontend/automations/editor-types.ts delete mode 100644 plugins/automations/src/frontend/index.tsx delete mode 100644 plugins/automations/src/frontend/maintenance-store.ts delete mode 100644 plugins/automations/src/maintenance.ts delete mode 100644 plugins/automations/src/types.ts delete mode 100644 plugins/automations/tests/backend/activate.test.ts delete mode 100644 plugins/automations/tests/backend/conditions.test.ts delete mode 100644 plugins/automations/tests/backend/cron.test.ts delete mode 100644 plugins/automations/tests/backend/engine-runtime.test.ts delete mode 100644 plugins/automations/tests/backend/engine.test.ts delete mode 100644 plugins/automations/tests/backend/helpers.ts delete mode 100644 plugins/automations/tests/backend/maintenance-scheduler.test.ts delete mode 100644 plugins/automations/tests/backend/maintenance-time.test.ts delete mode 100644 plugins/automations/tests/backend/maintenance.test.ts delete mode 100644 plugins/automations/tests/backend/migrations.test.ts delete mode 100644 plugins/automations/tests/backend/permissions.test.ts delete mode 100644 plugins/automations/tests/backend/routes.test.ts delete mode 100644 plugins/automations/tests/backend/sudo-capability.test.ts delete mode 100644 plugins/automations/tests/backend/template.test.ts delete mode 100644 plugins/automations/tests/backend/triggers.test.ts delete mode 100644 plugins/automations/tests/frontend/activate.test.tsx delete mode 100644 plugins/automations/tests/frontend/automations-editor.test.ts delete mode 100644 plugins/automations/tests/frontend/maintenance.test.tsx delete mode 100644 plugins/automations/tests/frontend/panel.test.tsx delete mode 100644 plugins/automations/tsconfig.json delete mode 100644 plugins/automations/vitest.config.ts delete mode 100644 plugins/docker/.commitlintrc.json delete mode 100644 plugins/docker/.github/funding.yml delete mode 100644 plugins/docker/.github/pull_request_template.md delete mode 100644 plugins/docker/.gitignore delete mode 100644 plugins/docker/.husky/commit-msg delete mode 100644 plugins/docker/.husky/pre-commit delete mode 100644 plugins/docker/.prettierignore delete mode 100644 plugins/docker/.prettierrc delete mode 100644 plugins/docker/CHANGELOG.json delete mode 100644 plugins/docker/CONTRIBUTING.md delete mode 100644 plugins/docker/README.md delete mode 100644 plugins/docker/locales/en.json delete mode 100644 plugins/docker/locales/translated/af_ZA.json delete mode 100644 plugins/docker/locales/translated/ar_SA.json delete mode 100644 plugins/docker/locales/translated/bg_BG.json delete mode 100644 plugins/docker/locales/translated/bn_BD.json delete mode 100644 plugins/docker/locales/translated/ca_ES.json delete mode 100644 plugins/docker/locales/translated/cs_CZ.json delete mode 100644 plugins/docker/locales/translated/da_DK.json delete mode 100644 plugins/docker/locales/translated/de_DE.json delete mode 100644 plugins/docker/locales/translated/el_GR.json delete mode 100644 plugins/docker/locales/translated/es_ES.json delete mode 100644 plugins/docker/locales/translated/fi_FI.json delete mode 100644 plugins/docker/locales/translated/fr_FR.json delete mode 100644 plugins/docker/locales/translated/he_IL.json delete mode 100644 plugins/docker/locales/translated/hi_IN.json delete mode 100644 plugins/docker/locales/translated/hu_HU.json delete mode 100644 plugins/docker/locales/translated/id_ID.json delete mode 100644 plugins/docker/locales/translated/it_IT.json delete mode 100644 plugins/docker/locales/translated/ja_JP.json delete mode 100644 plugins/docker/locales/translated/ko_KR.json delete mode 100644 plugins/docker/locales/translated/nl_NL.json delete mode 100644 plugins/docker/locales/translated/no_NO.json delete mode 100644 plugins/docker/locales/translated/pl_PL.json delete mode 100644 plugins/docker/locales/translated/pt_BR.json delete mode 100644 plugins/docker/locales/translated/pt_PT.json delete mode 100644 plugins/docker/locales/translated/ro_RO.json delete mode 100644 plugins/docker/locales/translated/ru_RU.json delete mode 100644 plugins/docker/locales/translated/sr_SP.json delete mode 100644 plugins/docker/locales/translated/sv_SE.json delete mode 100644 plugins/docker/locales/translated/th_TH.json delete mode 100644 plugins/docker/locales/translated/tr_TR.json delete mode 100644 plugins/docker/locales/translated/uk_UA.json delete mode 100644 plugins/docker/locales/translated/vi_VN.json delete mode 100644 plugins/docker/locales/translated/zh_CN.json delete mode 100644 plugins/docker/locales/translated/zh_TW.json delete mode 100644 plugins/docker/manifest.json delete mode 100644 plugins/docker/package.json delete mode 100644 plugins/docker/src/backend/connect.ts delete mode 100644 plugins/docker/src/backend/console.ts delete mode 100644 plugins/docker/src/backend/container-runtime.ts delete mode 100644 plugins/docker/src/backend/container-state.ts delete mode 100644 plugins/docker/src/backend/helpers.ts delete mode 100644 plugins/docker/src/backend/host-settings.ts delete mode 100644 plugins/docker/src/backend/index.ts delete mode 100644 plugins/docker/src/backend/routes.ts delete mode 100644 plugins/docker/src/backend/services.ts delete mode 100644 plugins/docker/src/backend/sessions.ts delete mode 100644 plugins/docker/src/frontend/DockerApp.tsx delete mode 100644 plugins/docker/src/frontend/DockerManager.tsx delete mode 100644 plugins/docker/src/frontend/DockerWidget.tsx delete mode 100644 plugins/docker/src/frontend/DockerWidgetEditForm.tsx delete mode 100644 plugins/docker/src/frontend/components/ConsoleTerminal.tsx delete mode 100644 plugins/docker/src/frontend/components/ContainerCard.tsx delete mode 100644 plugins/docker/src/frontend/components/ContainerDetail.tsx delete mode 100644 plugins/docker/src/frontend/components/ContainerList.tsx delete mode 100644 plugins/docker/src/frontend/components/ContainerStats.tsx delete mode 100644 plugins/docker/src/frontend/components/ContainerTable.tsx delete mode 100644 plugins/docker/src/frontend/components/LogViewer.tsx delete mode 100644 plugins/docker/src/frontend/components/console-look.ts delete mode 100644 plugins/docker/src/frontend/docker-api.ts delete mode 100644 plugins/docker/src/frontend/error-message.ts delete mode 100644 plugins/docker/src/frontend/homepage.ts delete mode 100644 plugins/docker/src/frontend/index.tsx delete mode 100644 plugins/docker/src/frontend/types.ts delete mode 100644 plugins/docker/tests/backend/console.test.ts delete mode 100644 plugins/docker/tests/backend/fake-ssh.ts delete mode 100644 plugins/docker/tests/backend/host-settings.test.ts delete mode 100644 plugins/docker/tests/backend/routes.test.ts delete mode 100644 plugins/docker/tests/backend/server.ts delete mode 100644 plugins/docker/tests/backend/services.test.ts delete mode 100644 plugins/docker/tests/frontend/DockerManager.test.tsx delete mode 100644 plugins/docker/tests/frontend/activate.test.tsx delete mode 100644 plugins/docker/tests/frontend/components/console-look.test.tsx delete mode 100644 plugins/docker/tsconfig.json delete mode 100644 plugins/docker/vitest.config.ts delete mode 100644 plugins/file-manager/.commitlintrc.json delete mode 100644 plugins/file-manager/.github/funding.yml delete mode 100644 plugins/file-manager/.github/pull_request_template.md delete mode 100644 plugins/file-manager/.gitignore delete mode 100644 plugins/file-manager/.husky/commit-msg delete mode 100644 plugins/file-manager/.husky/pre-commit delete mode 100644 plugins/file-manager/.prettierignore delete mode 100644 plugins/file-manager/.prettierrc delete mode 100644 plugins/file-manager/CHANGELOG.json delete mode 100644 plugins/file-manager/CONTRIBUTING.md delete mode 100644 plugins/file-manager/README.md delete mode 100644 plugins/file-manager/locales/en.json delete mode 100644 plugins/file-manager/locales/translated/af_ZA.json delete mode 100644 plugins/file-manager/locales/translated/ar_SA.json delete mode 100644 plugins/file-manager/locales/translated/bg_BG.json delete mode 100644 plugins/file-manager/locales/translated/bn_BD.json delete mode 100644 plugins/file-manager/locales/translated/ca_ES.json delete mode 100644 plugins/file-manager/locales/translated/cs_CZ.json delete mode 100644 plugins/file-manager/locales/translated/da_DK.json delete mode 100644 plugins/file-manager/locales/translated/de_DE.json delete mode 100644 plugins/file-manager/locales/translated/el_GR.json delete mode 100644 plugins/file-manager/locales/translated/es_ES.json delete mode 100644 plugins/file-manager/locales/translated/fi_FI.json delete mode 100644 plugins/file-manager/locales/translated/fr_FR.json delete mode 100644 plugins/file-manager/locales/translated/he_IL.json delete mode 100644 plugins/file-manager/locales/translated/hi_IN.json delete mode 100644 plugins/file-manager/locales/translated/hu_HU.json delete mode 100644 plugins/file-manager/locales/translated/id_ID.json delete mode 100644 plugins/file-manager/locales/translated/it_IT.json delete mode 100644 plugins/file-manager/locales/translated/ja_JP.json delete mode 100644 plugins/file-manager/locales/translated/ko_KR.json delete mode 100644 plugins/file-manager/locales/translated/nl_NL.json delete mode 100644 plugins/file-manager/locales/translated/no_NO.json delete mode 100644 plugins/file-manager/locales/translated/pl_PL.json delete mode 100644 plugins/file-manager/locales/translated/pt_BR.json delete mode 100644 plugins/file-manager/locales/translated/pt_PT.json delete mode 100644 plugins/file-manager/locales/translated/ro_RO.json delete mode 100644 plugins/file-manager/locales/translated/ru_RU.json delete mode 100644 plugins/file-manager/locales/translated/sr_SP.json delete mode 100644 plugins/file-manager/locales/translated/sv_SE.json delete mode 100644 plugins/file-manager/locales/translated/th_TH.json delete mode 100644 plugins/file-manager/locales/translated/tr_TR.json delete mode 100644 plugins/file-manager/locales/translated/uk_UA.json delete mode 100644 plugins/file-manager/locales/translated/vi_VN.json delete mode 100644 plugins/file-manager/locales/translated/zh_CN.json delete mode 100644 plugins/file-manager/locales/translated/zh_TW.json delete mode 100644 plugins/file-manager/manifest.json delete mode 100644 plugins/file-manager/migrations/mysql/0001_adopt_file_manager_tables.sql delete mode 100644 plugins/file-manager/migrations/postgres/0001_adopt_file_manager_tables.sql delete mode 100644 plugins/file-manager/migrations/snapshot.json delete mode 100644 plugins/file-manager/migrations/sqlite/0001_adopt_file_manager_tables.sql delete mode 100644 plugins/file-manager/package.json delete mode 100644 plugins/file-manager/src/backend/action-routes.ts delete mode 100644 plugins/file-manager/src/backend/bookmark-routes.ts delete mode 100644 plugins/file-manager/src/backend/content-routes.ts delete mode 100644 plugins/file-manager/src/backend/direct-transfer-routing.ts delete mode 100644 plugins/file-manager/src/backend/download-routes.ts delete mode 100644 plugins/file-manager/src/backend/download-stream.ts delete mode 100644 plugins/file-manager/src/backend/error-message.ts delete mode 100644 plugins/file-manager/src/backend/home-directory.ts delete mode 100644 plugins/file-manager/src/backend/host-identity.ts delete mode 100644 plugins/file-manager/src/backend/index.ts delete mode 100644 plugins/file-manager/src/backend/list-routes.ts delete mode 100644 plugins/file-manager/src/backend/operation-commands.ts delete mode 100644 plugins/file-manager/src/backend/operation-routes.ts delete mode 100644 plugins/file-manager/src/backend/plugin-ctx.ts delete mode 100644 plugins/file-manager/src/backend/service.ts delete mode 100644 plugins/file-manager/src/backend/session.ts delete mode 100644 plugins/file-manager/src/backend/sftp-promisify.ts delete mode 100644 plugins/file-manager/src/backend/ssh.ts delete mode 100644 plugins/file-manager/src/backend/tables.ts delete mode 100644 plugins/file-manager/src/backend/transfer-engine.ts delete mode 100644 plugins/file-manager/src/backend/transfer-errors.ts delete mode 100644 plugins/file-manager/src/backend/transfer-host-utils.ts delete mode 100644 plugins/file-manager/src/backend/transfer-integrity.ts delete mode 100644 plugins/file-manager/src/backend/transfer-paths.ts delete mode 100644 plugins/file-manager/src/backend/transfer-routing.ts delete mode 100644 plugins/file-manager/src/backend/transfer-scan.ts delete mode 100644 plugins/file-manager/src/backend/transfer-segment-copy.ts delete mode 100644 plugins/file-manager/src/backend/transfer-sftp-dir.ts delete mode 100644 plugins/file-manager/src/backend/transfer-stats.ts delete mode 100644 plugins/file-manager/src/backend/transfer-tuning.ts delete mode 100644 plugins/file-manager/src/backend/trash-service.ts delete mode 100644 plugins/file-manager/src/backend/utils.ts delete mode 100644 plugins/file-manager/src/frontend/FileManager.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerApp.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerContextMenu.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerDialogs.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerGrid.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerSidebar.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerToolbar.tsx delete mode 100644 plugins/file-manager/src/frontend/FileManagerTrashDialog.tsx delete mode 100644 plugins/file-manager/src/frontend/LocalFileContextMenu.tsx delete mode 100644 plugins/file-manager/src/frontend/LocalFilePane.tsx delete mode 100644 plugins/file-manager/src/frontend/SftpTransferTab.tsx delete mode 100644 plugins/file-manager/src/frontend/SudoPasswordDialog.tsx delete mode 100644 plugins/file-manager/src/frontend/TransferMonitor.tsx delete mode 100644 plugins/file-manager/src/frontend/api/client.ts delete mode 100644 plugins/file-manager/src/frontend/api/file-manager-data-api.ts delete mode 100644 plugins/file-manager/src/frontend/api/hosts.ts delete mode 100644 plugins/file-manager/src/frontend/api/local-transfer-api.ts delete mode 100644 plugins/file-manager/src/frontend/api/ssh-file-operations-api.ts delete mode 100644 plugins/file-manager/src/frontend/api/transfer-api.ts delete mode 100644 plugins/file-manager/src/frontend/components/AudioPreview.tsx delete mode 100644 plugins/file-manager/src/frontend/components/CodeEditor.tsx delete mode 100644 plugins/file-manager/src/frontend/components/ColumnResizeHandle.tsx delete mode 100644 plugins/file-manager/src/frontend/components/ColumnVisibilityMenu.tsx delete mode 100644 plugins/file-manager/src/frontend/components/CompressDialog.tsx delete mode 100644 plugins/file-manager/src/frontend/components/DiffViewer.tsx delete mode 100644 plugins/file-manager/src/frontend/components/DiffWindow.tsx delete mode 100644 plugins/file-manager/src/frontend/components/DownloadProgressToast.tsx delete mode 100644 plugins/file-manager/src/frontend/components/DraggableWindow.tsx delete mode 100644 plugins/file-manager/src/frontend/components/FileViewer.tsx delete mode 100644 plugins/file-manager/src/frontend/components/FileWindow.tsx delete mode 100644 plugins/file-manager/src/frontend/components/FolderTree.tsx delete mode 100644 plugins/file-manager/src/frontend/components/ImagePreview.tsx delete mode 100644 plugins/file-manager/src/frontend/components/LocalTransferProgressToast.tsx delete mode 100644 plugins/file-manager/src/frontend/components/MarkdownRenderer.tsx delete mode 100644 plugins/file-manager/src/frontend/components/PaneResizeHandle.tsx delete mode 100644 plugins/file-manager/src/frontend/components/PdfPreview.tsx delete mode 100644 plugins/file-manager/src/frontend/components/PermissionsDialog.tsx delete mode 100644 plugins/file-manager/src/frontend/components/TerminalWindow.tsx delete mode 100644 plugins/file-manager/src/frontend/components/TransferProgressBar.tsx delete mode 100644 plugins/file-manager/src/frontend/components/TransferProgressToast.tsx delete mode 100644 plugins/file-manager/src/frontend/components/TransferToHostDialog.tsx delete mode 100644 plugins/file-manager/src/frontend/components/WindowManager.tsx delete mode 100644 plugins/file-manager/src/frontend/disk-info.ts delete mode 100644 plugins/file-manager/src/frontend/file-manager-types.ts delete mode 100644 plugins/file-manager/src/frontend/file-manager-utils.ts delete mode 100644 plugins/file-manager/src/frontend/file-preview-preload.ts delete mode 100644 plugins/file-manager/src/frontend/homepage/FileManagerWidget.tsx delete mode 100644 plugins/file-manager/src/frontend/homepage/FileManagerWidgetEditForm.tsx delete mode 100644 plugins/file-manager/src/frontend/homepage/homepage.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useDragAndDrop.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useDragToDesktop.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useDragToSystemDesktop.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useFileSelection.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useLocalTransfers.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/usePaneWidth.ts delete mode 100644 plugins/file-manager/src/frontend/hooks/useResizableColumns.ts delete mode 100644 plugins/file-manager/src/frontend/host-settings.ts delete mode 100644 plugins/file-manager/src/frontend/host-types.ts delete mode 100644 plugins/file-manager/src/frontend/index.tsx delete mode 100644 plugins/file-manager/src/frontend/lib/error-message.ts delete mode 100644 plugins/file-manager/src/frontend/lib/file-content-request-cache.ts delete mode 100644 plugins/file-manager/src/frontend/lib/file-list-request-cache.ts delete mode 100644 plugins/file-manager/src/frontend/lib/http-error.ts delete mode 100644 plugins/file-manager/src/frontend/lib/keyed-request-cache.ts delete mode 100644 plugins/file-manager/src/frontend/lib/local-files.ts delete mode 100644 plugins/file-manager/src/frontend/local-transfer-utils.ts delete mode 100644 plugins/file-manager/src/frontend/optimistic-file-list.ts delete mode 100644 plugins/file-manager/src/frontend/sftp-transfer-utils.ts delete mode 100644 plugins/file-manager/src/frontend/transferMetricsFormat.ts delete mode 100644 plugins/file-manager/src/frontend/transferNotificationStore.ts delete mode 100644 plugins/file-manager/src/frontend/transferProgressMonitor.tsx delete mode 100644 plugins/file-manager/tests/backend/download-routes.test.ts delete mode 100644 plugins/file-manager/tests/backend/download-stream-sftp.test.ts delete mode 100644 plugins/file-manager/tests/backend/download-stream.test.ts delete mode 100644 plugins/file-manager/tests/backend/helpers.ts delete mode 100644 plugins/file-manager/tests/backend/home-directory.test.ts delete mode 100644 plugins/file-manager/tests/backend/migrations.test.ts delete mode 100644 plugins/file-manager/tests/backend/route-paths.test.ts delete mode 100644 plugins/file-manager/tests/backend/routes.test.ts delete mode 100644 plugins/file-manager/tests/backend/transfer-profile-store.test.ts delete mode 100644 plugins/file-manager/tests/backend/trash-service.test.ts delete mode 100644 plugins/file-manager/tests/backend/upload-cancellation.test.ts delete mode 100644 plugins/file-manager/tests/backend/write-content.test.ts delete mode 100644 plugins/file-manager/tests/frontend/FileManagerGrid.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/FileManagerToolbar.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/LocalFilePane.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/SftpTransferTab.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/activate.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/api/ssh-file-operations-api.test.ts delete mode 100644 plugins/file-manager/tests/frontend/components/MarkdownRenderer.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/components/TransferProgressBar.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/components/download-progress.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/disk-info.test.ts delete mode 100644 plugins/file-manager/tests/frontend/file-manager-utils.test.ts delete mode 100644 plugins/file-manager/tests/frontend/file-preview-preload.test.ts delete mode 100644 plugins/file-manager/tests/frontend/hooks/useDragAndDrop.test.ts delete mode 100644 plugins/file-manager/tests/frontend/hooks/useFileSelection.test.ts delete mode 100644 plugins/file-manager/tests/frontend/hooks/usePaneWidth.test.ts delete mode 100644 plugins/file-manager/tests/frontend/hooks/useResizableColumns.test.ts delete mode 100644 plugins/file-manager/tests/frontend/lib/file-content-request-cache.test.ts delete mode 100644 plugins/file-manager/tests/frontend/lib/keyed-request-cache.test.ts delete mode 100644 plugins/file-manager/tests/frontend/local-transfer-utils.test.ts delete mode 100644 plugins/file-manager/tests/frontend/optimistic-file-list.test.ts delete mode 100644 plugins/file-manager/tests/frontend/progress-toast-context.test.tsx delete mode 100644 plugins/file-manager/tests/frontend/sftp-transfer-utils.test.ts delete mode 100644 plugins/file-manager/tests/frontend/transfer-progress-toast.test.ts delete mode 100644 plugins/file-manager/tsconfig.json delete mode 100644 plugins/file-manager/vitest.config.ts delete mode 100644 plugins/fleets/.commitlintrc.json delete mode 100644 plugins/fleets/.github/funding.yml delete mode 100644 plugins/fleets/.github/pull_request_template.md delete mode 100644 plugins/fleets/.gitignore delete mode 100644 plugins/fleets/.husky/commit-msg delete mode 100644 plugins/fleets/.husky/pre-commit delete mode 100644 plugins/fleets/.prettierignore delete mode 100644 plugins/fleets/.prettierrc delete mode 100644 plugins/fleets/CHANGELOG.json delete mode 100644 plugins/fleets/CONTRIBUTING.md delete mode 100644 plugins/fleets/README.md delete mode 100644 plugins/fleets/locales/en.json delete mode 100644 plugins/fleets/locales/translated/af_ZA.json delete mode 100644 plugins/fleets/locales/translated/ar_SA.json delete mode 100644 plugins/fleets/locales/translated/bg_BG.json delete mode 100644 plugins/fleets/locales/translated/bn_BD.json delete mode 100644 plugins/fleets/locales/translated/ca_ES.json delete mode 100644 plugins/fleets/locales/translated/cs_CZ.json delete mode 100644 plugins/fleets/locales/translated/da_DK.json delete mode 100644 plugins/fleets/locales/translated/de_DE.json delete mode 100644 plugins/fleets/locales/translated/el_GR.json delete mode 100644 plugins/fleets/locales/translated/es_ES.json delete mode 100644 plugins/fleets/locales/translated/fi_FI.json delete mode 100644 plugins/fleets/locales/translated/fr_FR.json delete mode 100644 plugins/fleets/locales/translated/he_IL.json delete mode 100644 plugins/fleets/locales/translated/hi_IN.json delete mode 100644 plugins/fleets/locales/translated/hu_HU.json delete mode 100644 plugins/fleets/locales/translated/id_ID.json delete mode 100644 plugins/fleets/locales/translated/it_IT.json delete mode 100644 plugins/fleets/locales/translated/ja_JP.json delete mode 100644 plugins/fleets/locales/translated/ko_KR.json delete mode 100644 plugins/fleets/locales/translated/nl_NL.json delete mode 100644 plugins/fleets/locales/translated/no_NO.json delete mode 100644 plugins/fleets/locales/translated/pl_PL.json delete mode 100644 plugins/fleets/locales/translated/pt_BR.json delete mode 100644 plugins/fleets/locales/translated/pt_PT.json delete mode 100644 plugins/fleets/locales/translated/ro_RO.json delete mode 100644 plugins/fleets/locales/translated/ru_RU.json delete mode 100644 plugins/fleets/locales/translated/sr_SP.json delete mode 100644 plugins/fleets/locales/translated/sv_SE.json delete mode 100644 plugins/fleets/locales/translated/th_TH.json delete mode 100644 plugins/fleets/locales/translated/tr_TR.json delete mode 100644 plugins/fleets/locales/translated/uk_UA.json delete mode 100644 plugins/fleets/locales/translated/vi_VN.json delete mode 100644 plugins/fleets/locales/translated/zh_CN.json delete mode 100644 plugins/fleets/locales/translated/zh_TW.json delete mode 100644 plugins/fleets/manifest.json delete mode 100644 plugins/fleets/migrations/mysql/0001_adopt_fleets.sql delete mode 100644 plugins/fleets/migrations/postgres/0001_adopt_fleets.sql delete mode 100644 plugins/fleets/migrations/snapshot.json delete mode 100644 plugins/fleets/migrations/sqlite/0001_adopt_fleets.sql delete mode 100644 plugins/fleets/package.json delete mode 100644 plugins/fleets/src/backend/index.ts delete mode 100644 plugins/fleets/src/backend/repository.ts delete mode 100644 plugins/fleets/src/backend/routes.ts delete mode 100644 plugins/fleets/src/backend/tables.ts delete mode 100644 plugins/fleets/src/frontend/FleetInventoryTab.tsx delete mode 100644 plugins/fleets/src/frontend/FleetsPanel.tsx delete mode 100644 plugins/fleets/src/frontend/fleets-api.ts delete mode 100644 plugins/fleets/src/frontend/helpers.ts delete mode 100644 plugins/fleets/src/frontend/index.tsx delete mode 100644 plugins/fleets/tests/backend/command-variables.test.ts delete mode 100644 plugins/fleets/tests/backend/helpers.ts delete mode 100644 plugins/fleets/tests/backend/migrations.test.ts delete mode 100644 plugins/fleets/tests/backend/repository.test.ts delete mode 100644 plugins/fleets/tests/backend/routes.test.ts delete mode 100644 plugins/fleets/tests/backend/service.test.ts delete mode 100644 plugins/fleets/tests/backend/sudo-capability.test.ts delete mode 100644 plugins/fleets/tests/frontend/activate.test.tsx delete mode 100644 plugins/fleets/tsconfig.json delete mode 100644 plugins/fleets/vitest.config.ts delete mode 100644 plugins/homepage/.commitlintrc.json delete mode 100644 plugins/homepage/.github/funding.yml delete mode 100644 plugins/homepage/.github/pull_request_template.md delete mode 100644 plugins/homepage/.gitignore delete mode 100644 plugins/homepage/.husky/commit-msg delete mode 100644 plugins/homepage/.husky/pre-commit delete mode 100644 plugins/homepage/.prettierignore delete mode 100644 plugins/homepage/.prettierrc delete mode 100644 plugins/homepage/CHANGELOG.json delete mode 100644 plugins/homepage/CONTRIBUTING.md delete mode 100644 plugins/homepage/README.md delete mode 100644 plugins/homepage/locales/en.json delete mode 100644 plugins/homepage/locales/translated/af_ZA.json delete mode 100644 plugins/homepage/locales/translated/ar_SA.json delete mode 100644 plugins/homepage/locales/translated/bg_BG.json delete mode 100644 plugins/homepage/locales/translated/bn_BD.json delete mode 100644 plugins/homepage/locales/translated/ca_ES.json delete mode 100644 plugins/homepage/locales/translated/cs_CZ.json delete mode 100644 plugins/homepage/locales/translated/da_DK.json delete mode 100644 plugins/homepage/locales/translated/de_DE.json delete mode 100644 plugins/homepage/locales/translated/el_GR.json delete mode 100644 plugins/homepage/locales/translated/es_ES.json delete mode 100644 plugins/homepage/locales/translated/fi_FI.json delete mode 100644 plugins/homepage/locales/translated/fr_FR.json delete mode 100644 plugins/homepage/locales/translated/he_IL.json delete mode 100644 plugins/homepage/locales/translated/hi_IN.json delete mode 100644 plugins/homepage/locales/translated/hu_HU.json delete mode 100644 plugins/homepage/locales/translated/id_ID.json delete mode 100644 plugins/homepage/locales/translated/it_IT.json delete mode 100644 plugins/homepage/locales/translated/ja_JP.json delete mode 100644 plugins/homepage/locales/translated/ko_KR.json delete mode 100644 plugins/homepage/locales/translated/nl_NL.json delete mode 100644 plugins/homepage/locales/translated/no_NO.json delete mode 100644 plugins/homepage/locales/translated/pl_PL.json delete mode 100644 plugins/homepage/locales/translated/pt_BR.json delete mode 100644 plugins/homepage/locales/translated/pt_PT.json delete mode 100644 plugins/homepage/locales/translated/ro_RO.json delete mode 100644 plugins/homepage/locales/translated/ru_RU.json delete mode 100644 plugins/homepage/locales/translated/sr_SP.json delete mode 100644 plugins/homepage/locales/translated/sv_SE.json delete mode 100644 plugins/homepage/locales/translated/th_TH.json delete mode 100644 plugins/homepage/locales/translated/tr_TR.json delete mode 100644 plugins/homepage/locales/translated/uk_UA.json delete mode 100644 plugins/homepage/locales/translated/vi_VN.json delete mode 100644 plugins/homepage/locales/translated/zh_CN.json delete mode 100644 plugins/homepage/locales/translated/zh_TW.json delete mode 100644 plugins/homepage/manifest.json delete mode 100644 plugins/homepage/migrations/mysql/0001_adopt_homepage_tables.sql delete mode 100644 plugins/homepage/migrations/postgres/0001_adopt_homepage_tables.sql delete mode 100644 plugins/homepage/migrations/snapshot.json delete mode 100644 plugins/homepage/migrations/sqlite/0001_adopt_homepage_tables.sql delete mode 100644 plugins/homepage/package.json delete mode 100644 plugins/homepage/src/backend/index.ts delete mode 100644 plugins/homepage/src/backend/layout-item-ids.ts delete mode 100644 plugins/homepage/src/backend/outbound-routes.ts delete mode 100644 plugins/homepage/src/backend/repository.ts delete mode 100644 plugins/homepage/src/backend/routes.ts delete mode 100644 plugins/homepage/src/backend/services.ts delete mode 100644 plugins/homepage/src/backend/tables.ts delete mode 100644 plugins/homepage/src/backend/url.ts delete mode 100644 plugins/homepage/src/frontend/DashboardHomepageView.tsx delete mode 100644 plugins/homepage/src/frontend/HomepageCanvas.tsx delete mode 100644 plugins/homepage/src/frontend/HomepagePreviewCard.tsx delete mode 100644 plugins/homepage/src/frontend/ServiceLinksCard.tsx delete mode 100644 plugins/homepage/src/frontend/api.ts delete mode 100644 plugins/homepage/src/frontend/canvas/CanvasDotBackground.tsx delete mode 100644 plugins/homepage/src/frontend/canvas/canvasGeometry.ts delete mode 100644 plugins/homepage/src/frontend/canvas/snapToGrid.ts delete mode 100644 plugins/homepage/src/frontend/canvas/useBoxDrag.ts delete mode 100644 plugins/homepage/src/frontend/canvas/useBoxResize.ts delete mode 100644 plugins/homepage/src/frontend/canvas/useCanvasInput.ts delete mode 100644 plugins/homepage/src/frontend/clock-timezone.ts delete mode 100644 plugins/homepage/src/frontend/dialogs/AddWidgetMenu.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/BookmarkListEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/CalendarEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/ClockEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/CountdownEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/CustomApiEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/DashboardLinksEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/FolderEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/HostGridEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/HostStatusEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/IframeEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/ImageWidgetEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/LinkTreeEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/MarkdownNotesEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/NotesEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/PingStatusEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/QuickConnectEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/RecentActivityEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/RssFeedEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/SearchBarEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/SearchLinksEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/ServiceGridEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/ServiceLinkEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/SingleHostEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/SshTerminalEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/SystemOverviewEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/TermixUptimeEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/TextBannerEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/WeatherEditForm.tsx delete mode 100644 plugins/homepage/src/frontend/dialogs/WidgetEditDialog.tsx delete mode 100644 plugins/homepage/src/frontend/index.tsx delete mode 100644 plugins/homepage/src/frontend/quick-connect-targets.ts delete mode 100644 plugins/homepage/src/frontend/toolbar/HomepageToolbar.tsx delete mode 100644 plugins/homepage/src/frontend/types.ts delete mode 100644 plugins/homepage/src/frontend/url.ts delete mode 100644 plugins/homepage/src/frontend/use-page-visible-interval.ts delete mode 100644 plugins/homepage/src/frontend/widgets/BookmarkListWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/CalendarWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/ClockWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/CountdownWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/CustomApiWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/DashboardLinksWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/FolderWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/HostGridWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/HostStatusWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/IframeWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/ImageWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/LinkTreeWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/MarkdownNotesWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/NotesWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/PingStatusWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/QuickConnectWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/RecentActivityWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/RssFeedWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/SearchBarWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/SearchLinksWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/ServiceGridWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/ServiceLinkWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/SshTerminalWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/SystemOverviewWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/TermixUptimeWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/TextBannerWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/WeatherWidget.tsx delete mode 100644 plugins/homepage/src/frontend/widgets/WidgetRegistry.ts delete mode 100644 plugins/homepage/src/frontend/widgets/WidgetShell.tsx delete mode 100644 plugins/homepage/tests/backend/helpers.ts delete mode 100644 plugins/homepage/tests/backend/layout-sync.test.ts delete mode 100644 plugins/homepage/tests/backend/migrations.test.ts delete mode 100644 plugins/homepage/tests/backend/outbound-routes.test.ts delete mode 100644 plugins/homepage/tests/backend/routes.test.ts delete mode 100644 plugins/homepage/tests/backend/services.test.ts delete mode 100644 plugins/homepage/tests/backend/url.test.ts delete mode 100644 plugins/homepage/tests/frontend/ClockTimezone.test.tsx delete mode 100644 plugins/homepage/tests/frontend/SystemOverviewWidget.test.tsx delete mode 100644 plugins/homepage/tests/frontend/WidgetEditDialogClock.test.tsx delete mode 100644 plugins/homepage/tests/frontend/activate.test.tsx delete mode 100644 plugins/homepage/tests/frontend/canvas/canvasGeometry.test.ts delete mode 100644 plugins/homepage/tests/frontend/canvas/snapToGrid.test.ts delete mode 100644 plugins/homepage/tsconfig.json delete mode 100644 plugins/homepage/vitest.config.ts delete mode 100644 plugins/host-metrics/.commitlintrc.json delete mode 100644 plugins/host-metrics/.github/funding.yml delete mode 100644 plugins/host-metrics/.github/pull_request_template.md delete mode 100644 plugins/host-metrics/.gitignore delete mode 100644 plugins/host-metrics/.husky/commit-msg delete mode 100644 plugins/host-metrics/.husky/pre-commit delete mode 100644 plugins/host-metrics/.prettierignore delete mode 100644 plugins/host-metrics/.prettierrc delete mode 100644 plugins/host-metrics/CHANGELOG.json delete mode 100644 plugins/host-metrics/CONTRIBUTING.md delete mode 100644 plugins/host-metrics/README.md delete mode 100644 plugins/host-metrics/locales/en.json delete mode 100644 plugins/host-metrics/locales/translated/af_ZA.json delete mode 100644 plugins/host-metrics/locales/translated/ar_SA.json delete mode 100644 plugins/host-metrics/locales/translated/bg_BG.json delete mode 100644 plugins/host-metrics/locales/translated/bn_BD.json delete mode 100644 plugins/host-metrics/locales/translated/ca_ES.json delete mode 100644 plugins/host-metrics/locales/translated/cs_CZ.json delete mode 100644 plugins/host-metrics/locales/translated/da_DK.json delete mode 100644 plugins/host-metrics/locales/translated/de_DE.json delete mode 100644 plugins/host-metrics/locales/translated/el_GR.json delete mode 100644 plugins/host-metrics/locales/translated/es_ES.json delete mode 100644 plugins/host-metrics/locales/translated/fi_FI.json delete mode 100644 plugins/host-metrics/locales/translated/fr_FR.json delete mode 100644 plugins/host-metrics/locales/translated/he_IL.json delete mode 100644 plugins/host-metrics/locales/translated/hi_IN.json delete mode 100644 plugins/host-metrics/locales/translated/hu_HU.json delete mode 100644 plugins/host-metrics/locales/translated/id_ID.json delete mode 100644 plugins/host-metrics/locales/translated/it_IT.json delete mode 100644 plugins/host-metrics/locales/translated/ja_JP.json delete mode 100644 plugins/host-metrics/locales/translated/ko_KR.json delete mode 100644 plugins/host-metrics/locales/translated/nl_NL.json delete mode 100644 plugins/host-metrics/locales/translated/no_NO.json delete mode 100644 plugins/host-metrics/locales/translated/pl_PL.json delete mode 100644 plugins/host-metrics/locales/translated/pt_BR.json delete mode 100644 plugins/host-metrics/locales/translated/pt_PT.json delete mode 100644 plugins/host-metrics/locales/translated/ro_RO.json delete mode 100644 plugins/host-metrics/locales/translated/ru_RU.json delete mode 100644 plugins/host-metrics/locales/translated/sr_SP.json delete mode 100644 plugins/host-metrics/locales/translated/sv_SE.json delete mode 100644 plugins/host-metrics/locales/translated/th_TH.json delete mode 100644 plugins/host-metrics/locales/translated/tr_TR.json delete mode 100644 plugins/host-metrics/locales/translated/uk_UA.json delete mode 100644 plugins/host-metrics/locales/translated/vi_VN.json delete mode 100644 plugins/host-metrics/locales/translated/zh_CN.json delete mode 100644 plugins/host-metrics/locales/translated/zh_TW.json delete mode 100644 plugins/host-metrics/manifest.json delete mode 100644 plugins/host-metrics/migrations/mysql/0001_adopt_host_metrics_tables.sql delete mode 100644 plugins/host-metrics/migrations/postgres/0001_adopt_host_metrics_tables.sql delete mode 100644 plugins/host-metrics/migrations/snapshot.json delete mode 100644 plugins/host-metrics/migrations/sqlite/0001_adopt_host_metrics_tables.sql delete mode 100644 plugins/host-metrics/package.json delete mode 100644 plugins/host-metrics/src/backend/collect.ts delete mode 100644 plugins/host-metrics/src/backend/collectors.ts delete mode 100644 plugins/host-metrics/src/backend/helpers.ts delete mode 100644 plugins/host-metrics/src/backend/host-import.ts delete mode 100644 plugins/host-metrics/src/backend/index.ts delete mode 100644 plugins/host-metrics/src/backend/interactive.ts delete mode 100644 plugins/host-metrics/src/backend/log.ts delete mode 100644 plugins/host-metrics/src/backend/managers/cron.ts delete mode 100644 plugins/host-metrics/src/backend/managers/firewall.ts delete mode 100644 plugins/host-metrics/src/backend/managers/health.ts delete mode 100644 plugins/host-metrics/src/backend/managers/index.ts delete mode 100644 plugins/host-metrics/src/backend/managers/logs.ts delete mode 100644 plugins/host-metrics/src/backend/managers/packages.ts delete mode 100644 plugins/host-metrics/src/backend/managers/processes.ts delete mode 100644 plugins/host-metrics/src/backend/managers/route-helpers.ts delete mode 100644 plugins/host-metrics/src/backend/managers/services.ts delete mode 100644 plugins/host-metrics/src/backend/managers/simple-reads.ts delete mode 100644 plugins/host-metrics/src/backend/managers/ssl.ts delete mode 100644 plugins/host-metrics/src/backend/managers/types.ts delete mode 100644 plugins/host-metrics/src/backend/managers/users.ts delete mode 100644 plugins/host-metrics/src/backend/managers/validation.ts delete mode 100644 plugins/host-metrics/src/backend/managers/wireguard.ts delete mode 100644 plugins/host-metrics/src/backend/poller.ts delete mode 100644 plugins/host-metrics/src/backend/repository.ts delete mode 100644 plugins/host-metrics/src/backend/routes.ts delete mode 100644 plugins/host-metrics/src/backend/sessions.ts delete mode 100644 plugins/host-metrics/src/backend/state.ts delete mode 100644 plugins/host-metrics/src/backend/tables.ts delete mode 100644 plugins/host-metrics/src/backend/util.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/cpu-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/disk-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/firewall-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/gpu-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/login-stats-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/memory-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/network-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/ports-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/processes-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/system-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/temperature-collector.ts delete mode 100644 plugins/host-metrics/src/backend/widgets/uptime-collector.ts delete mode 100644 plugins/host-metrics/src/frontend/HostEditorStatsTab.tsx delete mode 100644 plugins/host-metrics/src/frontend/HostMetricsApp.tsx delete mode 100644 plugins/host-metrics/src/frontend/HostMetricsTab.tsx delete mode 100644 plugins/host-metrics/src/frontend/MetricsChartEditForm.tsx delete mode 100644 plugins/host-metrics/src/frontend/MetricsChartWidget.tsx delete mode 100644 plugins/host-metrics/src/frontend/SummaryViews.tsx delete mode 100644 plugins/host-metrics/src/frontend/TerminalMetricsStatus.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/CardTimeTabs.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/CpuCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/DiskCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/FilesystemPicker.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/FirewallCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/GpuCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/LoginStatsCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/MemoryCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/NetworkCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/PortsCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/ProcessesCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/SystemCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/TemperatureCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/UptimeCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/gpu-history.ts delete mode 100644 plugins/host-metrics/src/frontend/cards/index.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/CronManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/FirewallManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/HealthCheckCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/LogViewerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/PackageManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/ProcessInspectorCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/ServiceManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/SimpleManagerCards.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/SslManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/UserManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/WireGuardManagerCard.tsx delete mode 100644 plugins/host-metrics/src/frontend/cards/managers/useManagerData.ts delete mode 100644 plugins/host-metrics/src/frontend/hooks/useHostMetricsPreferences.ts delete mode 100644 plugins/host-metrics/src/frontend/host-metrics-api.ts delete mode 100644 plugins/host-metrics/src/frontend/index.tsx delete mode 100644 plugins/host-metrics/src/frontend/metrics-change-key.ts delete mode 100644 plugins/host-metrics/src/frontend/summary-store.ts delete mode 100644 plugins/host-metrics/src/frontend/temperature-preference.ts delete mode 100644 plugins/host-metrics/src/shared/homepage.ts delete mode 100644 plugins/host-metrics/src/shared/host-metrics.ts delete mode 100644 plugins/host-metrics/src/shared/metrics.ts delete mode 100644 plugins/host-metrics/src/shared/stats-widgets.ts delete mode 100644 plugins/host-metrics/tests/backend/activate.test.ts delete mode 100644 plugins/host-metrics/tests/backend/helpers.test.ts delete mode 100644 plugins/host-metrics/tests/backend/host-metrics-preferences.test.ts delete mode 100644 plugins/host-metrics/tests/backend/host-payload-legacy.test.ts delete mode 100644 plugins/host-metrics/tests/backend/interactive-jump-totp.test.ts delete mode 100644 plugins/host-metrics/tests/backend/managers/managers.test.ts delete mode 100644 plugins/host-metrics/tests/backend/migrations.test.ts delete mode 100644 plugins/host-metrics/tests/backend/poller.test.ts delete mode 100644 plugins/host-metrics/tests/backend/routes.test.ts delete mode 100644 plugins/host-metrics/tests/backend/server.ts delete mode 100644 plugins/host-metrics/tests/backend/state.test.ts delete mode 100644 plugins/host-metrics/tests/backend/widgets/cpu-collector.test.ts delete mode 100644 plugins/host-metrics/tests/backend/widgets/disk-collector.test.ts delete mode 100644 plugins/host-metrics/tests/backend/widgets/gpu-collector.test.ts delete mode 100644 plugins/host-metrics/tests/backend/widgets/network-collector.test.ts delete mode 100644 plugins/host-metrics/tests/backend/widgets/temperature-collector.test.ts delete mode 100644 plugins/host-metrics/tests/frontend/HostEditorStatsTab.test.tsx delete mode 100644 plugins/host-metrics/tests/frontend/HostMetricsTab.test.tsx delete mode 100644 plugins/host-metrics/tests/frontend/TerminalMetricsStatus.test.tsx delete mode 100644 plugins/host-metrics/tests/frontend/activate.test.tsx delete mode 100644 plugins/host-metrics/tests/frontend/cards/GpuCard.test.tsx delete mode 100644 plugins/host-metrics/tests/frontend/cards/gpu-history.test.ts delete mode 100644 plugins/host-metrics/tests/frontend/host-metrics-api.test.ts delete mode 100644 plugins/host-metrics/tests/frontend/metrics-change-key.test.ts delete mode 100644 plugins/host-metrics/tests/frontend/summary-store.test.ts delete mode 100644 plugins/host-metrics/tests/frontend/temperature-preference.test.ts delete mode 100644 plugins/host-metrics/tsconfig.json delete mode 100644 plugins/host-metrics/vitest.config.ts delete mode 100644 plugins/ldap/.commitlintrc.json delete mode 100644 plugins/ldap/.github/funding.yml delete mode 100644 plugins/ldap/.github/pull_request_template.md delete mode 100644 plugins/ldap/.gitignore delete mode 100644 plugins/ldap/.husky/commit-msg delete mode 100644 plugins/ldap/.husky/pre-commit delete mode 100644 plugins/ldap/.prettierignore delete mode 100644 plugins/ldap/.prettierrc delete mode 100644 plugins/ldap/CHANGELOG.json delete mode 100644 plugins/ldap/CONTRIBUTING.md delete mode 100644 plugins/ldap/README.md delete mode 100644 plugins/ldap/locales/en.json delete mode 100644 plugins/ldap/locales/translated/af_ZA.json delete mode 100644 plugins/ldap/locales/translated/ar_SA.json delete mode 100644 plugins/ldap/locales/translated/bg_BG.json delete mode 100644 plugins/ldap/locales/translated/bn_BD.json delete mode 100644 plugins/ldap/locales/translated/ca_ES.json delete mode 100644 plugins/ldap/locales/translated/cs_CZ.json delete mode 100644 plugins/ldap/locales/translated/da_DK.json delete mode 100644 plugins/ldap/locales/translated/de_DE.json delete mode 100644 plugins/ldap/locales/translated/el_GR.json delete mode 100644 plugins/ldap/locales/translated/es_ES.json delete mode 100644 plugins/ldap/locales/translated/fi_FI.json delete mode 100644 plugins/ldap/locales/translated/fr_FR.json delete mode 100644 plugins/ldap/locales/translated/he_IL.json delete mode 100644 plugins/ldap/locales/translated/hi_IN.json delete mode 100644 plugins/ldap/locales/translated/hu_HU.json delete mode 100644 plugins/ldap/locales/translated/id_ID.json delete mode 100644 plugins/ldap/locales/translated/it_IT.json delete mode 100644 plugins/ldap/locales/translated/ja_JP.json delete mode 100644 plugins/ldap/locales/translated/ko_KR.json delete mode 100644 plugins/ldap/locales/translated/nl_NL.json delete mode 100644 plugins/ldap/locales/translated/no_NO.json delete mode 100644 plugins/ldap/locales/translated/pl_PL.json delete mode 100644 plugins/ldap/locales/translated/pt_BR.json delete mode 100644 plugins/ldap/locales/translated/pt_PT.json delete mode 100644 plugins/ldap/locales/translated/ro_RO.json delete mode 100644 plugins/ldap/locales/translated/ru_RU.json delete mode 100644 plugins/ldap/locales/translated/sr_SP.json delete mode 100644 plugins/ldap/locales/translated/sv_SE.json delete mode 100644 plugins/ldap/locales/translated/th_TH.json delete mode 100644 plugins/ldap/locales/translated/tr_TR.json delete mode 100644 plugins/ldap/locales/translated/uk_UA.json delete mode 100644 plugins/ldap/locales/translated/vi_VN.json delete mode 100644 plugins/ldap/locales/translated/zh_CN.json delete mode 100644 plugins/ldap/locales/translated/zh_TW.json delete mode 100644 plugins/ldap/manifest.json delete mode 100644 plugins/ldap/migrations/mysql/0001_create_providers.sql delete mode 100644 plugins/ldap/migrations/postgres/0001_create_providers.sql delete mode 100644 plugins/ldap/migrations/snapshot.json delete mode 100644 plugins/ldap/migrations/sqlite/0001_create_providers.sql delete mode 100644 plugins/ldap/package.json delete mode 100644 plugins/ldap/src/backend/index.ts delete mode 100644 plugins/ldap/src/backend/ldap.ts delete mode 100644 plugins/ldap/src/backend/providers.ts delete mode 100644 plugins/ldap/src/backend/routes.ts delete mode 100644 plugins/ldap/src/backend/tables.ts delete mode 100644 plugins/ldap/src/backend/types.ts delete mode 100644 plugins/ldap/src/frontend/LdapLoginForms.tsx delete mode 100644 plugins/ldap/src/frontend/ProvidersSetting.tsx delete mode 100644 plugins/ldap/src/frontend/index.tsx delete mode 100644 plugins/ldap/src/frontend/ldap-api.ts delete mode 100644 plugins/ldap/tests/backend/helpers.ts delete mode 100644 plugins/ldap/tests/backend/ldap.test.ts delete mode 100644 plugins/ldap/tests/frontend/activate.test.tsx delete mode 100644 plugins/ldap/tsconfig.json delete mode 100644 plugins/ldap/vitest.config.ts delete mode 100644 plugins/network-topology/.commitlintrc.json delete mode 100644 plugins/network-topology/.github/funding.yml delete mode 100644 plugins/network-topology/.github/pull_request_template.md delete mode 100644 plugins/network-topology/.gitignore delete mode 100644 plugins/network-topology/.husky/commit-msg delete mode 100644 plugins/network-topology/.husky/pre-commit delete mode 100644 plugins/network-topology/.prettierignore delete mode 100644 plugins/network-topology/.prettierrc delete mode 100644 plugins/network-topology/CHANGELOG.json delete mode 100644 plugins/network-topology/CONTRIBUTING.md delete mode 100644 plugins/network-topology/README.md delete mode 100644 plugins/network-topology/locales/en.json delete mode 100644 plugins/network-topology/locales/translated/af_ZA.json delete mode 100644 plugins/network-topology/locales/translated/ar_SA.json delete mode 100644 plugins/network-topology/locales/translated/bg_BG.json delete mode 100644 plugins/network-topology/locales/translated/bn_BD.json delete mode 100644 plugins/network-topology/locales/translated/ca_ES.json delete mode 100644 plugins/network-topology/locales/translated/cs_CZ.json delete mode 100644 plugins/network-topology/locales/translated/da_DK.json delete mode 100644 plugins/network-topology/locales/translated/de_DE.json delete mode 100644 plugins/network-topology/locales/translated/el_GR.json delete mode 100644 plugins/network-topology/locales/translated/es_ES.json delete mode 100644 plugins/network-topology/locales/translated/fi_FI.json delete mode 100644 plugins/network-topology/locales/translated/fr_FR.json delete mode 100644 plugins/network-topology/locales/translated/he_IL.json delete mode 100644 plugins/network-topology/locales/translated/hi_IN.json delete mode 100644 plugins/network-topology/locales/translated/hu_HU.json delete mode 100644 plugins/network-topology/locales/translated/id_ID.json delete mode 100644 plugins/network-topology/locales/translated/it_IT.json delete mode 100644 plugins/network-topology/locales/translated/ja_JP.json delete mode 100644 plugins/network-topology/locales/translated/ko_KR.json delete mode 100644 plugins/network-topology/locales/translated/nl_NL.json delete mode 100644 plugins/network-topology/locales/translated/no_NO.json delete mode 100644 plugins/network-topology/locales/translated/pl_PL.json delete mode 100644 plugins/network-topology/locales/translated/pt_BR.json delete mode 100644 plugins/network-topology/locales/translated/pt_PT.json delete mode 100644 plugins/network-topology/locales/translated/ro_RO.json delete mode 100644 plugins/network-topology/locales/translated/ru_RU.json delete mode 100644 plugins/network-topology/locales/translated/sr_SP.json delete mode 100644 plugins/network-topology/locales/translated/sv_SE.json delete mode 100644 plugins/network-topology/locales/translated/th_TH.json delete mode 100644 plugins/network-topology/locales/translated/tr_TR.json delete mode 100644 plugins/network-topology/locales/translated/uk_UA.json delete mode 100644 plugins/network-topology/locales/translated/vi_VN.json delete mode 100644 plugins/network-topology/locales/translated/zh_CN.json delete mode 100644 plugins/network-topology/locales/translated/zh_TW.json delete mode 100644 plugins/network-topology/manifest.json delete mode 100644 plugins/network-topology/migrations/mysql/0001_adopt_network_topology.sql delete mode 100644 plugins/network-topology/migrations/postgres/0001_adopt_network_topology.sql delete mode 100644 plugins/network-topology/migrations/snapshot.json delete mode 100644 plugins/network-topology/migrations/sqlite/0001_adopt_network_topology.sql delete mode 100644 plugins/network-topology/package.json delete mode 100644 plugins/network-topology/src/backend/index.ts delete mode 100644 plugins/network-topology/src/backend/repository.ts delete mode 100644 plugins/network-topology/src/backend/routes.ts delete mode 100644 plugins/network-topology/src/backend/tables.ts delete mode 100644 plugins/network-topology/src/backend/topology-host-ids.ts delete mode 100644 plugins/network-topology/src/frontend/NetworkGraphCard.tsx delete mode 100644 plugins/network-topology/src/frontend/index.tsx delete mode 100644 plugins/network-topology/src/frontend/network-topology-api.ts delete mode 100644 plugins/network-topology/tests/backend/activate.test.ts delete mode 100644 plugins/network-topology/tests/backend/helpers.ts delete mode 100644 plugins/network-topology/tests/backend/migrations.test.ts delete mode 100644 plugins/network-topology/tests/backend/repository.test.ts delete mode 100644 plugins/network-topology/tests/backend/routes.test.ts delete mode 100644 plugins/network-topology/tests/frontend/activate.test.tsx delete mode 100644 plugins/network-topology/tsconfig.json delete mode 100644 plugins/network-topology/vitest.config.ts delete mode 100644 plugins/opkssh/.commitlintrc.json delete mode 100644 plugins/opkssh/.github/funding.yml delete mode 100644 plugins/opkssh/.github/pull_request_template.md delete mode 100644 plugins/opkssh/.gitignore delete mode 100644 plugins/opkssh/.husky/commit-msg delete mode 100644 plugins/opkssh/.husky/pre-commit delete mode 100644 plugins/opkssh/.prettierignore delete mode 100644 plugins/opkssh/.prettierrc delete mode 100644 plugins/opkssh/CHANGELOG.json delete mode 100644 plugins/opkssh/CONTRIBUTING.md delete mode 100644 plugins/opkssh/README.md delete mode 100644 plugins/opkssh/locales/en.json delete mode 100644 plugins/opkssh/locales/translated/af_ZA.json delete mode 100644 plugins/opkssh/locales/translated/ar_SA.json delete mode 100644 plugins/opkssh/locales/translated/bg_BG.json delete mode 100644 plugins/opkssh/locales/translated/bn_BD.json delete mode 100644 plugins/opkssh/locales/translated/ca_ES.json delete mode 100644 plugins/opkssh/locales/translated/cs_CZ.json delete mode 100644 plugins/opkssh/locales/translated/da_DK.json delete mode 100644 plugins/opkssh/locales/translated/de_DE.json delete mode 100644 plugins/opkssh/locales/translated/el_GR.json delete mode 100644 plugins/opkssh/locales/translated/es_ES.json delete mode 100644 plugins/opkssh/locales/translated/fi_FI.json delete mode 100644 plugins/opkssh/locales/translated/fr_FR.json delete mode 100644 plugins/opkssh/locales/translated/he_IL.json delete mode 100644 plugins/opkssh/locales/translated/hi_IN.json delete mode 100644 plugins/opkssh/locales/translated/hu_HU.json delete mode 100644 plugins/opkssh/locales/translated/id_ID.json delete mode 100644 plugins/opkssh/locales/translated/it_IT.json delete mode 100644 plugins/opkssh/locales/translated/ja_JP.json delete mode 100644 plugins/opkssh/locales/translated/ko_KR.json delete mode 100644 plugins/opkssh/locales/translated/nl_NL.json delete mode 100644 plugins/opkssh/locales/translated/no_NO.json delete mode 100644 plugins/opkssh/locales/translated/pl_PL.json delete mode 100644 plugins/opkssh/locales/translated/pt_BR.json delete mode 100644 plugins/opkssh/locales/translated/pt_PT.json delete mode 100644 plugins/opkssh/locales/translated/ro_RO.json delete mode 100644 plugins/opkssh/locales/translated/ru_RU.json delete mode 100644 plugins/opkssh/locales/translated/sr_SP.json delete mode 100644 plugins/opkssh/locales/translated/sv_SE.json delete mode 100644 plugins/opkssh/locales/translated/th_TH.json delete mode 100644 plugins/opkssh/locales/translated/tr_TR.json delete mode 100644 plugins/opkssh/locales/translated/uk_UA.json delete mode 100644 plugins/opkssh/locales/translated/vi_VN.json delete mode 100644 plugins/opkssh/locales/translated/zh_CN.json delete mode 100644 plugins/opkssh/locales/translated/zh_TW.json delete mode 100644 plugins/opkssh/manifest.json delete mode 100644 plugins/opkssh/migrations/mysql/0001_adopt_opkssh_tokens.sql delete mode 100644 plugins/opkssh/migrations/postgres/0001_adopt_opkssh_tokens.sql delete mode 100644 plugins/opkssh/migrations/snapshot.json delete mode 100644 plugins/opkssh/migrations/sqlite/0001_adopt_opkssh_tokens.sql delete mode 100644 plugins/opkssh/package.json delete mode 100644 plugins/opkssh/src/backend/auth-session.ts delete mode 100644 plugins/opkssh/src/backend/binary.ts delete mode 100644 plugins/opkssh/src/backend/config.ts delete mode 100644 plugins/opkssh/src/backend/html.ts delete mode 100644 plugins/opkssh/src/backend/index.ts delete mode 100644 plugins/opkssh/src/backend/provider.ts delete mode 100644 plugins/opkssh/src/backend/routes.ts delete mode 100644 plugins/opkssh/src/backend/tables.ts delete mode 100644 plugins/opkssh/src/backend/token-store.ts delete mode 100644 plugins/opkssh/src/frontend/OpksshAuthEditor.tsx delete mode 100644 plugins/opkssh/src/frontend/OpksshDialog.tsx delete mode 100644 plugins/opkssh/src/frontend/OpksshOverlay.tsx delete mode 100644 plugins/opkssh/src/frontend/RedirectUriSetting.tsx delete mode 100644 plugins/opkssh/src/frontend/index.tsx delete mode 100644 plugins/opkssh/tests/backend/helpers-pure.test.ts delete mode 100644 plugins/opkssh/tests/backend/helpers.ts delete mode 100644 plugins/opkssh/tests/backend/legacy-urls.test.ts delete mode 100644 plugins/opkssh/tests/backend/opkssh.test.ts delete mode 100644 plugins/opkssh/tests/frontend/activate.test.tsx delete mode 100644 plugins/opkssh/tsconfig.json delete mode 100644 plugins/opkssh/vitest.config.ts delete mode 100644 plugins/package.json delete mode 100644 plugins/proxmox/.commitlintrc.json delete mode 100644 plugins/proxmox/.github/funding.yml delete mode 100644 plugins/proxmox/.github/pull_request_template.md delete mode 100644 plugins/proxmox/.gitignore delete mode 100644 plugins/proxmox/.husky/commit-msg delete mode 100644 plugins/proxmox/.husky/pre-commit delete mode 100644 plugins/proxmox/.prettierignore delete mode 100644 plugins/proxmox/.prettierrc delete mode 100644 plugins/proxmox/CHANGELOG.json delete mode 100644 plugins/proxmox/CONTRIBUTING.md delete mode 100644 plugins/proxmox/README.md delete mode 100644 plugins/proxmox/locales/en.json delete mode 100644 plugins/proxmox/locales/translated/af_ZA.json delete mode 100644 plugins/proxmox/locales/translated/ar_SA.json delete mode 100644 plugins/proxmox/locales/translated/bg_BG.json delete mode 100644 plugins/proxmox/locales/translated/bn_BD.json delete mode 100644 plugins/proxmox/locales/translated/ca_ES.json delete mode 100644 plugins/proxmox/locales/translated/cs_CZ.json delete mode 100644 plugins/proxmox/locales/translated/da_DK.json delete mode 100644 plugins/proxmox/locales/translated/de_DE.json delete mode 100644 plugins/proxmox/locales/translated/el_GR.json delete mode 100644 plugins/proxmox/locales/translated/es_ES.json delete mode 100644 plugins/proxmox/locales/translated/fi_FI.json delete mode 100644 plugins/proxmox/locales/translated/fr_FR.json delete mode 100644 plugins/proxmox/locales/translated/he_IL.json delete mode 100644 plugins/proxmox/locales/translated/hi_IN.json delete mode 100644 plugins/proxmox/locales/translated/hu_HU.json delete mode 100644 plugins/proxmox/locales/translated/id_ID.json delete mode 100644 plugins/proxmox/locales/translated/it_IT.json delete mode 100644 plugins/proxmox/locales/translated/ja_JP.json delete mode 100644 plugins/proxmox/locales/translated/ko_KR.json delete mode 100644 plugins/proxmox/locales/translated/nl_NL.json delete mode 100644 plugins/proxmox/locales/translated/no_NO.json delete mode 100644 plugins/proxmox/locales/translated/pl_PL.json delete mode 100644 plugins/proxmox/locales/translated/pt_BR.json delete mode 100644 plugins/proxmox/locales/translated/pt_PT.json delete mode 100644 plugins/proxmox/locales/translated/ro_RO.json delete mode 100644 plugins/proxmox/locales/translated/ru_RU.json delete mode 100644 plugins/proxmox/locales/translated/sr_SP.json delete mode 100644 plugins/proxmox/locales/translated/sv_SE.json delete mode 100644 plugins/proxmox/locales/translated/th_TH.json delete mode 100644 plugins/proxmox/locales/translated/tr_TR.json delete mode 100644 plugins/proxmox/locales/translated/uk_UA.json delete mode 100644 plugins/proxmox/locales/translated/vi_VN.json delete mode 100644 plugins/proxmox/locales/translated/zh_CN.json delete mode 100644 plugins/proxmox/locales/translated/zh_TW.json delete mode 100644 plugins/proxmox/manifest.json delete mode 100644 plugins/proxmox/migrations/mysql/0001_adopt_proxmox_stats.sql delete mode 100644 plugins/proxmox/migrations/postgres/0001_adopt_proxmox_stats.sql delete mode 100644 plugins/proxmox/migrations/snapshot.json delete mode 100644 plugins/proxmox/migrations/sqlite/0001_adopt_proxmox_stats.sql delete mode 100644 plugins/proxmox/package.json delete mode 100644 plugins/proxmox/src/backend/guest-sync.ts delete mode 100644 plugins/proxmox/src/backend/index.ts delete mode 100644 plugins/proxmox/src/backend/plugin-ctx.ts delete mode 100644 plugins/proxmox/src/backend/proxmox-import-auth.ts delete mode 100644 plugins/proxmox/src/backend/proxmox-jump-hosts.ts delete mode 100644 plugins/proxmox/src/backend/proxmox-node-history-repository.ts delete mode 100644 plugins/proxmox/src/backend/proxmox-shared.ts delete mode 100644 plugins/proxmox/src/backend/proxmox-stats-polling.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/cluster-health-collector.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/collect-proxmox-stats.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/common-utils.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/guests-collector.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/node-network-collector.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/node-status-collector.ts delete mode 100644 plugins/proxmox/src/backend/proxmox/storage-collector.ts delete mode 100644 plugins/proxmox/src/backend/routes.ts delete mode 100644 plugins/proxmox/src/backend/ssh.ts delete mode 100644 plugins/proxmox/src/backend/stats-service.ts delete mode 100644 plugins/proxmox/src/backend/tables.ts delete mode 100644 plugins/proxmox/src/frontend/HostProxmoxTab.tsx delete mode 100644 plugins/proxmox/src/frontend/ProxmoxDiscoverDialog.tsx delete mode 100644 plugins/proxmox/src/frontend/index.tsx delete mode 100644 plugins/proxmox/src/frontend/proxmox-api.ts delete mode 100644 plugins/proxmox/src/frontend/proxmox-import-auth.ts delete mode 100644 plugins/proxmox/src/frontend/stats/GuestTable.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/HostProxmoxStatsTab.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/NodeSummaryStrip.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/ProxmoxStatsApp.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/ProxmoxStatsTab.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/cards/ClusterHealthCard.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/cards/NodeNetworkCard.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/cards/StoragePoolsCard.tsx delete mode 100644 plugins/proxmox/src/frontend/stats/proxmox-stats-api.ts delete mode 100644 plugins/proxmox/src/frontend/types.ts delete mode 100644 plugins/proxmox/tests/backend/guest-sync.test.ts delete mode 100644 plugins/proxmox/tests/backend/helpers.ts delete mode 100644 plugins/proxmox/tests/backend/migrations.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox-import-auth.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox-jump-hosts.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox-stats-polling.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox-sudo.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/cluster-health-collector.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/collect-proxmox-stats.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/guests-collector.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/node-network-collector.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/node-status-collector.test.ts delete mode 100644 plugins/proxmox/tests/backend/proxmox/storage-collector.test.ts delete mode 100644 plugins/proxmox/tests/backend/repositories/proxmox-node-history-repository.test.ts delete mode 100644 plugins/proxmox/tests/backend/routes.test.ts delete mode 100644 plugins/proxmox/tests/backend/sudo-capability.test.ts delete mode 100644 plugins/proxmox/tests/frontend/activate.test.tsx delete mode 100644 plugins/proxmox/tests/frontend/proxmox-import-auth.test.ts delete mode 100644 plugins/proxmox/tests/frontend/stats/ProxmoxStatsTab.test.tsx delete mode 100644 plugins/proxmox/tsconfig.json delete mode 100644 plugins/proxmox/vitest.config.ts delete mode 100644 plugins/remote-desktop/.commitlintrc.json delete mode 100644 plugins/remote-desktop/.github/funding.yml delete mode 100644 plugins/remote-desktop/.github/pull_request_template.md delete mode 100644 plugins/remote-desktop/.gitignore delete mode 100644 plugins/remote-desktop/.husky/commit-msg delete mode 100644 plugins/remote-desktop/.husky/pre-commit delete mode 100644 plugins/remote-desktop/.prettierignore delete mode 100644 plugins/remote-desktop/.prettierrc delete mode 100644 plugins/remote-desktop/CHANGELOG.json delete mode 100644 plugins/remote-desktop/CONTRIBUTING.md delete mode 100644 plugins/remote-desktop/README.md delete mode 100644 plugins/remote-desktop/locales/en.json delete mode 100644 plugins/remote-desktop/locales/translated/af_ZA.json delete mode 100644 plugins/remote-desktop/locales/translated/ar_SA.json delete mode 100644 plugins/remote-desktop/locales/translated/bg_BG.json delete mode 100644 plugins/remote-desktop/locales/translated/bn_BD.json delete mode 100644 plugins/remote-desktop/locales/translated/ca_ES.json delete mode 100644 plugins/remote-desktop/locales/translated/cs_CZ.json delete mode 100644 plugins/remote-desktop/locales/translated/da_DK.json delete mode 100644 plugins/remote-desktop/locales/translated/de_DE.json delete mode 100644 plugins/remote-desktop/locales/translated/el_GR.json delete mode 100644 plugins/remote-desktop/locales/translated/es_ES.json delete mode 100644 plugins/remote-desktop/locales/translated/fi_FI.json delete mode 100644 plugins/remote-desktop/locales/translated/fr_FR.json delete mode 100644 plugins/remote-desktop/locales/translated/he_IL.json delete mode 100644 plugins/remote-desktop/locales/translated/hi_IN.json delete mode 100644 plugins/remote-desktop/locales/translated/hu_HU.json delete mode 100644 plugins/remote-desktop/locales/translated/id_ID.json delete mode 100644 plugins/remote-desktop/locales/translated/it_IT.json delete mode 100644 plugins/remote-desktop/locales/translated/ja_JP.json delete mode 100644 plugins/remote-desktop/locales/translated/ko_KR.json delete mode 100644 plugins/remote-desktop/locales/translated/nl_NL.json delete mode 100644 plugins/remote-desktop/locales/translated/no_NO.json delete mode 100644 plugins/remote-desktop/locales/translated/pl_PL.json delete mode 100644 plugins/remote-desktop/locales/translated/pt_BR.json delete mode 100644 plugins/remote-desktop/locales/translated/pt_PT.json delete mode 100644 plugins/remote-desktop/locales/translated/ro_RO.json delete mode 100644 plugins/remote-desktop/locales/translated/ru_RU.json delete mode 100644 plugins/remote-desktop/locales/translated/sr_SP.json delete mode 100644 plugins/remote-desktop/locales/translated/sv_SE.json delete mode 100644 plugins/remote-desktop/locales/translated/th_TH.json delete mode 100644 plugins/remote-desktop/locales/translated/tr_TR.json delete mode 100644 plugins/remote-desktop/locales/translated/uk_UA.json delete mode 100644 plugins/remote-desktop/locales/translated/vi_VN.json delete mode 100644 plugins/remote-desktop/locales/translated/zh_CN.json delete mode 100644 plugins/remote-desktop/locales/translated/zh_TW.json delete mode 100644 plugins/remote-desktop/manifest.json delete mode 100644 plugins/remote-desktop/package.json delete mode 100644 plugins/remote-desktop/patches/guacamole-lite.cjs delete mode 100644 plugins/remote-desktop/src/backend/drive-settings.ts delete mode 100644 plugins/remote-desktop/src/backend/guacamole-server.ts delete mode 100644 plugins/remote-desktop/src/backend/guacd-config.ts delete mode 100644 plugins/remote-desktop/src/backend/host-settings.ts delete mode 100644 plugins/remote-desktop/src/backend/index.ts delete mode 100644 plugins/remote-desktop/src/backend/jump-tunnel-endpoint.ts delete mode 100644 plugins/remote-desktop/src/backend/jump-tunnel.ts delete mode 100644 plugins/remote-desktop/src/backend/live-sessions.ts delete mode 100644 plugins/remote-desktop/src/backend/log.ts delete mode 100644 plugins/remote-desktop/src/backend/macos-vnc-proxy.ts delete mode 100644 plugins/remote-desktop/src/backend/rdp-settings.ts delete mode 100644 plugins/remote-desktop/src/backend/recording-settings.ts delete mode 100644 plugins/remote-desktop/src/backend/routes.ts delete mode 100644 plugins/remote-desktop/src/backend/sessions.ts delete mode 100644 plugins/remote-desktop/src/backend/token-service.ts delete mode 100644 plugins/remote-desktop/src/frontend/GuacamoleApp.tsx delete mode 100644 plugins/remote-desktop/src/frontend/GuacamoleDisplay.tsx delete mode 100644 plugins/remote-desktop/src/frontend/GuacamoleFileBrowser.tsx delete mode 100644 plugins/remote-desktop/src/frontend/GuacamoleToolbar.tsx delete mode 100644 plugins/remote-desktop/src/frontend/HostEditorGuacamoleTabs.tsx delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-api.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-clipboard.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-common-js.d.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-config.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-display-size.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-file-drop.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-filesystem.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-meta-key.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-pointer.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-scroll.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-session-id.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-state.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-websocket-url.ts delete mode 100644 plugins/remote-desktop/src/frontend/guacamole-zoom.ts delete mode 100644 plugins/remote-desktop/src/frontend/host-remote.ts delete mode 100644 plugins/remote-desktop/src/frontend/index.tsx delete mode 100644 plugins/remote-desktop/src/frontend/quick-connect-guac-host.ts delete mode 100644 plugins/remote-desktop/src/frontend/rdp-credential-prompt.ts delete mode 100644 plugins/remote-desktop/src/frontend/remote-form.ts delete mode 100644 plugins/remote-desktop/tests/backend/drive-settings.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/host-settings.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/jump-tunnel-endpoint.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/macos-vnc-proxy.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/rdp-settings.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/recording-settings.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/routes.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/sessions.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/token-security.test.ts delete mode 100644 plugins/remote-desktop/tests/backend/token-service.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/GuacamoleToolbar.test.tsx delete mode 100644 plugins/remote-desktop/tests/frontend/activate.test.tsx delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-api.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-clipboard.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-display-size.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-file-drop.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-filesystem.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-key-release.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-meta-key.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-pointer.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-scroll.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-session-id.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-websocket-url.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/guacamole-zoom.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/quick-connect-guac-host.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/rdp-credential-prompt.test.ts delete mode 100644 plugins/remote-desktop/tests/frontend/remote-form.test.ts delete mode 100644 plugins/remote-desktop/tsconfig.json delete mode 100644 plugins/remote-desktop/vitest.config.ts delete mode 100644 plugins/secret-sources/.commitlintrc.json delete mode 100644 plugins/secret-sources/.github/funding.yml delete mode 100644 plugins/secret-sources/.github/pull_request_template.md delete mode 100644 plugins/secret-sources/.gitignore delete mode 100644 plugins/secret-sources/.husky/commit-msg delete mode 100644 plugins/secret-sources/.husky/pre-commit delete mode 100644 plugins/secret-sources/.prettierignore delete mode 100644 plugins/secret-sources/.prettierrc delete mode 100644 plugins/secret-sources/CHANGELOG.json delete mode 100644 plugins/secret-sources/CONTRIBUTING.md delete mode 100644 plugins/secret-sources/README.md delete mode 100644 plugins/secret-sources/locales/en.json delete mode 100644 plugins/secret-sources/locales/translated/af_ZA.json delete mode 100644 plugins/secret-sources/locales/translated/ar_SA.json delete mode 100644 plugins/secret-sources/locales/translated/bg_BG.json delete mode 100644 plugins/secret-sources/locales/translated/bn_BD.json delete mode 100644 plugins/secret-sources/locales/translated/ca_ES.json delete mode 100644 plugins/secret-sources/locales/translated/cs_CZ.json delete mode 100644 plugins/secret-sources/locales/translated/da_DK.json delete mode 100644 plugins/secret-sources/locales/translated/de_DE.json delete mode 100644 plugins/secret-sources/locales/translated/el_GR.json delete mode 100644 plugins/secret-sources/locales/translated/es_ES.json delete mode 100644 plugins/secret-sources/locales/translated/fi_FI.json delete mode 100644 plugins/secret-sources/locales/translated/fr_FR.json delete mode 100644 plugins/secret-sources/locales/translated/he_IL.json delete mode 100644 plugins/secret-sources/locales/translated/hi_IN.json delete mode 100644 plugins/secret-sources/locales/translated/hu_HU.json delete mode 100644 plugins/secret-sources/locales/translated/id_ID.json delete mode 100644 plugins/secret-sources/locales/translated/it_IT.json delete mode 100644 plugins/secret-sources/locales/translated/ja_JP.json delete mode 100644 plugins/secret-sources/locales/translated/ko_KR.json delete mode 100644 plugins/secret-sources/locales/translated/nl_NL.json delete mode 100644 plugins/secret-sources/locales/translated/no_NO.json delete mode 100644 plugins/secret-sources/locales/translated/pl_PL.json delete mode 100644 plugins/secret-sources/locales/translated/pt_BR.json delete mode 100644 plugins/secret-sources/locales/translated/pt_PT.json delete mode 100644 plugins/secret-sources/locales/translated/ro_RO.json delete mode 100644 plugins/secret-sources/locales/translated/ru_RU.json delete mode 100644 plugins/secret-sources/locales/translated/sr_SP.json delete mode 100644 plugins/secret-sources/locales/translated/sv_SE.json delete mode 100644 plugins/secret-sources/locales/translated/th_TH.json delete mode 100644 plugins/secret-sources/locales/translated/tr_TR.json delete mode 100644 plugins/secret-sources/locales/translated/uk_UA.json delete mode 100644 plugins/secret-sources/locales/translated/vi_VN.json delete mode 100644 plugins/secret-sources/locales/translated/zh_CN.json delete mode 100644 plugins/secret-sources/locales/translated/zh_TW.json delete mode 100644 plugins/secret-sources/manifest.json delete mode 100644 plugins/secret-sources/migrations/mysql/0001_adopt_secret_sources.sql delete mode 100644 plugins/secret-sources/migrations/postgres/0001_adopt_secret_sources.sql delete mode 100644 plugins/secret-sources/migrations/snapshot.json delete mode 100644 plugins/secret-sources/migrations/sqlite/0001_adopt_secret_sources.sql delete mode 100644 plugins/secret-sources/package.json delete mode 100644 plugins/secret-sources/src/backend/egress.ts delete mode 100644 plugins/secret-sources/src/backend/index.ts delete mode 100644 plugins/secret-sources/src/backend/onepassword-connect.ts delete mode 100644 plugins/secret-sources/src/backend/repository.ts delete mode 100644 plugins/secret-sources/src/backend/resolver.ts delete mode 100644 plugins/secret-sources/src/backend/routes.ts delete mode 100644 plugins/secret-sources/src/backend/tables.ts delete mode 100644 plugins/secret-sources/src/backend/token-store.ts delete mode 100644 plugins/secret-sources/src/frontend/SecretReferenceHint.tsx delete mode 100644 plugins/secret-sources/src/frontend/SecretSourceManager.tsx delete mode 100644 plugins/secret-sources/src/frontend/index.tsx delete mode 100644 plugins/secret-sources/src/frontend/secret-sources-api.ts delete mode 100644 plugins/secret-sources/tests/backend/helpers.ts delete mode 100644 plugins/secret-sources/tests/backend/onepassword-connect.test.ts delete mode 100644 plugins/secret-sources/tests/backend/resolver.test.ts delete mode 100644 plugins/secret-sources/tests/backend/routes.test.ts delete mode 100644 plugins/secret-sources/tests/backend/token-store.test.ts delete mode 100644 plugins/secret-sources/tests/frontend/activate.test.tsx delete mode 100644 plugins/secret-sources/tsconfig.json delete mode 100644 plugins/secret-sources/vitest.config.ts delete mode 100644 plugins/serial/.commitlintrc.json delete mode 100644 plugins/serial/.github/funding.yml delete mode 100644 plugins/serial/.github/pull_request_template.md delete mode 100644 plugins/serial/.gitignore delete mode 100644 plugins/serial/.husky/commit-msg delete mode 100644 plugins/serial/.husky/pre-commit delete mode 100644 plugins/serial/.prettierignore delete mode 100644 plugins/serial/.prettierrc delete mode 100644 plugins/serial/CHANGELOG.json delete mode 100644 plugins/serial/CONTRIBUTING.md delete mode 100644 plugins/serial/README.md delete mode 100644 plugins/serial/locales/en.json delete mode 100644 plugins/serial/locales/translated/af_ZA.json delete mode 100644 plugins/serial/locales/translated/ar_SA.json delete mode 100644 plugins/serial/locales/translated/bg_BG.json delete mode 100644 plugins/serial/locales/translated/bn_BD.json delete mode 100644 plugins/serial/locales/translated/ca_ES.json delete mode 100644 plugins/serial/locales/translated/cs_CZ.json delete mode 100644 plugins/serial/locales/translated/da_DK.json delete mode 100644 plugins/serial/locales/translated/de_DE.json delete mode 100644 plugins/serial/locales/translated/el_GR.json delete mode 100644 plugins/serial/locales/translated/es_ES.json delete mode 100644 plugins/serial/locales/translated/fi_FI.json delete mode 100644 plugins/serial/locales/translated/fr_FR.json delete mode 100644 plugins/serial/locales/translated/he_IL.json delete mode 100644 plugins/serial/locales/translated/hi_IN.json delete mode 100644 plugins/serial/locales/translated/hu_HU.json delete mode 100644 plugins/serial/locales/translated/id_ID.json delete mode 100644 plugins/serial/locales/translated/it_IT.json delete mode 100644 plugins/serial/locales/translated/ja_JP.json delete mode 100644 plugins/serial/locales/translated/ko_KR.json delete mode 100644 plugins/serial/locales/translated/nl_NL.json delete mode 100644 plugins/serial/locales/translated/no_NO.json delete mode 100644 plugins/serial/locales/translated/pl_PL.json delete mode 100644 plugins/serial/locales/translated/pt_BR.json delete mode 100644 plugins/serial/locales/translated/pt_PT.json delete mode 100644 plugins/serial/locales/translated/ro_RO.json delete mode 100644 plugins/serial/locales/translated/ru_RU.json delete mode 100644 plugins/serial/locales/translated/sr_SP.json delete mode 100644 plugins/serial/locales/translated/sv_SE.json delete mode 100644 plugins/serial/locales/translated/th_TH.json delete mode 100644 plugins/serial/locales/translated/tr_TR.json delete mode 100644 plugins/serial/locales/translated/uk_UA.json delete mode 100644 plugins/serial/locales/translated/vi_VN.json delete mode 100644 plugins/serial/locales/translated/zh_CN.json delete mode 100644 plugins/serial/locales/translated/zh_TW.json delete mode 100644 plugins/serial/manifest.json delete mode 100644 plugins/serial/package.json delete mode 100644 plugins/serial/src/backend/index.ts delete mode 100644 plugins/serial/src/backend/session.ts delete mode 100644 plugins/serial/src/backend/ws-message.ts delete mode 100644 plugins/serial/src/frontend/Serial.tsx delete mode 100644 plugins/serial/src/frontend/SerialPanel.tsx delete mode 100644 plugins/serial/src/frontend/electron.ts delete mode 100644 plugins/serial/src/frontend/index.tsx delete mode 100644 plugins/serial/src/frontend/transport.ts delete mode 100644 plugins/serial/src/frontend/types.ts delete mode 100644 plugins/serial/tests/backend/activate.test.ts delete mode 100644 plugins/serial/tests/backend/session.test.ts delete mode 100644 plugins/serial/tests/frontend/activate.test.tsx delete mode 100644 plugins/serial/tsconfig.json delete mode 100644 plugins/serial/vitest.config.ts delete mode 100644 plugins/session-recording/.commitlintrc.json delete mode 100644 plugins/session-recording/.github/funding.yml delete mode 100644 plugins/session-recording/.github/pull_request_template.md delete mode 100644 plugins/session-recording/.gitignore delete mode 100644 plugins/session-recording/.husky/commit-msg delete mode 100644 plugins/session-recording/.husky/pre-commit delete mode 100644 plugins/session-recording/.prettierignore delete mode 100644 plugins/session-recording/.prettierrc delete mode 100644 plugins/session-recording/CHANGELOG.json delete mode 100644 plugins/session-recording/CONTRIBUTING.md delete mode 100644 plugins/session-recording/README.md delete mode 100644 plugins/session-recording/locales/en.json delete mode 100644 plugins/session-recording/locales/translated/af_ZA.json delete mode 100644 plugins/session-recording/locales/translated/ar_SA.json delete mode 100644 plugins/session-recording/locales/translated/bg_BG.json delete mode 100644 plugins/session-recording/locales/translated/bn_BD.json delete mode 100644 plugins/session-recording/locales/translated/ca_ES.json delete mode 100644 plugins/session-recording/locales/translated/cs_CZ.json delete mode 100644 plugins/session-recording/locales/translated/da_DK.json delete mode 100644 plugins/session-recording/locales/translated/de_DE.json delete mode 100644 plugins/session-recording/locales/translated/el_GR.json delete mode 100644 plugins/session-recording/locales/translated/es_ES.json delete mode 100644 plugins/session-recording/locales/translated/fi_FI.json delete mode 100644 plugins/session-recording/locales/translated/fr_FR.json delete mode 100644 plugins/session-recording/locales/translated/he_IL.json delete mode 100644 plugins/session-recording/locales/translated/hi_IN.json delete mode 100644 plugins/session-recording/locales/translated/hu_HU.json delete mode 100644 plugins/session-recording/locales/translated/id_ID.json delete mode 100644 plugins/session-recording/locales/translated/it_IT.json delete mode 100644 plugins/session-recording/locales/translated/ja_JP.json delete mode 100644 plugins/session-recording/locales/translated/ko_KR.json delete mode 100644 plugins/session-recording/locales/translated/nl_NL.json delete mode 100644 plugins/session-recording/locales/translated/no_NO.json delete mode 100644 plugins/session-recording/locales/translated/pl_PL.json delete mode 100644 plugins/session-recording/locales/translated/pt_BR.json delete mode 100644 plugins/session-recording/locales/translated/pt_PT.json delete mode 100644 plugins/session-recording/locales/translated/ro_RO.json delete mode 100644 plugins/session-recording/locales/translated/ru_RU.json delete mode 100644 plugins/session-recording/locales/translated/sr_SP.json delete mode 100644 plugins/session-recording/locales/translated/sv_SE.json delete mode 100644 plugins/session-recording/locales/translated/th_TH.json delete mode 100644 plugins/session-recording/locales/translated/tr_TR.json delete mode 100644 plugins/session-recording/locales/translated/uk_UA.json delete mode 100644 plugins/session-recording/locales/translated/vi_VN.json delete mode 100644 plugins/session-recording/locales/translated/zh_CN.json delete mode 100644 plugins/session-recording/locales/translated/zh_TW.json delete mode 100644 plugins/session-recording/manifest.json delete mode 100644 plugins/session-recording/migrations/mysql/0001_adopt_session_recordings.sql delete mode 100644 plugins/session-recording/migrations/postgres/0001_adopt_session_recordings.sql delete mode 100644 plugins/session-recording/migrations/snapshot.json delete mode 100644 plugins/session-recording/migrations/sqlite/0001_adopt_session_recordings.sql delete mode 100644 plugins/session-recording/package.json delete mode 100644 plugins/session-recording/src/backend/files.ts delete mode 100644 plugins/session-recording/src/backend/index.ts delete mode 100644 plugins/session-recording/src/backend/repository.ts delete mode 100644 plugins/session-recording/src/backend/retention.ts delete mode 100644 plugins/session-recording/src/backend/routes.ts delete mode 100644 plugins/session-recording/src/backend/service.ts delete mode 100644 plugins/session-recording/src/backend/tables.ts delete mode 100644 plugins/session-recording/src/frontend/RecordingStatus.tsx delete mode 100644 plugins/session-recording/src/frontend/SessionLogsPanel.tsx delete mode 100644 plugins/session-recording/src/frontend/SessionRecordingPlayer.tsx delete mode 100644 plugins/session-recording/src/frontend/asciicast.ts delete mode 100644 plugins/session-recording/src/frontend/index.tsx delete mode 100644 plugins/session-recording/src/frontend/session-recording-api.ts delete mode 100644 plugins/session-recording/tests/backend/helpers.ts delete mode 100644 plugins/session-recording/tests/backend/migrations.test.ts delete mode 100644 plugins/session-recording/tests/backend/repository.test.ts delete mode 100644 plugins/session-recording/tests/backend/routes.test.ts delete mode 100644 plugins/session-recording/tests/backend/service.test.ts delete mode 100644 plugins/session-recording/tests/frontend/activate.test.tsx delete mode 100644 plugins/session-recording/tests/frontend/asciicast.test.ts delete mode 100644 plugins/session-recording/tsconfig.json delete mode 100644 plugins/session-recording/vitest.config.ts delete mode 100644 plugins/session-sharing/.commitlintrc.json delete mode 100644 plugins/session-sharing/.github/funding.yml delete mode 100644 plugins/session-sharing/.github/pull_request_template.md delete mode 100644 plugins/session-sharing/.gitignore delete mode 100644 plugins/session-sharing/.husky/commit-msg delete mode 100644 plugins/session-sharing/.husky/pre-commit delete mode 100644 plugins/session-sharing/.prettierignore delete mode 100644 plugins/session-sharing/.prettierrc delete mode 100644 plugins/session-sharing/CHANGELOG.json delete mode 100644 plugins/session-sharing/CONTRIBUTING.md delete mode 100644 plugins/session-sharing/README.md delete mode 100644 plugins/session-sharing/locales/en.json delete mode 100644 plugins/session-sharing/locales/translated/af_ZA.json delete mode 100644 plugins/session-sharing/locales/translated/ar_SA.json delete mode 100644 plugins/session-sharing/locales/translated/bg_BG.json delete mode 100644 plugins/session-sharing/locales/translated/bn_BD.json delete mode 100644 plugins/session-sharing/locales/translated/ca_ES.json delete mode 100644 plugins/session-sharing/locales/translated/cs_CZ.json delete mode 100644 plugins/session-sharing/locales/translated/da_DK.json delete mode 100644 plugins/session-sharing/locales/translated/de_DE.json delete mode 100644 plugins/session-sharing/locales/translated/el_GR.json delete mode 100644 plugins/session-sharing/locales/translated/es_ES.json delete mode 100644 plugins/session-sharing/locales/translated/fi_FI.json delete mode 100644 plugins/session-sharing/locales/translated/fr_FR.json delete mode 100644 plugins/session-sharing/locales/translated/he_IL.json delete mode 100644 plugins/session-sharing/locales/translated/hi_IN.json delete mode 100644 plugins/session-sharing/locales/translated/hu_HU.json delete mode 100644 plugins/session-sharing/locales/translated/id_ID.json delete mode 100644 plugins/session-sharing/locales/translated/it_IT.json delete mode 100644 plugins/session-sharing/locales/translated/ja_JP.json delete mode 100644 plugins/session-sharing/locales/translated/ko_KR.json delete mode 100644 plugins/session-sharing/locales/translated/nl_NL.json delete mode 100644 plugins/session-sharing/locales/translated/no_NO.json delete mode 100644 plugins/session-sharing/locales/translated/pl_PL.json delete mode 100644 plugins/session-sharing/locales/translated/pt_BR.json delete mode 100644 plugins/session-sharing/locales/translated/pt_PT.json delete mode 100644 plugins/session-sharing/locales/translated/ro_RO.json delete mode 100644 plugins/session-sharing/locales/translated/ru_RU.json delete mode 100644 plugins/session-sharing/locales/translated/sr_SP.json delete mode 100644 plugins/session-sharing/locales/translated/sv_SE.json delete mode 100644 plugins/session-sharing/locales/translated/th_TH.json delete mode 100644 plugins/session-sharing/locales/translated/tr_TR.json delete mode 100644 plugins/session-sharing/locales/translated/uk_UA.json delete mode 100644 plugins/session-sharing/locales/translated/vi_VN.json delete mode 100644 plugins/session-sharing/locales/translated/zh_CN.json delete mode 100644 plugins/session-sharing/locales/translated/zh_TW.json delete mode 100644 plugins/session-sharing/manifest.json delete mode 100644 plugins/session-sharing/migrations/mysql/0001_adopt_session_sharing.sql delete mode 100644 plugins/session-sharing/migrations/postgres/0001_adopt_session_sharing.sql delete mode 100644 plugins/session-sharing/migrations/snapshot.json delete mode 100644 plugins/session-sharing/migrations/sqlite/0001_adopt_session_sharing.sql delete mode 100644 plugins/session-sharing/package.json delete mode 100644 plugins/session-sharing/src/backend/deps.ts delete mode 100644 plugins/session-sharing/src/backend/index.ts delete mode 100644 plugins/session-sharing/src/backend/live.ts delete mode 100644 plugins/session-sharing/src/backend/rate-limit.ts delete mode 100644 plugins/session-sharing/src/backend/repositories.ts delete mode 100644 plugins/session-sharing/src/backend/room-hub.ts delete mode 100644 plugins/session-sharing/src/backend/room-routes.ts delete mode 100644 plugins/session-sharing/src/backend/runtime-store.ts delete mode 100644 plugins/session-sharing/src/backend/services.ts delete mode 100644 plugins/session-sharing/src/backend/share-routes.ts delete mode 100644 plugins/session-sharing/src/backend/tables.ts delete mode 100644 plugins/session-sharing/src/frontend/CollabGuestView.tsx delete mode 100644 plugins/session-sharing/src/frontend/CollabMembersSidebar.tsx delete mode 100644 plugins/session-sharing/src/frontend/CollabPanel.tsx delete mode 100644 plugins/session-sharing/src/frontend/CollabRoomTab.tsx delete mode 100644 plugins/session-sharing/src/frontend/ParticipantsOverlay.tsx delete mode 100644 plugins/session-sharing/src/frontend/ShareSessionModal.tsx delete mode 100644 plugins/session-sharing/src/frontend/SharedSessionView.tsx delete mode 100644 plugins/session-sharing/src/frontend/SharedWithMeSection.tsx delete mode 100644 plugins/session-sharing/src/frontend/api.ts delete mode 100644 plugins/session-sharing/src/frontend/index.tsx delete mode 100644 plugins/session-sharing/src/frontend/meeting-api.ts delete mode 100644 plugins/session-sharing/src/frontend/meeting-backend.ts delete mode 100644 plugins/session-sharing/src/frontend/share-dialog.tsx delete mode 100644 plugins/session-sharing/src/frontend/shared.tsx delete mode 100644 plugins/session-sharing/tests/backend/activate.test.ts delete mode 100644 plugins/session-sharing/tests/backend/helpers.ts delete mode 100644 plugins/session-sharing/tests/backend/migrations.test.ts delete mode 100644 plugins/session-sharing/tests/backend/rooms.test.ts delete mode 100644 plugins/session-sharing/tests/backend/security.test.ts delete mode 100644 plugins/session-sharing/tests/backend/shares.test.ts delete mode 100644 plugins/session-sharing/tests/frontend/SharedSessionView.test.tsx delete mode 100644 plugins/session-sharing/tests/frontend/SharedWithMeSection.test.tsx delete mode 100644 plugins/session-sharing/tests/frontend/activate.test.tsx delete mode 100644 plugins/session-sharing/tests/frontend/guest-terminal-size.test.tsx delete mode 100644 plugins/session-sharing/tests/frontend/meeting-backend.test.ts delete mode 100644 plugins/session-sharing/tests/frontend/meeting-transport.test.ts delete mode 100644 plugins/session-sharing/tests/frontend/presenter-takeover.test.ts delete mode 100644 plugins/session-sharing/tests/frontend/share-link-url.test.ts delete mode 100644 plugins/session-sharing/tsconfig.json delete mode 100644 plugins/session-sharing/vitest.config.ts delete mode 100644 plugins/snippets/.commitlintrc.json delete mode 100644 plugins/snippets/.github/funding.yml delete mode 100644 plugins/snippets/.github/pull_request_template.md delete mode 100644 plugins/snippets/.gitignore delete mode 100644 plugins/snippets/.husky/commit-msg delete mode 100644 plugins/snippets/.husky/pre-commit delete mode 100644 plugins/snippets/.prettierignore delete mode 100644 plugins/snippets/.prettierrc delete mode 100644 plugins/snippets/CHANGELOG.json delete mode 100644 plugins/snippets/CONTRIBUTING.md delete mode 100644 plugins/snippets/README.md delete mode 100644 plugins/snippets/locales/en.json delete mode 100644 plugins/snippets/locales/translated/af_ZA.json delete mode 100644 plugins/snippets/locales/translated/ar_SA.json delete mode 100644 plugins/snippets/locales/translated/bg_BG.json delete mode 100644 plugins/snippets/locales/translated/bn_BD.json delete mode 100644 plugins/snippets/locales/translated/ca_ES.json delete mode 100644 plugins/snippets/locales/translated/cs_CZ.json delete mode 100644 plugins/snippets/locales/translated/da_DK.json delete mode 100644 plugins/snippets/locales/translated/de_DE.json delete mode 100644 plugins/snippets/locales/translated/el_GR.json delete mode 100644 plugins/snippets/locales/translated/es_ES.json delete mode 100644 plugins/snippets/locales/translated/fi_FI.json delete mode 100644 plugins/snippets/locales/translated/fr_FR.json delete mode 100644 plugins/snippets/locales/translated/he_IL.json delete mode 100644 plugins/snippets/locales/translated/hi_IN.json delete mode 100644 plugins/snippets/locales/translated/hu_HU.json delete mode 100644 plugins/snippets/locales/translated/id_ID.json delete mode 100644 plugins/snippets/locales/translated/it_IT.json delete mode 100644 plugins/snippets/locales/translated/ja_JP.json delete mode 100644 plugins/snippets/locales/translated/ko_KR.json delete mode 100644 plugins/snippets/locales/translated/nl_NL.json delete mode 100644 plugins/snippets/locales/translated/no_NO.json delete mode 100644 plugins/snippets/locales/translated/pl_PL.json delete mode 100644 plugins/snippets/locales/translated/pt_BR.json delete mode 100644 plugins/snippets/locales/translated/pt_PT.json delete mode 100644 plugins/snippets/locales/translated/ro_RO.json delete mode 100644 plugins/snippets/locales/translated/ru_RU.json delete mode 100644 plugins/snippets/locales/translated/sr_SP.json delete mode 100644 plugins/snippets/locales/translated/sv_SE.json delete mode 100644 plugins/snippets/locales/translated/th_TH.json delete mode 100644 plugins/snippets/locales/translated/tr_TR.json delete mode 100644 plugins/snippets/locales/translated/uk_UA.json delete mode 100644 plugins/snippets/locales/translated/vi_VN.json delete mode 100644 plugins/snippets/locales/translated/zh_CN.json delete mode 100644 plugins/snippets/locales/translated/zh_TW.json delete mode 100644 plugins/snippets/manifest.json delete mode 100644 plugins/snippets/migrations/mysql/0001_adopt_snippets_tables.sql delete mode 100644 plugins/snippets/migrations/postgres/0001_adopt_snippets_tables.sql delete mode 100644 plugins/snippets/migrations/snapshot.json delete mode 100644 plugins/snippets/migrations/sqlite/0001_adopt_snippets_tables.sql delete mode 100644 plugins/snippets/package.json delete mode 100644 plugins/snippets/src/backend/execution.ts delete mode 100644 plugins/snippets/src/backend/host-import.ts delete mode 100644 plugins/snippets/src/backend/index.ts delete mode 100644 plugins/snippets/src/backend/reorder.ts delete mode 100644 plugins/snippets/src/backend/repository.ts delete mode 100644 plugins/snippets/src/backend/routes.ts delete mode 100644 plugins/snippets/src/backend/tables.ts delete mode 100644 plugins/snippets/src/frontend/AdminUserSnippets.tsx delete mode 100644 plugins/snippets/src/frontend/ExecutionResultsDialog.tsx delete mode 100644 plugins/snippets/src/frontend/HostSnippetsSection.tsx delete mode 100644 plugins/snippets/src/frontend/QuickActionButtons.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetEditor.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetFolderDialog.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetOverlay.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetSettings.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetShareView.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetTree.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetVariablesDialog.tsx delete mode 100644 plugins/snippets/src/frontend/SnippetsPanel.tsx delete mode 100644 plugins/snippets/src/frontend/TargetTerminals.tsx delete mode 100644 plugins/snippets/src/frontend/folder-icons.tsx delete mode 100644 plugins/snippets/src/frontend/index.tsx delete mode 100644 plugins/snippets/src/frontend/keybinding.tsx delete mode 100644 plugins/snippets/src/frontend/prompt-store.ts delete mode 100644 plugins/snippets/src/frontend/run-flows.ts delete mode 100644 plugins/snippets/src/frontend/settings.ts delete mode 100644 plugins/snippets/src/frontend/snippets-api.ts delete mode 100644 plugins/snippets/src/frontend/types.ts delete mode 100644 plugins/snippets/src/frontend/use-snippet-options.ts delete mode 100644 plugins/snippets/src/frontend/use-snippet-runner.tsx delete mode 100644 plugins/snippets/src/shared/host-settings.ts delete mode 100644 plugins/snippets/src/shared/variables.ts delete mode 100644 plugins/snippets/tests/backend/activate.test.ts delete mode 100644 plugins/snippets/tests/backend/execute.test.ts delete mode 100644 plugins/snippets/tests/backend/helpers.ts delete mode 100644 plugins/snippets/tests/backend/host-import.test.ts delete mode 100644 plugins/snippets/tests/backend/migrations.test.ts delete mode 100644 plugins/snippets/tests/backend/repository.test.ts delete mode 100644 plugins/snippets/tests/backend/routes.test.ts delete mode 100644 plugins/snippets/tests/backend/share-targets.test.ts delete mode 100644 plugins/snippets/tests/backend/variables.test.ts delete mode 100644 plugins/snippets/tests/frontend/activate.test.tsx delete mode 100644 plugins/snippets/tests/frontend/admin-user-snippets.test.tsx delete mode 100644 plugins/snippets/tests/frontend/settings.test.ts delete mode 100644 plugins/snippets/tests/frontend/snippets-panel.test.tsx delete mode 100644 plugins/snippets/tests/frontend/types.test.ts delete mode 100644 plugins/snippets/tsconfig.json delete mode 100644 plugins/snippets/vitest.config.ts delete mode 100644 plugins/ssh-terminal/.commitlintrc.json delete mode 100644 plugins/ssh-terminal/.github/funding.yml delete mode 100644 plugins/ssh-terminal/.github/pull_request_template.md delete mode 100644 plugins/ssh-terminal/.gitignore delete mode 100644 plugins/ssh-terminal/.husky/commit-msg delete mode 100644 plugins/ssh-terminal/.husky/pre-commit delete mode 100644 plugins/ssh-terminal/.prettierignore delete mode 100644 plugins/ssh-terminal/.prettierrc delete mode 100644 plugins/ssh-terminal/CHANGELOG.json delete mode 100644 plugins/ssh-terminal/CONTRIBUTING.md delete mode 100644 plugins/ssh-terminal/README.md delete mode 100644 plugins/ssh-terminal/locales/en.json delete mode 100644 plugins/ssh-terminal/locales/translated/af_ZA.json delete mode 100644 plugins/ssh-terminal/locales/translated/ar_SA.json delete mode 100644 plugins/ssh-terminal/locales/translated/bg_BG.json delete mode 100644 plugins/ssh-terminal/locales/translated/bn_BD.json delete mode 100644 plugins/ssh-terminal/locales/translated/ca_ES.json delete mode 100644 plugins/ssh-terminal/locales/translated/cs_CZ.json delete mode 100644 plugins/ssh-terminal/locales/translated/da_DK.json delete mode 100644 plugins/ssh-terminal/locales/translated/de_DE.json delete mode 100644 plugins/ssh-terminal/locales/translated/el_GR.json delete mode 100644 plugins/ssh-terminal/locales/translated/es_ES.json delete mode 100644 plugins/ssh-terminal/locales/translated/fi_FI.json delete mode 100644 plugins/ssh-terminal/locales/translated/fr_FR.json delete mode 100644 plugins/ssh-terminal/locales/translated/he_IL.json delete mode 100644 plugins/ssh-terminal/locales/translated/hi_IN.json delete mode 100644 plugins/ssh-terminal/locales/translated/hu_HU.json delete mode 100644 plugins/ssh-terminal/locales/translated/id_ID.json delete mode 100644 plugins/ssh-terminal/locales/translated/it_IT.json delete mode 100644 plugins/ssh-terminal/locales/translated/ja_JP.json delete mode 100644 plugins/ssh-terminal/locales/translated/ko_KR.json delete mode 100644 plugins/ssh-terminal/locales/translated/nl_NL.json delete mode 100644 plugins/ssh-terminal/locales/translated/no_NO.json delete mode 100644 plugins/ssh-terminal/locales/translated/pl_PL.json delete mode 100644 plugins/ssh-terminal/locales/translated/pt_BR.json delete mode 100644 plugins/ssh-terminal/locales/translated/pt_PT.json delete mode 100644 plugins/ssh-terminal/locales/translated/ro_RO.json delete mode 100644 plugins/ssh-terminal/locales/translated/ru_RU.json delete mode 100644 plugins/ssh-terminal/locales/translated/sr_SP.json delete mode 100644 plugins/ssh-terminal/locales/translated/sv_SE.json delete mode 100644 plugins/ssh-terminal/locales/translated/th_TH.json delete mode 100644 plugins/ssh-terminal/locales/translated/tr_TR.json delete mode 100644 plugins/ssh-terminal/locales/translated/uk_UA.json delete mode 100644 plugins/ssh-terminal/locales/translated/vi_VN.json delete mode 100644 plugins/ssh-terminal/locales/translated/zh_CN.json delete mode 100644 plugins/ssh-terminal/locales/translated/zh_TW.json delete mode 100644 plugins/ssh-terminal/manifest.json delete mode 100644 plugins/ssh-terminal/migrations/mysql/0001_adopt_command_history.sql delete mode 100644 plugins/ssh-terminal/migrations/postgres/0001_adopt_command_history.sql delete mode 100644 plugins/ssh-terminal/migrations/snapshot.json delete mode 100644 plugins/ssh-terminal/migrations/sqlite/0001_adopt_command_history.sql delete mode 100644 plugins/ssh-terminal/package.json delete mode 100644 plugins/ssh-terminal/src/backend/helpers.ts delete mode 100644 plugins/ssh-terminal/src/backend/history-repository.ts delete mode 100644 plugins/ssh-terminal/src/backend/host-import.ts delete mode 100644 plugins/ssh-terminal/src/backend/images/image-storage-settings.ts delete mode 100644 plugins/ssh-terminal/src/backend/images/image-storage.ts delete mode 100644 plugins/ssh-terminal/src/backend/images/image-utils.ts delete mode 100644 plugins/ssh-terminal/src/backend/index.ts delete mode 100644 plugins/ssh-terminal/src/backend/keyboard-prompt.ts delete mode 100644 plugins/ssh-terminal/src/backend/routes.ts delete mode 100644 plugins/ssh-terminal/src/backend/services.ts delete mode 100644 plugins/ssh-terminal/src/backend/session-manager.ts delete mode 100644 plugins/ssh-terminal/src/backend/settings-validation.ts delete mode 100644 plugins/ssh-terminal/src/backend/settings.ts delete mode 100644 plugins/ssh-terminal/src/backend/tables.ts delete mode 100644 plugins/ssh-terminal/src/backend/terminal-socket.ts delete mode 100644 plugins/ssh-terminal/src/frontend/TerminalTabWithRegistry.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/TerminalView.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/history/HistoryPanel.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/index.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/lib/keybinding-dispatch.ts delete mode 100644 plugins/ssh-terminal/src/frontend/lib/keybinding-editors.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/lib/mac-line-navigation.ts delete mode 100644 plugins/ssh-terminal/src/frontend/lib/terminal-local-echo.ts delete mode 100644 plugins/ssh-terminal/src/frontend/lib/terminal-syntax-highlighter.ts delete mode 100644 plugins/ssh-terminal/src/frontend/local-terminal/LocalTerminal.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/look/TerminalPreview.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/look/look-actions.ts delete mode 100644 plugins/ssh-terminal/src/frontend/look/terminal-font-zoom.ts delete mode 100644 plugins/ssh-terminal/src/frontend/look/terminal-global-styles.ts delete mode 100644 plugins/ssh-terminal/src/frontend/look/terminal-theme.ts delete mode 100644 plugins/ssh-terminal/src/frontend/look/terminal-themes.ts delete mode 100644 plugins/ssh-terminal/src/frontend/look/theme-preview.ts delete mode 100644 plugins/ssh-terminal/src/frontend/macros/MacrosPanel.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/macros/terminal-macros.ts delete mode 100644 plugins/ssh-terminal/src/frontend/session-registry.ts delete mode 100644 plugins/ssh-terminal/src/frontend/settings/HostTerminalSection.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/settings/HostTerminalSettings.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/settings/ImageStorageTest.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/settings/TouchInputSettings.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/settings/local-preferences-migration.ts delete mode 100644 plugins/ssh-terminal/src/frontend/ssh-tools/SshToolsPanel.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal-api.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal-settings.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/MobileTerminalKeyboard.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/Terminal.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/TerminalApp.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/TerminalTabContent.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/TerminalToolbar.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/TmuxSessionPicker.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/android-hardware-keyboard.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/command-history/CommandAutocomplete.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/command-history/CommandHistoryContext.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/command-history/useCommandTracker.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/font-size-storage.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/search/TerminalSearchBar.tsx delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-buffer-text.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-clipboard-actions.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-clipboard.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-helpers.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-image-path.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-image-upload.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-key-event.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-slots.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/terminal-types.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/toolbar-geometry.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/toolbar-settings.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/touch-input-settings-store.ts delete mode 100644 plugins/ssh-terminal/src/frontend/terminal/touch-wheel-coordinator.ts delete mode 100644 plugins/ssh-terminal/src/frontend/types.ts delete mode 100644 plugins/ssh-terminal/src/shared/terminal-settings.ts delete mode 100644 plugins/ssh-terminal/src/shared/touch-input-settings.ts delete mode 100644 plugins/ssh-terminal/tests/backend/activate.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/guest-socket.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/helpers.ts delete mode 100644 plugins/ssh-terminal/tests/backend/host-import.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/images/image-storage-settings.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/images/image-storage.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/images/image-utils.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/keyboard-prompt.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/memory-agent.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/migrations.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/routes.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/session-manager.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/settings-validation.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/ssh-dns.test.ts delete mode 100644 plugins/ssh-terminal/tests/backend/terminal-settings.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/activate.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/lib/keybinding-dispatch.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/lib/mac-line-navigation.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/lib/terminal-local-echo.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/lib/terminal-local-echo.xterm.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/lib/terminal-syntax-highlighter.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/look/terminal-font-zoom.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/look/terminal-global-styles.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/look/terminal-theme.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/macros/terminal-macros.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/settings/HostTerminalSettings.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/settings/TouchInputSettings.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/settings/local-preferences-migration.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/ssh-tools/SshToolsPanel.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/TerminalTabContent.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/TerminalToolbar.test.tsx delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/android-hardware-keyboard.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/android-ime.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/auto-reconnect.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/font-size-storage.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/ios-rapid-typing.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/reconnect-disconnected.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/shared-terminal-size.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-buffer-text.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-clipboard-actions.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-clipboard.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-image-path.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-image-upload-metadata.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-key-event.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/terminal-unmount.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/toolbar-settings.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/touch-wheel-coordinator.test.ts delete mode 100644 plugins/ssh-terminal/tests/frontend/terminal/touch-wheel-coordinator.xterm.test.ts delete mode 100644 plugins/ssh-terminal/tsconfig.json delete mode 100644 plugins/ssh-terminal/vitest.config.ts delete mode 100644 plugins/sso/.commitlintrc.json delete mode 100644 plugins/sso/.github/funding.yml delete mode 100644 plugins/sso/.github/pull_request_template.md delete mode 100644 plugins/sso/.gitignore delete mode 100644 plugins/sso/.husky/commit-msg delete mode 100644 plugins/sso/.husky/pre-commit delete mode 100644 plugins/sso/.prettierignore delete mode 100644 plugins/sso/.prettierrc delete mode 100644 plugins/sso/CHANGELOG.json delete mode 100644 plugins/sso/CONTRIBUTING.md delete mode 100644 plugins/sso/README.md delete mode 100644 plugins/sso/locales/en.json delete mode 100644 plugins/sso/locales/translated/af_ZA.json delete mode 100644 plugins/sso/locales/translated/ar_SA.json delete mode 100644 plugins/sso/locales/translated/bg_BG.json delete mode 100644 plugins/sso/locales/translated/bn_BD.json delete mode 100644 plugins/sso/locales/translated/ca_ES.json delete mode 100644 plugins/sso/locales/translated/cs_CZ.json delete mode 100644 plugins/sso/locales/translated/da_DK.json delete mode 100644 plugins/sso/locales/translated/de_DE.json delete mode 100644 plugins/sso/locales/translated/el_GR.json delete mode 100644 plugins/sso/locales/translated/es_ES.json delete mode 100644 plugins/sso/locales/translated/fi_FI.json delete mode 100644 plugins/sso/locales/translated/fr_FR.json delete mode 100644 plugins/sso/locales/translated/he_IL.json delete mode 100644 plugins/sso/locales/translated/hi_IN.json delete mode 100644 plugins/sso/locales/translated/hu_HU.json delete mode 100644 plugins/sso/locales/translated/id_ID.json delete mode 100644 plugins/sso/locales/translated/it_IT.json delete mode 100644 plugins/sso/locales/translated/ja_JP.json delete mode 100644 plugins/sso/locales/translated/ko_KR.json delete mode 100644 plugins/sso/locales/translated/nl_NL.json delete mode 100644 plugins/sso/locales/translated/no_NO.json delete mode 100644 plugins/sso/locales/translated/pl_PL.json delete mode 100644 plugins/sso/locales/translated/pt_BR.json delete mode 100644 plugins/sso/locales/translated/pt_PT.json delete mode 100644 plugins/sso/locales/translated/ro_RO.json delete mode 100644 plugins/sso/locales/translated/ru_RU.json delete mode 100644 plugins/sso/locales/translated/sr_SP.json delete mode 100644 plugins/sso/locales/translated/sv_SE.json delete mode 100644 plugins/sso/locales/translated/th_TH.json delete mode 100644 plugins/sso/locales/translated/tr_TR.json delete mode 100644 plugins/sso/locales/translated/uk_UA.json delete mode 100644 plugins/sso/locales/translated/vi_VN.json delete mode 100644 plugins/sso/locales/translated/zh_CN.json delete mode 100644 plugins/sso/locales/translated/zh_TW.json delete mode 100644 plugins/sso/manifest.json delete mode 100644 plugins/sso/migrations/mysql/0001_adopt_sso_providers.sql delete mode 100644 plugins/sso/migrations/mysql/0002_legacy_callback.sql delete mode 100644 plugins/sso/migrations/postgres/0001_adopt_sso_providers.sql delete mode 100644 plugins/sso/migrations/postgres/0002_legacy_callback.sql delete mode 100644 plugins/sso/migrations/snapshot.json delete mode 100644 plugins/sso/migrations/sqlite/0001_adopt_sso_providers.sql delete mode 100644 plugins/sso/migrations/sqlite/0002_legacy_callback.sql delete mode 100644 plugins/sso/package.json delete mode 100644 plugins/sso/src/backend/index.ts delete mode 100644 plugins/sso/src/backend/login.ts delete mode 100644 plugins/sso/src/backend/oidc-protocol.ts delete mode 100644 plugins/sso/src/backend/providers.ts delete mode 100644 plugins/sso/src/backend/routes.ts delete mode 100644 plugins/sso/src/backend/tables.ts delete mode 100644 plugins/sso/src/backend/types.ts delete mode 100644 plugins/sso/src/frontend/OidcLoginButtons.tsx delete mode 100644 plugins/sso/src/frontend/ProvidersSetting.tsx delete mode 100644 plugins/sso/src/frontend/index.tsx delete mode 100644 plugins/sso/src/frontend/sso-api.ts delete mode 100644 plugins/sso/tests/backend/helpers.ts delete mode 100644 plugins/sso/tests/backend/legacy-urls.test.ts delete mode 100644 plugins/sso/tests/backend/oidc-protocol.test.ts delete mode 100644 plugins/sso/tests/backend/sso.test.ts delete mode 100644 plugins/sso/tests/frontend/activate.test.tsx delete mode 100644 plugins/sso/tsconfig.json delete mode 100644 plugins/sso/vitest.config.ts delete mode 100644 plugins/step-ca/.commitlintrc.json delete mode 100644 plugins/step-ca/.github/funding.yml delete mode 100644 plugins/step-ca/.github/pull_request_template.md delete mode 100644 plugins/step-ca/.gitignore delete mode 100644 plugins/step-ca/.husky/commit-msg delete mode 100644 plugins/step-ca/.husky/pre-commit delete mode 100644 plugins/step-ca/.prettierignore delete mode 100644 plugins/step-ca/.prettierrc delete mode 100644 plugins/step-ca/CHANGELOG.json delete mode 100644 plugins/step-ca/CONTRIBUTING.md delete mode 100644 plugins/step-ca/README.md delete mode 100644 plugins/step-ca/locales/en.json delete mode 100644 plugins/step-ca/locales/translated/af_ZA.json delete mode 100644 plugins/step-ca/locales/translated/ar_SA.json delete mode 100644 plugins/step-ca/locales/translated/bg_BG.json delete mode 100644 plugins/step-ca/locales/translated/bn_BD.json delete mode 100644 plugins/step-ca/locales/translated/ca_ES.json delete mode 100644 plugins/step-ca/locales/translated/cs_CZ.json delete mode 100644 plugins/step-ca/locales/translated/da_DK.json delete mode 100644 plugins/step-ca/locales/translated/de_DE.json delete mode 100644 plugins/step-ca/locales/translated/el_GR.json delete mode 100644 plugins/step-ca/locales/translated/es_ES.json delete mode 100644 plugins/step-ca/locales/translated/fi_FI.json delete mode 100644 plugins/step-ca/locales/translated/fr_FR.json delete mode 100644 plugins/step-ca/locales/translated/he_IL.json delete mode 100644 plugins/step-ca/locales/translated/hi_IN.json delete mode 100644 plugins/step-ca/locales/translated/hu_HU.json delete mode 100644 plugins/step-ca/locales/translated/id_ID.json delete mode 100644 plugins/step-ca/locales/translated/it_IT.json delete mode 100644 plugins/step-ca/locales/translated/ja_JP.json delete mode 100644 plugins/step-ca/locales/translated/ko_KR.json delete mode 100644 plugins/step-ca/locales/translated/nl_NL.json delete mode 100644 plugins/step-ca/locales/translated/no_NO.json delete mode 100644 plugins/step-ca/locales/translated/pl_PL.json delete mode 100644 plugins/step-ca/locales/translated/pt_BR.json delete mode 100644 plugins/step-ca/locales/translated/pt_PT.json delete mode 100644 plugins/step-ca/locales/translated/ro_RO.json delete mode 100644 plugins/step-ca/locales/translated/ru_RU.json delete mode 100644 plugins/step-ca/locales/translated/sr_SP.json delete mode 100644 plugins/step-ca/locales/translated/sv_SE.json delete mode 100644 plugins/step-ca/locales/translated/th_TH.json delete mode 100644 plugins/step-ca/locales/translated/tr_TR.json delete mode 100644 plugins/step-ca/locales/translated/uk_UA.json delete mode 100644 plugins/step-ca/locales/translated/vi_VN.json delete mode 100644 plugins/step-ca/locales/translated/zh_CN.json delete mode 100644 plugins/step-ca/locales/translated/zh_TW.json delete mode 100644 plugins/step-ca/manifest.json delete mode 100644 plugins/step-ca/migrations/mysql/0001_create_certs.sql delete mode 100644 plugins/step-ca/migrations/postgres/0001_create_certs.sql delete mode 100644 plugins/step-ca/migrations/snapshot.json delete mode 100644 plugins/step-ca/migrations/sqlite/0001_create_certs.sql delete mode 100644 plugins/step-ca/package.json delete mode 100644 plugins/step-ca/src/backend/auth-session.ts delete mode 100644 plugins/step-ca/src/backend/cert-store.ts delete mode 100644 plugins/step-ca/src/backend/client.ts delete mode 100644 plugins/step-ca/src/backend/index.ts delete mode 100644 plugins/step-ca/src/backend/provider.ts delete mode 100644 plugins/step-ca/src/backend/routes.ts delete mode 100644 plugins/step-ca/src/backend/runtime.ts delete mode 100644 plugins/step-ca/src/backend/tables.ts delete mode 100644 plugins/step-ca/src/frontend/RedirectUriSetting.tsx delete mode 100644 plugins/step-ca/src/frontend/StepCaAuthEditor.tsx delete mode 100644 plugins/step-ca/src/frontend/StepCaDialog.tsx delete mode 100644 plugins/step-ca/src/frontend/StepCaOverlay.tsx delete mode 100644 plugins/step-ca/src/frontend/index.tsx delete mode 100644 plugins/step-ca/tests/backend/client.test.ts delete mode 100644 plugins/step-ca/tests/backend/fake-ca.ts delete mode 100644 plugins/step-ca/tests/backend/helpers.ts delete mode 100644 plugins/step-ca/tests/backend/legacy-urls.test.ts delete mode 100644 plugins/step-ca/tests/backend/settings-validation.test.ts delete mode 100644 plugins/step-ca/tests/backend/step-ca.test.ts delete mode 100644 plugins/step-ca/tests/frontend/activate.test.tsx delete mode 100644 plugins/step-ca/tsconfig.json delete mode 100644 plugins/step-ca/vitest.config.ts delete mode 100644 plugins/tailscale/.commitlintrc.json delete mode 100644 plugins/tailscale/.github/funding.yml delete mode 100644 plugins/tailscale/.github/pull_request_template.md delete mode 100644 plugins/tailscale/.gitignore delete mode 100644 plugins/tailscale/.husky/commit-msg delete mode 100644 plugins/tailscale/.husky/pre-commit delete mode 100644 plugins/tailscale/.prettierignore delete mode 100644 plugins/tailscale/.prettierrc delete mode 100644 plugins/tailscale/CHANGELOG.json delete mode 100644 plugins/tailscale/CONTRIBUTING.md delete mode 100644 plugins/tailscale/README.md delete mode 100644 plugins/tailscale/locales/en.json delete mode 100644 plugins/tailscale/locales/translated/af_ZA.json delete mode 100644 plugins/tailscale/locales/translated/ar_SA.json delete mode 100644 plugins/tailscale/locales/translated/bg_BG.json delete mode 100644 plugins/tailscale/locales/translated/bn_BD.json delete mode 100644 plugins/tailscale/locales/translated/ca_ES.json delete mode 100644 plugins/tailscale/locales/translated/cs_CZ.json delete mode 100644 plugins/tailscale/locales/translated/da_DK.json delete mode 100644 plugins/tailscale/locales/translated/de_DE.json delete mode 100644 plugins/tailscale/locales/translated/el_GR.json delete mode 100644 plugins/tailscale/locales/translated/es_ES.json delete mode 100644 plugins/tailscale/locales/translated/fi_FI.json delete mode 100644 plugins/tailscale/locales/translated/fr_FR.json delete mode 100644 plugins/tailscale/locales/translated/he_IL.json delete mode 100644 plugins/tailscale/locales/translated/hi_IN.json delete mode 100644 plugins/tailscale/locales/translated/hu_HU.json delete mode 100644 plugins/tailscale/locales/translated/id_ID.json delete mode 100644 plugins/tailscale/locales/translated/it_IT.json delete mode 100644 plugins/tailscale/locales/translated/ja_JP.json delete mode 100644 plugins/tailscale/locales/translated/ko_KR.json delete mode 100644 plugins/tailscale/locales/translated/nl_NL.json delete mode 100644 plugins/tailscale/locales/translated/no_NO.json delete mode 100644 plugins/tailscale/locales/translated/pl_PL.json delete mode 100644 plugins/tailscale/locales/translated/pt_BR.json delete mode 100644 plugins/tailscale/locales/translated/pt_PT.json delete mode 100644 plugins/tailscale/locales/translated/ro_RO.json delete mode 100644 plugins/tailscale/locales/translated/ru_RU.json delete mode 100644 plugins/tailscale/locales/translated/sr_SP.json delete mode 100644 plugins/tailscale/locales/translated/sv_SE.json delete mode 100644 plugins/tailscale/locales/translated/th_TH.json delete mode 100644 plugins/tailscale/locales/translated/tr_TR.json delete mode 100644 plugins/tailscale/locales/translated/uk_UA.json delete mode 100644 plugins/tailscale/locales/translated/vi_VN.json delete mode 100644 plugins/tailscale/locales/translated/zh_CN.json delete mode 100644 plugins/tailscale/locales/translated/zh_TW.json delete mode 100644 plugins/tailscale/manifest.json delete mode 100644 plugins/tailscale/package.json delete mode 100644 plugins/tailscale/src/backend/host-metrics-manager.ts delete mode 100644 plugins/tailscale/src/backend/index.ts delete mode 100644 plugins/tailscale/src/backend/routes.ts delete mode 100644 plugins/tailscale/src/backend/ssh-auth-provider.ts delete mode 100644 plugins/tailscale/src/backend/ssh.ts delete mode 100644 plugins/tailscale/src/backend/tailscale-check.ts delete mode 100644 plugins/tailscale/src/frontend/TailscaleAuthEditor.tsx delete mode 100644 plugins/tailscale/src/frontend/TailscaleCheckDialog.tsx delete mode 100644 plugins/tailscale/src/frontend/TailscaleCheckOverlay.tsx delete mode 100644 plugins/tailscale/src/frontend/TailscaleDevicesPanel.tsx delete mode 100644 plugins/tailscale/src/frontend/TailscaleDevicesStatus.tsx delete mode 100644 plugins/tailscale/src/frontend/TailscaleManagerCard.tsx delete mode 100644 plugins/tailscale/src/frontend/index.tsx delete mode 100644 plugins/tailscale/src/frontend/tailscale-api.ts delete mode 100644 plugins/tailscale/src/frontend/useTailscaleManager.ts delete mode 100644 plugins/tailscale/tests/backend/host-metrics-manager.test.ts delete mode 100644 plugins/tailscale/tests/backend/permissions.test.ts delete mode 100644 plugins/tailscale/tests/backend/settings.test.ts delete mode 100644 plugins/tailscale/tests/backend/ssh-auth-provider.test.ts delete mode 100644 plugins/tailscale/tests/backend/tailscale-check.test.ts delete mode 100644 plugins/tailscale/tests/frontend/TailscaleDevicesPanel.test.tsx delete mode 100644 plugins/tailscale/tests/frontend/activate.test.tsx delete mode 100644 plugins/tailscale/tests/frontend/tailscale-api.test.ts delete mode 100644 plugins/tailscale/tsconfig.json delete mode 100644 plugins/tailscale/vitest.config.ts delete mode 100644 plugins/telemetry/.commitlintrc.json delete mode 100644 plugins/telemetry/.github/funding.yml delete mode 100644 plugins/telemetry/.github/pull_request_template.md delete mode 100644 plugins/telemetry/.gitignore delete mode 100644 plugins/telemetry/.husky/commit-msg delete mode 100644 plugins/telemetry/.husky/pre-commit delete mode 100644 plugins/telemetry/.prettierignore delete mode 100644 plugins/telemetry/.prettierrc delete mode 100644 plugins/telemetry/CHANGELOG.json delete mode 100644 plugins/telemetry/CONTRIBUTING.md delete mode 100644 plugins/telemetry/README.md delete mode 100644 plugins/telemetry/locales/en.json delete mode 100644 plugins/telemetry/locales/translated/af_ZA.json delete mode 100644 plugins/telemetry/locales/translated/ar_SA.json delete mode 100644 plugins/telemetry/locales/translated/bg_BG.json delete mode 100644 plugins/telemetry/locales/translated/bn_BD.json delete mode 100644 plugins/telemetry/locales/translated/ca_ES.json delete mode 100644 plugins/telemetry/locales/translated/cs_CZ.json delete mode 100644 plugins/telemetry/locales/translated/da_DK.json delete mode 100644 plugins/telemetry/locales/translated/de_DE.json delete mode 100644 plugins/telemetry/locales/translated/el_GR.json delete mode 100644 plugins/telemetry/locales/translated/es_ES.json delete mode 100644 plugins/telemetry/locales/translated/fi_FI.json delete mode 100644 plugins/telemetry/locales/translated/fr_FR.json delete mode 100644 plugins/telemetry/locales/translated/he_IL.json delete mode 100644 plugins/telemetry/locales/translated/hi_IN.json delete mode 100644 plugins/telemetry/locales/translated/hu_HU.json delete mode 100644 plugins/telemetry/locales/translated/id_ID.json delete mode 100644 plugins/telemetry/locales/translated/it_IT.json delete mode 100644 plugins/telemetry/locales/translated/ja_JP.json delete mode 100644 plugins/telemetry/locales/translated/ko_KR.json delete mode 100644 plugins/telemetry/locales/translated/nl_NL.json delete mode 100644 plugins/telemetry/locales/translated/no_NO.json delete mode 100644 plugins/telemetry/locales/translated/pl_PL.json delete mode 100644 plugins/telemetry/locales/translated/pt_BR.json delete mode 100644 plugins/telemetry/locales/translated/pt_PT.json delete mode 100644 plugins/telemetry/locales/translated/ro_RO.json delete mode 100644 plugins/telemetry/locales/translated/ru_RU.json delete mode 100644 plugins/telemetry/locales/translated/sr_SP.json delete mode 100644 plugins/telemetry/locales/translated/sv_SE.json delete mode 100644 plugins/telemetry/locales/translated/th_TH.json delete mode 100644 plugins/telemetry/locales/translated/tr_TR.json delete mode 100644 plugins/telemetry/locales/translated/uk_UA.json delete mode 100644 plugins/telemetry/locales/translated/vi_VN.json delete mode 100644 plugins/telemetry/locales/translated/zh_CN.json delete mode 100644 plugins/telemetry/locales/translated/zh_TW.json delete mode 100644 plugins/telemetry/manifest.json delete mode 100644 plugins/telemetry/package.json delete mode 100644 plugins/telemetry/src/backend/collect.ts delete mode 100644 plugins/telemetry/src/backend/config.ts delete mode 100644 plugins/telemetry/src/backend/index.ts delete mode 100644 plugins/telemetry/src/backend/reporter.ts delete mode 100644 plugins/telemetry/src/backend/routes.ts delete mode 100644 plugins/telemetry/src/backend/usage.ts delete mode 100644 plugins/telemetry/src/frontend/TelemetryStatusSetting.tsx delete mode 100644 plugins/telemetry/src/frontend/index.tsx delete mode 100644 plugins/telemetry/src/frontend/tracker.ts delete mode 100644 plugins/telemetry/tests/backend/telemetry.test.ts delete mode 100644 plugins/telemetry/tests/frontend/telemetry.test.tsx delete mode 100644 plugins/telemetry/tsconfig.json delete mode 100644 plugins/telemetry/vitest.config.ts delete mode 100644 plugins/termix-identity/.commitlintrc.json delete mode 100644 plugins/termix-identity/.github/funding.yml delete mode 100644 plugins/termix-identity/.github/pull_request_template.md delete mode 100644 plugins/termix-identity/.gitignore delete mode 100644 plugins/termix-identity/.husky/commit-msg delete mode 100644 plugins/termix-identity/.husky/pre-commit delete mode 100644 plugins/termix-identity/.prettierignore delete mode 100644 plugins/termix-identity/.prettierrc delete mode 100644 plugins/termix-identity/CHANGELOG.json delete mode 100644 plugins/termix-identity/CONTRIBUTING.md delete mode 100644 plugins/termix-identity/README.md delete mode 100644 plugins/termix-identity/locales/en.json delete mode 100644 plugins/termix-identity/locales/translated/af_ZA.json delete mode 100644 plugins/termix-identity/locales/translated/ar_SA.json delete mode 100644 plugins/termix-identity/locales/translated/bg_BG.json delete mode 100644 plugins/termix-identity/locales/translated/bn_BD.json delete mode 100644 plugins/termix-identity/locales/translated/ca_ES.json delete mode 100644 plugins/termix-identity/locales/translated/cs_CZ.json delete mode 100644 plugins/termix-identity/locales/translated/da_DK.json delete mode 100644 plugins/termix-identity/locales/translated/de_DE.json delete mode 100644 plugins/termix-identity/locales/translated/el_GR.json delete mode 100644 plugins/termix-identity/locales/translated/es_ES.json delete mode 100644 plugins/termix-identity/locales/translated/fi_FI.json delete mode 100644 plugins/termix-identity/locales/translated/fr_FR.json delete mode 100644 plugins/termix-identity/locales/translated/he_IL.json delete mode 100644 plugins/termix-identity/locales/translated/hi_IN.json delete mode 100644 plugins/termix-identity/locales/translated/hu_HU.json delete mode 100644 plugins/termix-identity/locales/translated/id_ID.json delete mode 100644 plugins/termix-identity/locales/translated/it_IT.json delete mode 100644 plugins/termix-identity/locales/translated/ja_JP.json delete mode 100644 plugins/termix-identity/locales/translated/ko_KR.json delete mode 100644 plugins/termix-identity/locales/translated/nl_NL.json delete mode 100644 plugins/termix-identity/locales/translated/no_NO.json delete mode 100644 plugins/termix-identity/locales/translated/pl_PL.json delete mode 100644 plugins/termix-identity/locales/translated/pt_BR.json delete mode 100644 plugins/termix-identity/locales/translated/pt_PT.json delete mode 100644 plugins/termix-identity/locales/translated/ro_RO.json delete mode 100644 plugins/termix-identity/locales/translated/ru_RU.json delete mode 100644 plugins/termix-identity/locales/translated/sr_SP.json delete mode 100644 plugins/termix-identity/locales/translated/sv_SE.json delete mode 100644 plugins/termix-identity/locales/translated/th_TH.json delete mode 100644 plugins/termix-identity/locales/translated/tr_TR.json delete mode 100644 plugins/termix-identity/locales/translated/uk_UA.json delete mode 100644 plugins/termix-identity/locales/translated/vi_VN.json delete mode 100644 plugins/termix-identity/locales/translated/zh_CN.json delete mode 100644 plugins/termix-identity/locales/translated/zh_TW.json delete mode 100644 plugins/termix-identity/manifest.json delete mode 100644 plugins/termix-identity/migrations/mysql/0001_adopt_termix_identity_tables.sql delete mode 100644 plugins/termix-identity/migrations/postgres/0001_adopt_termix_identity_tables.sql delete mode 100644 plugins/termix-identity/migrations/snapshot.json delete mode 100644 plugins/termix-identity/migrations/sqlite/0001_adopt_termix_identity_tables.sql delete mode 100644 plugins/termix-identity/package.json delete mode 100644 plugins/termix-identity/src/backend/certificate.ts delete mode 100644 plugins/termix-identity/src/backend/handles.ts delete mode 100644 plugins/termix-identity/src/backend/index.ts delete mode 100644 plugins/termix-identity/src/backend/keys.ts delete mode 100644 plugins/termix-identity/src/backend/resolver.ts delete mode 100644 plugins/termix-identity/src/backend/routes.ts delete mode 100644 plugins/termix-identity/src/backend/store.ts delete mode 100644 plugins/termix-identity/src/backend/tables.ts delete mode 100644 plugins/termix-identity/src/frontend/TermixIdPanel.tsx delete mode 100644 plugins/termix-identity/src/frontend/api.ts delete mode 100644 plugins/termix-identity/src/frontend/index.tsx delete mode 100644 plugins/termix-identity/src/frontend/linked-store.ts delete mode 100644 plugins/termix-identity/src/frontend/types.ts delete mode 100644 plugins/termix-identity/tests/backend/certificate.test.ts delete mode 100644 plugins/termix-identity/tests/backend/helpers.ts delete mode 100644 plugins/termix-identity/tests/backend/keys.test.ts delete mode 100644 plugins/termix-identity/tests/backend/legacy-urls.test.ts delete mode 100644 plugins/termix-identity/tests/backend/migrations.test.ts delete mode 100644 plugins/termix-identity/tests/backend/termix-identity.test.ts delete mode 100644 plugins/termix-identity/tests/frontend/activate.test.tsx delete mode 100644 plugins/termix-identity/tsconfig.json delete mode 100644 plugins/termix-identity/vitest.config.ts delete mode 100644 plugins/tmux-monitor/.commitlintrc.json delete mode 100644 plugins/tmux-monitor/.github/funding.yml delete mode 100644 plugins/tmux-monitor/.github/pull_request_template.md delete mode 100644 plugins/tmux-monitor/.gitignore delete mode 100644 plugins/tmux-monitor/.husky/commit-msg delete mode 100644 plugins/tmux-monitor/.husky/pre-commit delete mode 100644 plugins/tmux-monitor/.prettierignore delete mode 100644 plugins/tmux-monitor/.prettierrc delete mode 100644 plugins/tmux-monitor/CHANGELOG.json delete mode 100644 plugins/tmux-monitor/CONTRIBUTING.md delete mode 100644 plugins/tmux-monitor/README.md delete mode 100644 plugins/tmux-monitor/locales/en.json delete mode 100644 plugins/tmux-monitor/locales/translated/af_ZA.json delete mode 100644 plugins/tmux-monitor/locales/translated/ar_SA.json delete mode 100644 plugins/tmux-monitor/locales/translated/bg_BG.json delete mode 100644 plugins/tmux-monitor/locales/translated/bn_BD.json delete mode 100644 plugins/tmux-monitor/locales/translated/ca_ES.json delete mode 100644 plugins/tmux-monitor/locales/translated/cs_CZ.json delete mode 100644 plugins/tmux-monitor/locales/translated/da_DK.json delete mode 100644 plugins/tmux-monitor/locales/translated/de_DE.json delete mode 100644 plugins/tmux-monitor/locales/translated/el_GR.json delete mode 100644 plugins/tmux-monitor/locales/translated/es_ES.json delete mode 100644 plugins/tmux-monitor/locales/translated/fi_FI.json delete mode 100644 plugins/tmux-monitor/locales/translated/fr_FR.json delete mode 100644 plugins/tmux-monitor/locales/translated/he_IL.json delete mode 100644 plugins/tmux-monitor/locales/translated/hi_IN.json delete mode 100644 plugins/tmux-monitor/locales/translated/hu_HU.json delete mode 100644 plugins/tmux-monitor/locales/translated/id_ID.json delete mode 100644 plugins/tmux-monitor/locales/translated/it_IT.json delete mode 100644 plugins/tmux-monitor/locales/translated/ja_JP.json delete mode 100644 plugins/tmux-monitor/locales/translated/ko_KR.json delete mode 100644 plugins/tmux-monitor/locales/translated/nl_NL.json delete mode 100644 plugins/tmux-monitor/locales/translated/no_NO.json delete mode 100644 plugins/tmux-monitor/locales/translated/pl_PL.json delete mode 100644 plugins/tmux-monitor/locales/translated/pt_BR.json delete mode 100644 plugins/tmux-monitor/locales/translated/pt_PT.json delete mode 100644 plugins/tmux-monitor/locales/translated/ro_RO.json delete mode 100644 plugins/tmux-monitor/locales/translated/ru_RU.json delete mode 100644 plugins/tmux-monitor/locales/translated/sr_SP.json delete mode 100644 plugins/tmux-monitor/locales/translated/sv_SE.json delete mode 100644 plugins/tmux-monitor/locales/translated/th_TH.json delete mode 100644 plugins/tmux-monitor/locales/translated/tr_TR.json delete mode 100644 plugins/tmux-monitor/locales/translated/uk_UA.json delete mode 100644 plugins/tmux-monitor/locales/translated/vi_VN.json delete mode 100644 plugins/tmux-monitor/locales/translated/zh_CN.json delete mode 100644 plugins/tmux-monitor/locales/translated/zh_TW.json delete mode 100644 plugins/tmux-monitor/manifest.json delete mode 100644 plugins/tmux-monitor/migrations/mysql/0001_adopt_tmux_session_tags.sql delete mode 100644 plugins/tmux-monitor/migrations/postgres/0001_adopt_tmux_session_tags.sql delete mode 100644 plugins/tmux-monitor/migrations/snapshot.json delete mode 100644 plugins/tmux-monitor/migrations/sqlite/0001_adopt_tmux_session_tags.sql delete mode 100644 plugins/tmux-monitor/package.json delete mode 100644 plugins/tmux-monitor/src/backend/index.ts delete mode 100644 plugins/tmux-monitor/src/backend/monitor-helpers.ts delete mode 100644 plugins/tmux-monitor/src/backend/repository.ts delete mode 100644 plugins/tmux-monitor/src/backend/routes.ts delete mode 100644 plugins/tmux-monitor/src/backend/service.ts delete mode 100644 plugins/tmux-monitor/src/backend/tables.ts delete mode 100644 plugins/tmux-monitor/src/backend/tmux-commands.ts delete mode 100644 plugins/tmux-monitor/src/frontend/HostOverview.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/HostTmuxMonitorSection.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/PanePreview.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/SearchResults.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/SessionTree.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/TmuxMonitor.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/TmuxMonitorApp.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/api.ts delete mode 100644 plugins/tmux-monitor/src/frontend/format.ts delete mode 100644 plugins/tmux-monitor/src/frontend/host-tmux-monitor.ts delete mode 100644 plugins/tmux-monitor/src/frontend/index.tsx delete mode 100644 plugins/tmux-monitor/src/frontend/monitor-storage.ts delete mode 100644 plugins/tmux-monitor/src/frontend/poll-queue.ts delete mode 100644 plugins/tmux-monitor/src/frontend/types.ts delete mode 100644 plugins/tmux-monitor/src/frontend/use-adaptive-polling.ts delete mode 100644 plugins/tmux-monitor/tests/backend/helpers.ts delete mode 100644 plugins/tmux-monitor/tests/backend/migrations.test.ts delete mode 100644 plugins/tmux-monitor/tests/backend/monitor-helpers.test.ts delete mode 100644 plugins/tmux-monitor/tests/backend/repository.test.ts delete mode 100644 plugins/tmux-monitor/tests/backend/routes.test.ts delete mode 100644 plugins/tmux-monitor/tests/backend/service.test.ts delete mode 100644 plugins/tmux-monitor/tests/backend/tmux-commands.test.ts delete mode 100644 plugins/tmux-monitor/tests/frontend/activate.test.tsx delete mode 100644 plugins/tmux-monitor/tests/frontend/host-tmux-monitor.test.ts delete mode 100644 plugins/tmux-monitor/tests/frontend/monitor-storage.test.ts delete mode 100644 plugins/tmux-monitor/tests/frontend/multi-host-monitor.test.tsx delete mode 100644 plugins/tmux-monitor/tests/frontend/poll-queue.test.ts delete mode 100644 plugins/tmux-monitor/tests/frontend/session-tree-selection.test.tsx delete mode 100644 plugins/tmux-monitor/tsconfig.json delete mode 100644 plugins/tmux-monitor/vitest.config.ts delete mode 100644 plugins/totp/.commitlintrc.json delete mode 100644 plugins/totp/.github/funding.yml delete mode 100644 plugins/totp/.github/pull_request_template.md delete mode 100644 plugins/totp/.gitignore delete mode 100644 plugins/totp/.husky/commit-msg delete mode 100644 plugins/totp/.husky/pre-commit delete mode 100644 plugins/totp/.prettierignore delete mode 100644 plugins/totp/.prettierrc delete mode 100644 plugins/totp/CHANGELOG.json delete mode 100644 plugins/totp/CONTRIBUTING.md delete mode 100644 plugins/totp/README.md delete mode 100644 plugins/totp/locales/en.json delete mode 100644 plugins/totp/locales/translated/af_ZA.json delete mode 100644 plugins/totp/locales/translated/ar_SA.json delete mode 100644 plugins/totp/locales/translated/bg_BG.json delete mode 100644 plugins/totp/locales/translated/bn_BD.json delete mode 100644 plugins/totp/locales/translated/ca_ES.json delete mode 100644 plugins/totp/locales/translated/cs_CZ.json delete mode 100644 plugins/totp/locales/translated/da_DK.json delete mode 100644 plugins/totp/locales/translated/de_DE.json delete mode 100644 plugins/totp/locales/translated/el_GR.json delete mode 100644 plugins/totp/locales/translated/es_ES.json delete mode 100644 plugins/totp/locales/translated/fi_FI.json delete mode 100644 plugins/totp/locales/translated/fr_FR.json delete mode 100644 plugins/totp/locales/translated/he_IL.json delete mode 100644 plugins/totp/locales/translated/hi_IN.json delete mode 100644 plugins/totp/locales/translated/hu_HU.json delete mode 100644 plugins/totp/locales/translated/id_ID.json delete mode 100644 plugins/totp/locales/translated/it_IT.json delete mode 100644 plugins/totp/locales/translated/ja_JP.json delete mode 100644 plugins/totp/locales/translated/ko_KR.json delete mode 100644 plugins/totp/locales/translated/nl_NL.json delete mode 100644 plugins/totp/locales/translated/no_NO.json delete mode 100644 plugins/totp/locales/translated/pl_PL.json delete mode 100644 plugins/totp/locales/translated/pt_BR.json delete mode 100644 plugins/totp/locales/translated/pt_PT.json delete mode 100644 plugins/totp/locales/translated/ro_RO.json delete mode 100644 plugins/totp/locales/translated/ru_RU.json delete mode 100644 plugins/totp/locales/translated/sr_SP.json delete mode 100644 plugins/totp/locales/translated/sv_SE.json delete mode 100644 plugins/totp/locales/translated/th_TH.json delete mode 100644 plugins/totp/locales/translated/tr_TR.json delete mode 100644 plugins/totp/locales/translated/uk_UA.json delete mode 100644 plugins/totp/locales/translated/vi_VN.json delete mode 100644 plugins/totp/locales/translated/zh_CN.json delete mode 100644 plugins/totp/locales/translated/zh_TW.json delete mode 100644 plugins/totp/manifest.json delete mode 100644 plugins/totp/migrations/mysql/0001_create_enrollments.sql delete mode 100644 plugins/totp/migrations/postgres/0001_create_enrollments.sql delete mode 100644 plugins/totp/migrations/snapshot.json delete mode 100644 plugins/totp/migrations/sqlite/0001_create_enrollments.sql delete mode 100644 plugins/totp/package.json delete mode 100644 plugins/totp/src/backend/index.ts delete mode 100644 plugins/totp/src/backend/repository.ts delete mode 100644 plugins/totp/src/backend/routes.ts delete mode 100644 plugins/totp/src/backend/tables.ts delete mode 100644 plugins/totp/src/backend/totp.ts delete mode 100644 plugins/totp/src/frontend/TotpChallenge.tsx delete mode 100644 plugins/totp/src/frontend/TotpEnrollment.tsx delete mode 100644 plugins/totp/src/frontend/index.tsx delete mode 100644 plugins/totp/src/frontend/totp-input.ts delete mode 100644 plugins/totp/tests/backend/helpers.ts delete mode 100644 plugins/totp/tests/backend/totp.test.ts delete mode 100644 plugins/totp/tests/frontend/activate.test.tsx delete mode 100644 plugins/totp/tsconfig.json delete mode 100644 plugins/totp/vitest.config.ts delete mode 100644 plugins/tunnels/.commitlintrc.json delete mode 100644 plugins/tunnels/.github/funding.yml delete mode 100644 plugins/tunnels/.github/pull_request_template.md delete mode 100644 plugins/tunnels/.gitignore delete mode 100644 plugins/tunnels/.husky/commit-msg delete mode 100644 plugins/tunnels/.husky/pre-commit delete mode 100644 plugins/tunnels/.prettierignore delete mode 100644 plugins/tunnels/.prettierrc delete mode 100644 plugins/tunnels/CHANGELOG.json delete mode 100644 plugins/tunnels/CONTRIBUTING.md delete mode 100644 plugins/tunnels/README.md delete mode 100644 plugins/tunnels/locales/en.json delete mode 100644 plugins/tunnels/locales/translated/af_ZA.json delete mode 100644 plugins/tunnels/locales/translated/ar_SA.json delete mode 100644 plugins/tunnels/locales/translated/bg_BG.json delete mode 100644 plugins/tunnels/locales/translated/bn_BD.json delete mode 100644 plugins/tunnels/locales/translated/ca_ES.json delete mode 100644 plugins/tunnels/locales/translated/cs_CZ.json delete mode 100644 plugins/tunnels/locales/translated/da_DK.json delete mode 100644 plugins/tunnels/locales/translated/de_DE.json delete mode 100644 plugins/tunnels/locales/translated/el_GR.json delete mode 100644 plugins/tunnels/locales/translated/es_ES.json delete mode 100644 plugins/tunnels/locales/translated/fi_FI.json delete mode 100644 plugins/tunnels/locales/translated/fr_FR.json delete mode 100644 plugins/tunnels/locales/translated/he_IL.json delete mode 100644 plugins/tunnels/locales/translated/hi_IN.json delete mode 100644 plugins/tunnels/locales/translated/hu_HU.json delete mode 100644 plugins/tunnels/locales/translated/id_ID.json delete mode 100644 plugins/tunnels/locales/translated/it_IT.json delete mode 100644 plugins/tunnels/locales/translated/ja_JP.json delete mode 100644 plugins/tunnels/locales/translated/ko_KR.json delete mode 100644 plugins/tunnels/locales/translated/nl_NL.json delete mode 100644 plugins/tunnels/locales/translated/no_NO.json delete mode 100644 plugins/tunnels/locales/translated/pl_PL.json delete mode 100644 plugins/tunnels/locales/translated/pt_BR.json delete mode 100644 plugins/tunnels/locales/translated/pt_PT.json delete mode 100644 plugins/tunnels/locales/translated/ro_RO.json delete mode 100644 plugins/tunnels/locales/translated/ru_RU.json delete mode 100644 plugins/tunnels/locales/translated/sr_SP.json delete mode 100644 plugins/tunnels/locales/translated/sv_SE.json delete mode 100644 plugins/tunnels/locales/translated/th_TH.json delete mode 100644 plugins/tunnels/locales/translated/tr_TR.json delete mode 100644 plugins/tunnels/locales/translated/uk_UA.json delete mode 100644 plugins/tunnels/locales/translated/vi_VN.json delete mode 100644 plugins/tunnels/locales/translated/zh_CN.json delete mode 100644 plugins/tunnels/locales/translated/zh_TW.json delete mode 100644 plugins/tunnels/manifest.json delete mode 100644 plugins/tunnels/migrations/mysql/0001_adopt_c2s_tunnel_presets.sql delete mode 100644 plugins/tunnels/migrations/postgres/0001_adopt_c2s_tunnel_presets.sql delete mode 100644 plugins/tunnels/migrations/snapshot.json delete mode 100644 plugins/tunnels/migrations/sqlite/0001_adopt_c2s_tunnel_presets.sql delete mode 100644 plugins/tunnels/package.json delete mode 100644 plugins/tunnels/src/backend/authorize.ts delete mode 100644 plugins/tunnels/src/backend/autostart.ts delete mode 100644 plugins/tunnels/src/backend/c2s-auth.ts delete mode 100644 plugins/tunnels/src/backend/c2s-connections.ts delete mode 100644 plugins/tunnels/src/backend/c2s-relay.ts delete mode 100644 plugins/tunnels/src/backend/config.ts delete mode 100644 plugins/tunnels/src/backend/index.ts delete mode 100644 plugins/tunnels/src/backend/manager.ts delete mode 100644 plugins/tunnels/src/backend/repository.ts delete mode 100644 plugins/tunnels/src/backend/routes.ts delete mode 100644 plugins/tunnels/src/backend/service.ts delete mode 100644 plugins/tunnels/src/backend/socks5-relay.ts delete mode 100644 plugins/tunnels/src/backend/ssh-primitives.ts delete mode 100644 plugins/tunnels/src/backend/tables.ts delete mode 100644 plugins/tunnels/src/backend/types.ts delete mode 100644 plugins/tunnels/src/backend/utils.ts delete mode 100644 plugins/tunnels/src/frontend/ActiveTunnelsCounter.tsx delete mode 100644 plugins/tunnels/src/frontend/C2STunnelPresetManager.tsx delete mode 100644 plugins/tunnels/src/frontend/HostTunnelsSection.tsx delete mode 100644 plugins/tunnels/src/frontend/TunnelAuthPrompts.tsx delete mode 100644 plugins/tunnels/src/frontend/TunnelTab.tsx delete mode 100644 plugins/tunnels/src/frontend/api.ts delete mode 100644 plugins/tunnels/src/frontend/c2s-tunnel-config-utils.ts delete mode 100644 plugins/tunnels/src/frontend/host-tunnels.ts delete mode 100644 plugins/tunnels/src/frontend/index.tsx delete mode 100644 plugins/tunnels/src/frontend/tunnel-form-utils.ts delete mode 100644 plugins/tunnels/src/frontend/views.tsx delete mode 100644 plugins/tunnels/src/shared/tunnel-naming.ts delete mode 100644 plugins/tunnels/src/shared/types.ts delete mode 100644 plugins/tunnels/tests/backend/activate.test.ts delete mode 100644 plugins/tunnels/tests/backend/authorize.test.ts delete mode 100644 plugins/tunnels/tests/backend/autostart.test.ts delete mode 100644 plugins/tunnels/tests/backend/c2s-auth.test.ts delete mode 100644 plugins/tunnels/tests/backend/c2s-relay-auth.test.ts delete mode 100644 plugins/tunnels/tests/backend/c2s-source-host.test.ts delete mode 100644 plugins/tunnels/tests/backend/helpers.ts delete mode 100644 plugins/tunnels/tests/backend/manager.test.ts delete mode 100644 plugins/tunnels/tests/backend/migrations.test.ts delete mode 100644 plugins/tunnels/tests/backend/reserved-names.test.ts delete mode 100644 plugins/tunnels/tests/backend/routes.test.ts delete mode 100644 plugins/tunnels/tests/backend/service.test.ts delete mode 100644 plugins/tunnels/tests/frontend/activate.test.tsx delete mode 100644 plugins/tunnels/tests/frontend/api.test.ts delete mode 100644 plugins/tunnels/tests/frontend/auth-prompts.test.tsx delete mode 100644 plugins/tunnels/tests/frontend/c2s-tunnel-config-utils.test.ts delete mode 100644 plugins/tunnels/tests/frontend/tunnel-form-utils.test.ts delete mode 100644 plugins/tunnels/tests/frontend/tunnel-naming.test.ts delete mode 100644 plugins/tunnels/tsconfig.json delete mode 100644 plugins/tunnels/vitest.config.ts delete mode 100644 plugins/vault/.commitlintrc.json delete mode 100644 plugins/vault/.github/funding.yml delete mode 100644 plugins/vault/.github/pull_request_template.md delete mode 100644 plugins/vault/.gitignore delete mode 100644 plugins/vault/.husky/commit-msg delete mode 100644 plugins/vault/.husky/pre-commit delete mode 100644 plugins/vault/.prettierignore delete mode 100644 plugins/vault/.prettierrc delete mode 100644 plugins/vault/CHANGELOG.json delete mode 100644 plugins/vault/CONTRIBUTING.md delete mode 100644 plugins/vault/README.md delete mode 100644 plugins/vault/locales/en.json delete mode 100644 plugins/vault/locales/translated/af_ZA.json delete mode 100644 plugins/vault/locales/translated/ar_SA.json delete mode 100644 plugins/vault/locales/translated/bg_BG.json delete mode 100644 plugins/vault/locales/translated/bn_BD.json delete mode 100644 plugins/vault/locales/translated/ca_ES.json delete mode 100644 plugins/vault/locales/translated/cs_CZ.json delete mode 100644 plugins/vault/locales/translated/da_DK.json delete mode 100644 plugins/vault/locales/translated/de_DE.json delete mode 100644 plugins/vault/locales/translated/el_GR.json delete mode 100644 plugins/vault/locales/translated/es_ES.json delete mode 100644 plugins/vault/locales/translated/fi_FI.json delete mode 100644 plugins/vault/locales/translated/fr_FR.json delete mode 100644 plugins/vault/locales/translated/he_IL.json delete mode 100644 plugins/vault/locales/translated/hi_IN.json delete mode 100644 plugins/vault/locales/translated/hu_HU.json delete mode 100644 plugins/vault/locales/translated/id_ID.json delete mode 100644 plugins/vault/locales/translated/it_IT.json delete mode 100644 plugins/vault/locales/translated/ja_JP.json delete mode 100644 plugins/vault/locales/translated/ko_KR.json delete mode 100644 plugins/vault/locales/translated/nl_NL.json delete mode 100644 plugins/vault/locales/translated/no_NO.json delete mode 100644 plugins/vault/locales/translated/pl_PL.json delete mode 100644 plugins/vault/locales/translated/pt_BR.json delete mode 100644 plugins/vault/locales/translated/pt_PT.json delete mode 100644 plugins/vault/locales/translated/ro_RO.json delete mode 100644 plugins/vault/locales/translated/ru_RU.json delete mode 100644 plugins/vault/locales/translated/sr_SP.json delete mode 100644 plugins/vault/locales/translated/sv_SE.json delete mode 100644 plugins/vault/locales/translated/th_TH.json delete mode 100644 plugins/vault/locales/translated/tr_TR.json delete mode 100644 plugins/vault/locales/translated/uk_UA.json delete mode 100644 plugins/vault/locales/translated/vi_VN.json delete mode 100644 plugins/vault/locales/translated/zh_CN.json delete mode 100644 plugins/vault/locales/translated/zh_TW.json delete mode 100644 plugins/vault/manifest.json delete mode 100644 plugins/vault/migrations/mysql/0001_adopt_vault_tables.sql delete mode 100644 plugins/vault/migrations/postgres/0001_adopt_vault_tables.sql delete mode 100644 plugins/vault/migrations/snapshot.json delete mode 100644 plugins/vault/migrations/sqlite/0001_adopt_vault_tables.sql delete mode 100644 plugins/vault/package.json delete mode 100644 plugins/vault/src/backend/auth-session.ts delete mode 100644 plugins/vault/src/backend/index.ts delete mode 100644 plugins/vault/src/backend/profile-store.ts delete mode 100644 plugins/vault/src/backend/provider.ts delete mode 100644 plugins/vault/src/backend/routes.ts delete mode 100644 plugins/vault/src/backend/tables.ts delete mode 100644 plugins/vault/src/backend/token-store.ts delete mode 100644 plugins/vault/src/backend/vault-client.ts delete mode 100644 plugins/vault/src/frontend/RedirectUriSetting.tsx delete mode 100644 plugins/vault/src/frontend/VaultAuthEditor.tsx delete mode 100644 plugins/vault/src/frontend/VaultOverlay.tsx delete mode 100644 plugins/vault/src/frontend/VaultProfileManager.tsx delete mode 100644 plugins/vault/src/frontend/index.tsx delete mode 100644 plugins/vault/src/frontend/types.ts delete mode 100644 plugins/vault/tests/backend/helpers.ts delete mode 100644 plugins/vault/tests/backend/legacy-urls.test.ts delete mode 100644 plugins/vault/tests/backend/migrations.test.ts delete mode 100644 plugins/vault/tests/backend/vault-client.test.ts delete mode 100644 plugins/vault/tests/backend/vault.test.ts delete mode 100644 plugins/vault/tests/frontend/activate.test.tsx delete mode 100644 plugins/vault/tsconfig.json delete mode 100644 plugins/vault/vitest.config.ts delete mode 100644 plugins/wake-on-lan/.commitlintrc.json delete mode 100644 plugins/wake-on-lan/.github/funding.yml delete mode 100644 plugins/wake-on-lan/.github/pull_request_template.md delete mode 100644 plugins/wake-on-lan/.gitignore delete mode 100644 plugins/wake-on-lan/.husky/commit-msg delete mode 100644 plugins/wake-on-lan/.husky/pre-commit delete mode 100644 plugins/wake-on-lan/.prettierignore delete mode 100644 plugins/wake-on-lan/.prettierrc delete mode 100644 plugins/wake-on-lan/CHANGELOG.json delete mode 100644 plugins/wake-on-lan/CONTRIBUTING.md delete mode 100644 plugins/wake-on-lan/README.md delete mode 100644 plugins/wake-on-lan/locales/en.json delete mode 100644 plugins/wake-on-lan/locales/translated/af_ZA.json delete mode 100644 plugins/wake-on-lan/locales/translated/ar_SA.json delete mode 100644 plugins/wake-on-lan/locales/translated/bg_BG.json delete mode 100644 plugins/wake-on-lan/locales/translated/bn_BD.json delete mode 100644 plugins/wake-on-lan/locales/translated/ca_ES.json delete mode 100644 plugins/wake-on-lan/locales/translated/cs_CZ.json delete mode 100644 plugins/wake-on-lan/locales/translated/da_DK.json delete mode 100644 plugins/wake-on-lan/locales/translated/de_DE.json delete mode 100644 plugins/wake-on-lan/locales/translated/el_GR.json delete mode 100644 plugins/wake-on-lan/locales/translated/es_ES.json delete mode 100644 plugins/wake-on-lan/locales/translated/fi_FI.json delete mode 100644 plugins/wake-on-lan/locales/translated/fr_FR.json delete mode 100644 plugins/wake-on-lan/locales/translated/he_IL.json delete mode 100644 plugins/wake-on-lan/locales/translated/hi_IN.json delete mode 100644 plugins/wake-on-lan/locales/translated/hu_HU.json delete mode 100644 plugins/wake-on-lan/locales/translated/id_ID.json delete mode 100644 plugins/wake-on-lan/locales/translated/it_IT.json delete mode 100644 plugins/wake-on-lan/locales/translated/ja_JP.json delete mode 100644 plugins/wake-on-lan/locales/translated/ko_KR.json delete mode 100644 plugins/wake-on-lan/locales/translated/nl_NL.json delete mode 100644 plugins/wake-on-lan/locales/translated/no_NO.json delete mode 100644 plugins/wake-on-lan/locales/translated/pl_PL.json delete mode 100644 plugins/wake-on-lan/locales/translated/pt_BR.json delete mode 100644 plugins/wake-on-lan/locales/translated/pt_PT.json delete mode 100644 plugins/wake-on-lan/locales/translated/ro_RO.json delete mode 100644 plugins/wake-on-lan/locales/translated/ru_RU.json delete mode 100644 plugins/wake-on-lan/locales/translated/sr_SP.json delete mode 100644 plugins/wake-on-lan/locales/translated/sv_SE.json delete mode 100644 plugins/wake-on-lan/locales/translated/th_TH.json delete mode 100644 plugins/wake-on-lan/locales/translated/tr_TR.json delete mode 100644 plugins/wake-on-lan/locales/translated/uk_UA.json delete mode 100644 plugins/wake-on-lan/locales/translated/vi_VN.json delete mode 100644 plugins/wake-on-lan/locales/translated/zh_CN.json delete mode 100644 plugins/wake-on-lan/locales/translated/zh_TW.json delete mode 100644 plugins/wake-on-lan/manifest.json delete mode 100644 plugins/wake-on-lan/package.json delete mode 100644 plugins/wake-on-lan/src/backend/index.ts delete mode 100644 plugins/wake-on-lan/src/backend/magic-packet.ts delete mode 100644 plugins/wake-on-lan/src/backend/routes.ts delete mode 100644 plugins/wake-on-lan/src/backend/service.ts delete mode 100644 plugins/wake-on-lan/src/frontend/host-action.ts delete mode 100644 plugins/wake-on-lan/src/frontend/index.tsx delete mode 100644 plugins/wake-on-lan/tests/backend/helpers.ts delete mode 100644 plugins/wake-on-lan/tests/backend/magic-packet.test.ts delete mode 100644 plugins/wake-on-lan/tests/backend/routes.test.ts delete mode 100644 plugins/wake-on-lan/tests/backend/service.test.ts delete mode 100644 plugins/wake-on-lan/tests/frontend/activate.test.tsx delete mode 100644 plugins/wake-on-lan/tests/frontend/host-action.test.ts delete mode 100644 plugins/wake-on-lan/tsconfig.json delete mode 100644 plugins/wake-on-lan/vitest.config.ts delete mode 100644 plugins/warpgate/.commitlintrc.json delete mode 100644 plugins/warpgate/.github/funding.yml delete mode 100644 plugins/warpgate/.github/pull_request_template.md delete mode 100644 plugins/warpgate/.gitignore delete mode 100644 plugins/warpgate/.husky/commit-msg delete mode 100644 plugins/warpgate/.husky/pre-commit delete mode 100644 plugins/warpgate/.prettierignore delete mode 100644 plugins/warpgate/.prettierrc delete mode 100644 plugins/warpgate/CHANGELOG.json delete mode 100644 plugins/warpgate/CONTRIBUTING.md delete mode 100644 plugins/warpgate/README.md delete mode 100644 plugins/warpgate/locales/en.json delete mode 100644 plugins/warpgate/locales/translated/af_ZA.json delete mode 100644 plugins/warpgate/locales/translated/ar_SA.json delete mode 100644 plugins/warpgate/locales/translated/bg_BG.json delete mode 100644 plugins/warpgate/locales/translated/bn_BD.json delete mode 100644 plugins/warpgate/locales/translated/ca_ES.json delete mode 100644 plugins/warpgate/locales/translated/cs_CZ.json delete mode 100644 plugins/warpgate/locales/translated/da_DK.json delete mode 100644 plugins/warpgate/locales/translated/de_DE.json delete mode 100644 plugins/warpgate/locales/translated/el_GR.json delete mode 100644 plugins/warpgate/locales/translated/es_ES.json delete mode 100644 plugins/warpgate/locales/translated/fi_FI.json delete mode 100644 plugins/warpgate/locales/translated/fr_FR.json delete mode 100644 plugins/warpgate/locales/translated/he_IL.json delete mode 100644 plugins/warpgate/locales/translated/hi_IN.json delete mode 100644 plugins/warpgate/locales/translated/hu_HU.json delete mode 100644 plugins/warpgate/locales/translated/id_ID.json delete mode 100644 plugins/warpgate/locales/translated/it_IT.json delete mode 100644 plugins/warpgate/locales/translated/ja_JP.json delete mode 100644 plugins/warpgate/locales/translated/ko_KR.json delete mode 100644 plugins/warpgate/locales/translated/nl_NL.json delete mode 100644 plugins/warpgate/locales/translated/no_NO.json delete mode 100644 plugins/warpgate/locales/translated/pl_PL.json delete mode 100644 plugins/warpgate/locales/translated/pt_BR.json delete mode 100644 plugins/warpgate/locales/translated/pt_PT.json delete mode 100644 plugins/warpgate/locales/translated/ro_RO.json delete mode 100644 plugins/warpgate/locales/translated/ru_RU.json delete mode 100644 plugins/warpgate/locales/translated/sr_SP.json delete mode 100644 plugins/warpgate/locales/translated/sv_SE.json delete mode 100644 plugins/warpgate/locales/translated/th_TH.json delete mode 100644 plugins/warpgate/locales/translated/tr_TR.json delete mode 100644 plugins/warpgate/locales/translated/uk_UA.json delete mode 100644 plugins/warpgate/locales/translated/vi_VN.json delete mode 100644 plugins/warpgate/locales/translated/zh_CN.json delete mode 100644 plugins/warpgate/locales/translated/zh_TW.json delete mode 100644 plugins/warpgate/manifest.json delete mode 100644 plugins/warpgate/package.json delete mode 100644 plugins/warpgate/src/backend/detect.ts delete mode 100644 plugins/warpgate/src/backend/host-import.ts delete mode 100644 plugins/warpgate/src/backend/index.ts delete mode 100644 plugins/warpgate/src/frontend/index.tsx delete mode 100644 plugins/warpgate/tests/backend/warpgate.test.ts delete mode 100644 plugins/warpgate/tsconfig.json delete mode 100644 plugins/warpgate/vitest.config.ts delete mode 100644 plugins/web-endpoint/.commitlintrc.json delete mode 100644 plugins/web-endpoint/.github/funding.yml delete mode 100644 plugins/web-endpoint/.github/pull_request_template.md delete mode 100644 plugins/web-endpoint/.gitignore delete mode 100644 plugins/web-endpoint/.husky/commit-msg delete mode 100644 plugins/web-endpoint/.husky/pre-commit delete mode 100644 plugins/web-endpoint/.prettierignore delete mode 100644 plugins/web-endpoint/.prettierrc delete mode 100644 plugins/web-endpoint/CHANGELOG.json delete mode 100644 plugins/web-endpoint/CONTRIBUTING.md delete mode 100644 plugins/web-endpoint/README.md delete mode 100644 plugins/web-endpoint/locales/en.json delete mode 100644 plugins/web-endpoint/locales/translated/af_ZA.json delete mode 100644 plugins/web-endpoint/locales/translated/ar_SA.json delete mode 100644 plugins/web-endpoint/locales/translated/bg_BG.json delete mode 100644 plugins/web-endpoint/locales/translated/bn_BD.json delete mode 100644 plugins/web-endpoint/locales/translated/ca_ES.json delete mode 100644 plugins/web-endpoint/locales/translated/cs_CZ.json delete mode 100644 plugins/web-endpoint/locales/translated/da_DK.json delete mode 100644 plugins/web-endpoint/locales/translated/de_DE.json delete mode 100644 plugins/web-endpoint/locales/translated/el_GR.json delete mode 100644 plugins/web-endpoint/locales/translated/es_ES.json delete mode 100644 plugins/web-endpoint/locales/translated/fi_FI.json delete mode 100644 plugins/web-endpoint/locales/translated/fr_FR.json delete mode 100644 plugins/web-endpoint/locales/translated/he_IL.json delete mode 100644 plugins/web-endpoint/locales/translated/hi_IN.json delete mode 100644 plugins/web-endpoint/locales/translated/hu_HU.json delete mode 100644 plugins/web-endpoint/locales/translated/id_ID.json delete mode 100644 plugins/web-endpoint/locales/translated/it_IT.json delete mode 100644 plugins/web-endpoint/locales/translated/ja_JP.json delete mode 100644 plugins/web-endpoint/locales/translated/ko_KR.json delete mode 100644 plugins/web-endpoint/locales/translated/nl_NL.json delete mode 100644 plugins/web-endpoint/locales/translated/no_NO.json delete mode 100644 plugins/web-endpoint/locales/translated/pl_PL.json delete mode 100644 plugins/web-endpoint/locales/translated/pt_BR.json delete mode 100644 plugins/web-endpoint/locales/translated/pt_PT.json delete mode 100644 plugins/web-endpoint/locales/translated/ro_RO.json delete mode 100644 plugins/web-endpoint/locales/translated/ru_RU.json delete mode 100644 plugins/web-endpoint/locales/translated/sr_SP.json delete mode 100644 plugins/web-endpoint/locales/translated/sv_SE.json delete mode 100644 plugins/web-endpoint/locales/translated/th_TH.json delete mode 100644 plugins/web-endpoint/locales/translated/tr_TR.json delete mode 100644 plugins/web-endpoint/locales/translated/uk_UA.json delete mode 100644 plugins/web-endpoint/locales/translated/vi_VN.json delete mode 100644 plugins/web-endpoint/locales/translated/zh_CN.json delete mode 100644 plugins/web-endpoint/locales/translated/zh_TW.json delete mode 100644 plugins/web-endpoint/manifest.json delete mode 100644 plugins/web-endpoint/package.json delete mode 100644 plugins/web-endpoint/src/backend/host-import.ts delete mode 100644 plugins/web-endpoint/src/backend/index.ts delete mode 100644 plugins/web-endpoint/src/backend/routes.ts delete mode 100644 plugins/web-endpoint/src/frontend/HostEditorWebUiSection.tsx delete mode 100644 plugins/web-endpoint/src/frontend/WebEndpointTab.tsx delete mode 100644 plugins/web-endpoint/src/frontend/index.tsx delete mode 100644 plugins/web-endpoint/src/frontend/web-endpoint-api.ts delete mode 100644 plugins/web-endpoint/src/frontend/web-endpoint-url.ts delete mode 100644 plugins/web-endpoint/src/frontend/web-endpoint-validation.ts delete mode 100644 plugins/web-endpoint/src/shared/web-endpoint-config.ts delete mode 100644 plugins/web-endpoint/tests/backend/activate.test.ts delete mode 100644 plugins/web-endpoint/tests/backend/host-import.test.ts delete mode 100644 plugins/web-endpoint/tests/backend/routes.test.ts delete mode 100644 plugins/web-endpoint/tests/backend/web-endpoint-config.test.ts delete mode 100644 plugins/web-endpoint/tests/frontend/WebEndpointTab.test.tsx delete mode 100644 plugins/web-endpoint/tests/frontend/activate.test.tsx delete mode 100644 plugins/web-endpoint/tests/frontend/web-endpoint-api.test.ts delete mode 100644 plugins/web-endpoint/tests/frontend/web-endpoint-url.test.ts delete mode 100644 plugins/web-endpoint/tests/frontend/web-endpoint-validation.test.ts delete mode 100644 plugins/web-endpoint/tsconfig.json delete mode 100644 plugins/web-endpoint/vitest.config.ts delete mode 100644 plugins/webauthn/.commitlintrc.json delete mode 100644 plugins/webauthn/.github/funding.yml delete mode 100644 plugins/webauthn/.github/pull_request_template.md delete mode 100644 plugins/webauthn/.gitignore delete mode 100644 plugins/webauthn/.husky/commit-msg delete mode 100644 plugins/webauthn/.husky/pre-commit delete mode 100644 plugins/webauthn/.prettierignore delete mode 100644 plugins/webauthn/.prettierrc delete mode 100644 plugins/webauthn/CHANGELOG.json delete mode 100644 plugins/webauthn/CONTRIBUTING.md delete mode 100644 plugins/webauthn/README.md delete mode 100644 plugins/webauthn/locales/en.json delete mode 100644 plugins/webauthn/locales/translated/af_ZA.json delete mode 100644 plugins/webauthn/locales/translated/ar_SA.json delete mode 100644 plugins/webauthn/locales/translated/bg_BG.json delete mode 100644 plugins/webauthn/locales/translated/bn_BD.json delete mode 100644 plugins/webauthn/locales/translated/ca_ES.json delete mode 100644 plugins/webauthn/locales/translated/cs_CZ.json delete mode 100644 plugins/webauthn/locales/translated/da_DK.json delete mode 100644 plugins/webauthn/locales/translated/de_DE.json delete mode 100644 plugins/webauthn/locales/translated/el_GR.json delete mode 100644 plugins/webauthn/locales/translated/es_ES.json delete mode 100644 plugins/webauthn/locales/translated/fi_FI.json delete mode 100644 plugins/webauthn/locales/translated/fr_FR.json delete mode 100644 plugins/webauthn/locales/translated/he_IL.json delete mode 100644 plugins/webauthn/locales/translated/hi_IN.json delete mode 100644 plugins/webauthn/locales/translated/hu_HU.json delete mode 100644 plugins/webauthn/locales/translated/id_ID.json delete mode 100644 plugins/webauthn/locales/translated/it_IT.json delete mode 100644 plugins/webauthn/locales/translated/ja_JP.json delete mode 100644 plugins/webauthn/locales/translated/ko_KR.json delete mode 100644 plugins/webauthn/locales/translated/nl_NL.json delete mode 100644 plugins/webauthn/locales/translated/no_NO.json delete mode 100644 plugins/webauthn/locales/translated/pl_PL.json delete mode 100644 plugins/webauthn/locales/translated/pt_BR.json delete mode 100644 plugins/webauthn/locales/translated/pt_PT.json delete mode 100644 plugins/webauthn/locales/translated/ro_RO.json delete mode 100644 plugins/webauthn/locales/translated/ru_RU.json delete mode 100644 plugins/webauthn/locales/translated/sr_SP.json delete mode 100644 plugins/webauthn/locales/translated/sv_SE.json delete mode 100644 plugins/webauthn/locales/translated/th_TH.json delete mode 100644 plugins/webauthn/locales/translated/tr_TR.json delete mode 100644 plugins/webauthn/locales/translated/uk_UA.json delete mode 100644 plugins/webauthn/locales/translated/vi_VN.json delete mode 100644 plugins/webauthn/locales/translated/zh_CN.json delete mode 100644 plugins/webauthn/locales/translated/zh_TW.json delete mode 100644 plugins/webauthn/manifest.json delete mode 100644 plugins/webauthn/migrations/mysql/0001_adopt_webauthn_credentials.sql delete mode 100644 plugins/webauthn/migrations/postgres/0001_adopt_webauthn_credentials.sql delete mode 100644 plugins/webauthn/migrations/snapshot.json delete mode 100644 plugins/webauthn/migrations/sqlite/0001_adopt_webauthn_credentials.sql delete mode 100644 plugins/webauthn/package.json delete mode 100644 plugins/webauthn/src/backend/index.ts delete mode 100644 plugins/webauthn/src/backend/passkeys.ts delete mode 100644 plugins/webauthn/src/backend/repository.ts delete mode 100644 plugins/webauthn/src/backend/routes.ts delete mode 100644 plugins/webauthn/src/backend/tables.ts delete mode 100644 plugins/webauthn/src/frontend/PasskeyEnrollment.tsx delete mode 100644 plugins/webauthn/src/frontend/PasskeyLoginButton.tsx delete mode 100644 plugins/webauthn/src/frontend/index.tsx delete mode 100644 plugins/webauthn/src/frontend/webauthn-api.ts delete mode 100644 plugins/webauthn/tests/backend/helpers.ts delete mode 100644 plugins/webauthn/tests/backend/passkeys.test.ts delete mode 100644 plugins/webauthn/tests/frontend/activate.test.tsx delete mode 100644 plugins/webauthn/tsconfig.json delete mode 100644 plugins/webauthn/vitest.config.ts delete mode 100644 plugins/workspaces/.commitlintrc.json delete mode 100644 plugins/workspaces/.github/funding.yml delete mode 100644 plugins/workspaces/.github/pull_request_template.md delete mode 100644 plugins/workspaces/.gitignore delete mode 100644 plugins/workspaces/.husky/commit-msg delete mode 100644 plugins/workspaces/.husky/pre-commit delete mode 100644 plugins/workspaces/.prettierignore delete mode 100644 plugins/workspaces/.prettierrc delete mode 100644 plugins/workspaces/CHANGELOG.json delete mode 100644 plugins/workspaces/CONTRIBUTING.md delete mode 100644 plugins/workspaces/README.md delete mode 100644 plugins/workspaces/locales/en.json delete mode 100644 plugins/workspaces/locales/translated/af_ZA.json delete mode 100644 plugins/workspaces/locales/translated/ar_SA.json delete mode 100644 plugins/workspaces/locales/translated/bg_BG.json delete mode 100644 plugins/workspaces/locales/translated/bn_BD.json delete mode 100644 plugins/workspaces/locales/translated/ca_ES.json delete mode 100644 plugins/workspaces/locales/translated/cs_CZ.json delete mode 100644 plugins/workspaces/locales/translated/da_DK.json delete mode 100644 plugins/workspaces/locales/translated/de_DE.json delete mode 100644 plugins/workspaces/locales/translated/el_GR.json delete mode 100644 plugins/workspaces/locales/translated/es_ES.json delete mode 100644 plugins/workspaces/locales/translated/fi_FI.json delete mode 100644 plugins/workspaces/locales/translated/fr_FR.json delete mode 100644 plugins/workspaces/locales/translated/he_IL.json delete mode 100644 plugins/workspaces/locales/translated/hi_IN.json delete mode 100644 plugins/workspaces/locales/translated/hu_HU.json delete mode 100644 plugins/workspaces/locales/translated/id_ID.json delete mode 100644 plugins/workspaces/locales/translated/it_IT.json delete mode 100644 plugins/workspaces/locales/translated/ja_JP.json delete mode 100644 plugins/workspaces/locales/translated/ko_KR.json delete mode 100644 plugins/workspaces/locales/translated/nl_NL.json delete mode 100644 plugins/workspaces/locales/translated/no_NO.json delete mode 100644 plugins/workspaces/locales/translated/pl_PL.json delete mode 100644 plugins/workspaces/locales/translated/pt_BR.json delete mode 100644 plugins/workspaces/locales/translated/pt_PT.json delete mode 100644 plugins/workspaces/locales/translated/ro_RO.json delete mode 100644 plugins/workspaces/locales/translated/ru_RU.json delete mode 100644 plugins/workspaces/locales/translated/sr_SP.json delete mode 100644 plugins/workspaces/locales/translated/sv_SE.json delete mode 100644 plugins/workspaces/locales/translated/th_TH.json delete mode 100644 plugins/workspaces/locales/translated/tr_TR.json delete mode 100644 plugins/workspaces/locales/translated/uk_UA.json delete mode 100644 plugins/workspaces/locales/translated/vi_VN.json delete mode 100644 plugins/workspaces/locales/translated/zh_CN.json delete mode 100644 plugins/workspaces/locales/translated/zh_TW.json delete mode 100644 plugins/workspaces/manifest.json delete mode 100644 plugins/workspaces/migrations/mysql/0001_adopt_user_workspaces.sql delete mode 100644 plugins/workspaces/migrations/postgres/0001_adopt_user_workspaces.sql delete mode 100644 plugins/workspaces/migrations/snapshot.json delete mode 100644 plugins/workspaces/migrations/sqlite/0001_adopt_user_workspaces.sql delete mode 100644 plugins/workspaces/package.json delete mode 100644 plugins/workspaces/src/backend/index.ts delete mode 100644 plugins/workspaces/src/backend/repository.ts delete mode 100644 plugins/workspaces/src/backend/routes.ts delete mode 100644 plugins/workspaces/src/backend/tables.ts delete mode 100644 plugins/workspaces/src/frontend/WorkspacesPanel.tsx delete mode 100644 plugins/workspaces/src/frontend/index.tsx delete mode 100644 plugins/workspaces/src/frontend/types.ts delete mode 100644 plugins/workspaces/src/frontend/workspaces-api.ts delete mode 100644 plugins/workspaces/tests/backend/activate.test.ts delete mode 100644 plugins/workspaces/tests/backend/helpers.ts delete mode 100644 plugins/workspaces/tests/backend/migrations.test.ts delete mode 100644 plugins/workspaces/tests/backend/repository.test.ts delete mode 100644 plugins/workspaces/tests/backend/routes.test.ts delete mode 100644 plugins/workspaces/tests/frontend/activate.test.tsx delete mode 100644 plugins/workspaces/tests/frontend/workspaces-api.test.ts delete mode 100644 plugins/workspaces/tsconfig.json delete mode 100644 plugins/workspaces/vitest.config.ts delete mode 100644 scripts/apply-plugin-patches.cjs delete mode 100644 scripts/build-upgrade-fixture.ts delete mode 100644 scripts/check-plugin-locales.cjs delete mode 100644 scripts/check-plugin-locales.test.ts delete mode 100644 scripts/check-plugin-uses.cjs delete mode 100644 scripts/check-plugin-uses.test.ts delete mode 100644 scripts/install-native-plugin-deps.cjs delete mode 100644 scripts/lib/upgrade-sql.mjs delete mode 100644 scripts/patch-guacamole-lite.test.ts delete mode 100644 scripts/run-plugin-tests.cjs delete mode 100644 scripts/upgrade-check.compose.yml delete mode 100755 scripts/upgrade-check.sh delete mode 100644 scripts/upgrade-sql.test.ts delete mode 100644 scripts/validate-plugin-manifest.cjs delete mode 100644 scripts/validate-plugin-manifest.test.ts delete mode 100644 src/backend/tests/fixtures/upgrade/db.sqlite delete mode 100644 src/backend/tests/fixtures/upgrade/files/session_logs/d4-user-admin/d4-session.cast delete mode 100644 src/backend/tests/fixtures/upgrade/files/session_recordings/guacamole/d4-rdp.guac delete mode 100644 src/backend/tests/fixtures/upgrade/fixture.env delete mode 100644 src/backend/tests/fixtures/upgrade/rows.ts delete mode 100644 src/backend/tests/fixtures/upgrade/upgrade-mysql.sql delete mode 100644 src/backend/tests/fixtures/upgrade/upgrade-postgres.sql delete mode 100644 src/backend/tests/plugins/built-harness.ts delete mode 100644 src/backend/tests/plugins/built-plugins-boot.test.ts delete mode 100644 src/backend/tests/plugins/bundled-plugin.test.ts delete mode 100644 src/backend/tests/plugins/remote-seed.ts delete mode 100644 src/backend/tests/plugins/upgrade-fixture-data.test.ts delete mode 100644 src/backend/tests/plugins/upgrade-fixture.test.ts delete mode 100644 src/ui/plugin-host/workspace-plugins.ts delete mode 100644 src/ui/tests/plugin-host/built-bundles.test.tsx delete mode 100644 tsconfig.plugins.json diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index 5125ddaba..bac25a12c 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -84,16 +84,12 @@ jobs: - name: Type check run: npm run type-check - # Before the tests: the plugin boot tests load the built bundles. - name: Build run: npm run build - name: Run unit tests run: npm run test - - name: Run plugin tests - run: npm run test:plugins - create-release: needs: [prep, verify] if: ${{ needs.prep.outputs.dev_branch != '' && inputs.dry_run != true }} diff --git a/.github/workflows/crowdin-sync.yml b/.github/workflows/crowdin-sync.yml index 71a141524..0f32ccf74 100644 --- a/.github/workflows/crowdin-sync.yml +++ b/.github/workflows/crowdin-sync.yml @@ -74,7 +74,7 @@ jobs: git config user.name "LukeGus" git config user.email "bugattiguy527@gmail.com" - git add src/ui/locales/translated plugins/*/locales/translated + git add src/ui/locales/translated if git diff --cached --quiet; then echo "No translation changes to commit." exit 0 diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index cee0b3135..866188d5e 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -44,9 +44,6 @@ jobs: - name: Run unit tests run: npm run test - - name: Run plugin tests - run: npm run test:plugins - database-dialects: name: Postgres and MySQL runs-on: blacksmith-2vcpu-ubuntu-2404 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07b60cf2f..460c35f95 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -110,16 +110,9 @@ jobs: npm run format npm run lint - # The plugin boot tests load the built bundles. - - name: Build plugins - run: npm run build:plugins - - name: Run unit tests run: npm run test - - name: Run plugin tests - run: npm run test:plugins - - name: Sync version run: node scripts/sync-version.cjs --version "${{ needs.prep.outputs.version }}" @@ -217,7 +210,7 @@ jobs: git config user.name "LukeGus" git config user.email "bugattiguy527@gmail.com" - git add src/ui/locales/translated plugins/*/locales/translated + git add src/ui/locales/translated if git diff --cached --quiet; then echo "No translation changes to commit." exit 0 diff --git a/.gitignore b/.gitignore index 458fe27ac..22edf97e3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,6 @@ coverage # Plugin build output. termix-plugin build writes it, the packaged server # gets its copy under dist/plugins. -/plugins/*/dist/ *.tmxplug *.tmxplug.sig termix-plugin-signing.key diff --git a/crowdin.yml b/crowdin.yml index 8a88aa9aa..a50e729d5 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -7,5 +7,3 @@ files: - source: /src/ui/locales/en.json dest: /en.json translation: /src/ui/locales/translated/%locale_with_underscore%.json - - source: /plugins/*/locales/en.json - translation: /%original_path%/translated/%locale_with_underscore%.json diff --git a/docker/Dockerfile b/docker/Dockerfile index 473ffbd47..90433e060 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,12 +1,3 @@ -# Stage 0: every plugin's package.json and manifest.json and nothing else. -# npm ci needs each workspace package.json in place or it silently skips it, -# and a glob COPY flattens paths, so the tree is trimmed here instead. The -# deps layer below stays cached until one of these files changes. -FROM node:26-slim AS workspace-manifests -WORKDIR /src -COPY plugins ./plugins -RUN find plugins -mindepth 2 -maxdepth 2 ! -name package.json ! -name manifest.json -exec rm -rf {} + - # Stage 1: Install dependencies # The builders only produce JS, so they run on the build machine's own arch # once instead of under QEMU per target. Rolldown hangs under arm64 emulation. @@ -17,7 +8,6 @@ RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt COPY package*.json ./ COPY packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json -COPY --from=workspace-manifests /src/plugins ./plugins COPY .npmrc ./ COPY vendor ./vendor @@ -80,15 +70,9 @@ COPY packages/plugin-sdk/package.json ./packages/plugin-sdk/package.json COPY .npmrc ./ COPY vendor ./vendor -COPY scripts/patch-ssh2-agent.cjs scripts/install-native-plugin-deps.cjs ./scripts/ -# Plugin manifests only, for their nativeDependencies. Outside plugins/ so npm -# ci does not treat them as workspaces: everything else a plugin uses is -# already inside its bundle. -COPY --from=workspace-manifests /src/plugins ./plugin-manifests +COPY scripts/patch-ssh2-agent.cjs ./scripts/ RUN npm ci --omit=dev --ignore-scripts && \ - node scripts/install-native-plugin-deps.cjs plugin-manifests && \ - rm -rf plugin-manifests && \ node scripts/patch-ssh2-agent.cjs && \ rm -rf node_modules/better-sqlite3/prebuilds && \ npm run build-release --prefix node_modules/better-sqlite3 && \ @@ -128,7 +112,7 @@ COPY --chown=node:node --from=frontend-builder /app/dist /app/html COPY --chown=node:node --from=production-deps /app/node_modules /app/node_modules COPY --chown=node:node --from=backend-builder /app/dist/backend ./dist/backend -# Bundled first-party plugins, resolved as dist/plugins by +# Plugins pinned in docker/bundled-plugins.json, resolved as dist/plugins by # getBundledPluginsDir(). COPY --chown=node:node --from=backend-builder /app/dist/plugins ./dist/plugins # Target of node_modules/@termix/plugin-sdk, which core and the plugins import. diff --git a/docker/bundled-plugins.json b/docker/bundled-plugins.json index 2728154af..a5b818f53 100644 --- a/docker/bundled-plugins.json +++ b/docker/bundled-plugins.json @@ -1,140 +1,3 @@ { - "plugins": [ - { - "id": "acme-ssl", - "source": "workspace" - }, - { - "id": "ai", - "source": "workspace" - }, - { - "id": "alerts", - "source": "workspace" - }, - { - "id": "automations", - "source": "workspace" - }, - { - "id": "docker", - "source": "workspace" - }, - { - "id": "file-manager", - "source": "workspace" - }, - { - "id": "fleets", - "source": "workspace" - }, - { - "id": "homepage", - "source": "workspace" - }, - { - "id": "host-metrics", - "source": "workspace" - }, - { - "id": "ldap", - "source": "workspace" - }, - { - "id": "network-topology", - "source": "workspace" - }, - { - "id": "opkssh", - "source": "workspace" - }, - { - "id": "proxmox", - "source": "workspace" - }, - { - "id": "remote-desktop", - "source": "workspace" - }, - { - "id": "secret-sources", - "source": "workspace" - }, - { - "id": "serial", - "source": "workspace" - }, - { - "id": "session-recording", - "source": "workspace" - }, - { - "id": "session-sharing", - "source": "workspace" - }, - { - "id": "snippets", - "source": "workspace" - }, - { - "id": "ssh-terminal", - "source": "workspace" - }, - { - "id": "sso", - "source": "workspace" - }, - { - "id": "step-ca", - "source": "workspace" - }, - { - "id": "tailscale", - "source": "workspace" - }, - { - "id": "telemetry", - "source": "workspace" - }, - { - "id": "termix-identity", - "source": "workspace" - }, - { - "id": "tmux-monitor", - "source": "workspace" - }, - { - "id": "totp", - "source": "workspace" - }, - { - "id": "tunnels", - "source": "workspace" - }, - { - "id": "vault", - "source": "workspace" - }, - { - "id": "wake-on-lan", - "source": "workspace" - }, - { - "id": "warpgate", - "source": "workspace" - }, - { - "id": "web-endpoint", - "source": "workspace" - }, - { - "id": "webauthn", - "source": "workspace" - }, - { - "id": "workspaces", - "source": "workspace" - } - ] + "plugins": [] } diff --git a/eslint.config.mjs b/eslint.config.mjs index a8717f6d3..675a66a6b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,64 +5,6 @@ import reactRefresh from "eslint-plugin-react-refresh"; import unusedImports from "eslint-plugin-unused-imports"; import tseslint from "typescript-eslint"; import { globalIgnores } from "eslint/config"; -import path from "node:path"; - -// A plugin may import @termix/plugin-sdk, npm packages and its own files. -// A relative path that climbs out of plugins// reaches core or another -// plugin, and "@/..." is core's alias. -const pluginBoundary = { - rules: { - "stay-inside": { - meta: { - type: "problem", - messages: { - core: "A plugin reaches core only through @termix/plugin-sdk. Use an SDK API, or add one.", - outside: - "A plugin cannot import files outside its own folder. Use the SDK, ctx.services, events, actions or slots.", - }, - }, - create(context) { - const file = context.filename.replace(/\\/g, "/"); - const root = /^(.*\/plugins\/[^/]+)\//.exec(file)?.[1]; - const check = (node, source) => { - if (typeof source !== "string") return; - if (source === "@" || source.startsWith("@/")) { - context.report({ node, messageId: "core" }); - return; - } - if (!root || !source.startsWith(".")) return; - const target = path.posix.normalize( - path.posix.join(path.posix.dirname(file), source), - ); - if (target !== root && !target.startsWith(`${root}/`)) { - context.report({ node, messageId: "outside" }); - } - }; - return { - ImportDeclaration: (node) => check(node, node.source.value), - ExportNamedDeclaration: (node) => check(node, node.source?.value), - ExportAllDeclaration: (node) => check(node, node.source.value), - ImportExpression: (node) => check(node, node.source.value), - // vi.mock("@/x") and require("../../x") name a module too. - CallExpression: (node) => { - const callee = node.callee; - const named = - (callee.type === "Identifier" && callee.name === "require") || - (callee.type === "MemberExpression" && - callee.object.type === "Identifier" && - callee.object.name === "vi" && - callee.property.type === "Identifier" && - ["mock", "doMock", "importActual", "unmock"].includes( - callee.property.name, - )); - const first = node.arguments[0]; - if (named && first?.type === "Literal") check(node, first.value); - }, - }; - }, - }, - }, -}; export default tseslint.config([ globalIgnores([ @@ -162,75 +104,4 @@ export default tseslint.config([ ], }, }, - { - // Core must not reach into a plugin. A plugin can be disabled, upgraded - // or removed, so an import from core turns "disabled" into a broken - // build rather than a missing feature. Core talks to plugins through the - // runtime in src/backend/plugins/, which handles absence. Core tests only - // use fixture plugins. - files: ["src/**/*.{ts,tsx}"], - rules: { - "no-restricted-imports": [ - "error", - { - patterns: [ - { - group: ["**/plugins/*/src/backend/**"], - message: - "Core must not import a plugin backend. Go through the plugin runtime, so disabling the plugin degrades cleanly.", - }, - ], - }, - ], - }, - }, - { - // The shell never imports a plugin. It reaches plugins only through the - // registries their frontends fill via the app object (A7). This block - // also carries the backend rule for src/ui files, because flat config - // replaces a rule's options rather than merging them. - files: ["src/ui/**/*.{ts,tsx}", "src/main.tsx"], - rules: { - "no-restricted-imports": [ - "error", - { - patterns: [ - { - group: ["**/plugins/*/src/**", "**/plugins/*/dist/**"], - message: - "The shell does not import plugins. Register the surface through the app object and read it from a registry.", - }, - ], - }, - ], - }, - }, - { - // The other direction. A plugin reaches core only through - // @termix/plugin-sdk, and never reaches into another plugin's source. - files: ["plugins/*/src/**/*.{ts,tsx,mjs}", "plugins/*/tests/**/*.{ts,tsx}"], - plugins: { "termix-plugins": pluginBoundary }, - rules: { - "termix-plugins/stay-inside": "error", - }, - }, - { - // react-i18next's hook reads core's namespace, so plugin strings show as raw keys. - files: ["plugins/*/src/**/*.{ts,tsx}"], - rules: { - "no-restricted-imports": [ - "error", - { - paths: [ - { - name: "react-i18next", - importNames: ["useTranslation", "Trans", "withTranslation"], - message: - "Use useTranslation from @termix/plugin-sdk/frontend so keys resolve in this plugin's namespace.", - }, - ], - }, - ], - }, - }, ]); diff --git a/knip.json b/knip.json index 4e7318445..a94f8044c 100644 --- a/knip.json +++ b/knip.json @@ -76,15 +76,6 @@ "packages/plugin-sdk": { "entry": ["src/*.ts", "src/testing/*.ts", "cli/**/*.mjs"], "project": ["src/**/*.ts", "cli/**/*.mjs"] - }, - "plugins/*": { - "entry": [ - "src/backend/index.ts", - "src/frontend/index.{ts,tsx}", - "tests/**/*.{ts,tsx}", - "vitest.config.ts" - ], - "project": ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}"] } } } diff --git a/package-lock.json b/package-lock.json index 5754f8430..d042faf4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "26.10.0", "hasInstallScript": true, "workspaces": [ - "packages/*", - "plugins/*" + "packages/*" ], "dependencies": { "@tanstack/react-virtual": "^3.14.13", @@ -138,27 +137,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.120.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.120.0.tgz", - "integrity": "sha512-ZlvmNFT/iIF6JD13rxbbMWD8nvGR0RaUp6yMQnoc+4Af0YjVVe/bIdW1XSQQsoxXAtg1NaT6Vak0LKFlJ4d37Q==", - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "^3.1.1", - "standardwebhooks": "^1.0.0" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", @@ -480,6 +458,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -584,6 +563,7 @@ "version": "6.20.3", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -596,6 +576,7 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.1.tgz", "integrity": "sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -604,276 +585,11 @@ "@lezer/common": "^1.1.0" } }, - "node_modules/@codemirror/lang-cpp": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.3.tgz", - "integrity": "sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/cpp": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-css": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", - "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/css": "^1.1.7" - } - }, - "node_modules/@codemirror/lang-go": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-go/-/lang-go-6.0.1.tgz", - "integrity": "sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/go": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-html": { - "version": "6.4.12", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.12.tgz", - "integrity": "sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.12" - } - }, - "node_modules/@codemirror/lang-java": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", - "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/java": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-javascript": { - "version": "6.2.5", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", - "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-jinja": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-jinja/-/lang-jinja-6.0.1.tgz", - "integrity": "sha512-P5kyHLObzjtbGj16h+hyvZTxJhSjBEeSx4wMjbnAf3b0uwTy2+F0zGjMZL4PQOm/mh2eGZ5xUDVZXgwP783Nsw==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-html": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.2.0", - "@lezer/lr": "^1.4.0" - } - }, - "node_modules/@codemirror/lang-json": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", - "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/json": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-less": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-less/-/lang-less-6.0.2.tgz", - "integrity": "sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ==", - "license": "MIT", - "dependencies": { - "@codemirror/lang-css": "^6.2.0", - "@codemirror/language": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-liquid": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/lang-liquid/-/lang-liquid-6.3.3.tgz", - "integrity": "sha512-U9EnyzWuAx1TX3MZSKDXydKH734n1Aogk/ijKdW+jdIwmbf9zSPQYOIKggC/b2PTRjQb7ddvMktmxnfTLuv/SQ==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-html": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.1" - } - }, - "node_modules/@codemirror/lang-markdown": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.2.tgz", - "integrity": "sha512-AwBOdkWYuA//WcM0xO5PfHPUcmz/O2i5o0Nsg1U69SII/loCJlFI1Romd9xp2HYb1kYJRGZotyqRghuHH5n8Kw==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.7.1", - "@codemirror/lang-html": "^6.0.0", - "@codemirror/language": "^6.3.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.2.1", - "@lezer/markdown": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-php": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", - "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", - "license": "MIT", - "dependencies": { - "@codemirror/lang-html": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/php": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-python": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", - "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.3.2", - "@codemirror/language": "^6.8.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.2.1", - "@lezer/python": "^1.1.4" - } - }, - "node_modules/@codemirror/lang-rust": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz", - "integrity": "sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/rust": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-sass": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz", - "integrity": "sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==", - "license": "MIT", - "dependencies": { - "@codemirror/lang-css": "^6.2.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/sass": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-sql": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", - "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-vue": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz", - "integrity": "sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==", - "license": "MIT", - "dependencies": { - "@codemirror/lang-html": "^6.0.0", - "@codemirror/lang-javascript": "^6.1.2", - "@codemirror/language": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.1" - } - }, - "node_modules/@codemirror/lang-wast": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz", - "integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-xml": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", - "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/xml": "^1.0.0" - } - }, - "node_modules/@codemirror/lang-yaml": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.3.tgz", - "integrity": "sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==", - "license": "MIT", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.2.0", - "@lezer/lr": "^1.0.0", - "@lezer/yaml": "^1.0.0" - } - }, "node_modules/@codemirror/language": { "version": "6.12.4", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz", "integrity": "sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -884,19 +600,11 @@ "style-mod": "^4.0.0" } }, - "node_modules/@codemirror/legacy-modes": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.4.tgz", - "integrity": "sha512-/cZr6qZyl08iYNLGsJ862CXXNI51LryRFRE40ejgoIjXZz0C1rGkD3/Ek5jM/8w1ceRjqtt4qx/KLMh4zBTgew==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0" - } - }, "node_modules/@codemirror/lint": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -908,6 +616,7 @@ "version": "6.7.2", "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.2.tgz", "integrity": "sha512-gUYkYhT2+n/+VGZ+8EzE5WFkYZUZYm1VOKDudIsNqh42uRVQJ0a6Yss9sdKT3MeOYfuL1N6AZA57oza0Oyr0LA==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", @@ -919,6 +628,7 @@ "version": "6.7.6", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.6.tgz", "integrity": "sha512-kAz+AncRtKuIknedxT1bq4XwXv4UowhbkHU1myPrtVb/jZtImWuV5BXzv5vK6i3kYACsdiZiQKFQQ5Mq7elW8w==", + "dev": true, "license": "MIT", "dependencies": { "@marijn/find-cluster-break": "^1.0.0" @@ -928,6 +638,7 @@ "version": "6.1.3", "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", @@ -940,6 +651,7 @@ "version": "6.43.13", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.13.tgz", "integrity": "sha512-sihaFrUzAsYBQsL9J2t69y8nfMQGwcYmggAZsk+kjPbjYZMyuf2hU8tUNTZ+P+isb6XRr8JE22TZlJxBoVdH1A==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/state": "^6.7.0", @@ -2371,12 +2083,6 @@ "url": "https://github.com/sponsors/ayuhito" } }, - "node_modules/@hexagon/base64": { - "version": "1.1.28", - "resolved": "https://registry.npmjs.org/@hexagon/base64/-/base64-1.1.28.tgz", - "integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==", - "license": "MIT" - }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -2443,27 +2149,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@iconify/react": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@iconify/react/-/react-5.2.1.tgz", - "integrity": "sha512-37GDR3fYDZmnmUn9RagyaX+zca24jfVOMY8E1IXTqJuE8pxNtN51KWPQe3VODOWvuUurq7q9uUu3CFrpqj5Iqg==", - "license": "MIT", - "dependencies": { - "@iconify/types": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/cyberalien" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", - "license": "MIT" - }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", @@ -3103,284 +2788,33 @@ "dev": true, "license": "MIT" }, - "node_modules/@ldapjs/asn1": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-2.0.0.tgz", - "integrity": "sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT" - }, - "node_modules/@ldapjs/attribute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ldapjs/attribute/-/attribute-1.0.0.tgz", - "integrity": "sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "2.0.0", - "@ldapjs/protocol": "^1.2.1", - "process-warning": "^2.1.0" - } - }, - "node_modules/@ldapjs/change": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ldapjs/change/-/change-1.0.0.tgz", - "integrity": "sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "2.0.0", - "@ldapjs/attribute": "1.0.0" - } - }, - "node_modules/@ldapjs/controls": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@ldapjs/controls/-/controls-2.1.0.tgz", - "integrity": "sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "^1.2.0", - "@ldapjs/protocol": "^1.2.1" - } - }, - "node_modules/@ldapjs/controls/node_modules/@ldapjs/asn1": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ldapjs/asn1/-/asn1-1.2.0.tgz", - "integrity": "sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT" - }, - "node_modules/@ldapjs/dn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@ldapjs/dn/-/dn-1.1.0.tgz", - "integrity": "sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "2.0.0", - "process-warning": "^2.1.0" - } - }, - "node_modules/@ldapjs/filter": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@ldapjs/filter/-/filter-2.1.1.tgz", - "integrity": "sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "2.0.0", - "@ldapjs/protocol": "^1.2.1", - "process-warning": "^2.1.0" - } - }, - "node_modules/@ldapjs/messages": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ldapjs/messages/-/messages-1.3.0.tgz", - "integrity": "sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "^2.0.0", - "@ldapjs/attribute": "^1.0.0", - "@ldapjs/change": "^1.0.0", - "@ldapjs/controls": "^2.1.0", - "@ldapjs/dn": "^1.1.0", - "@ldapjs/filter": "^2.1.1", - "@ldapjs/protocol": "^1.2.1", - "process-warning": "^2.2.0" - } - }, - "node_modules/@ldapjs/protocol": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@ldapjs/protocol/-/protocol-1.2.1.tgz", - "integrity": "sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT" - }, - "node_modules/@levischuck/tiny-cbor": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz", - "integrity": "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==", - "license": "MIT" - }, "node_modules/@lezer/common": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.3.tgz", "integrity": "sha512-H0iErY4e43LpXbYDyBci5W4v/RwTgGV3YzYOlJPiGZ8RY8w51kVE+xLn1tg3Z6UmSfyETvtsUK3r7YsgDQEI7Q==", + "dev": true, "license": "MIT" }, - "node_modules/@lezer/cpp": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.6.tgz", - "integrity": "sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/css": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.8.tgz", - "integrity": "sha512-EJn1zcL9qoDptief6ipWKZKLiOpXkxSe0+t8CH9oiMVcZlq7NBWrjCqnc/41EIjeo/ITj1gFFiATdTkaJDL+Og==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.0" - } - }, - "node_modules/@lezer/go": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lezer/go/-/go-1.0.1.tgz", - "integrity": "sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.3.0" - } - }, "node_modules/@lezer/highlight": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.5.tgz", "integrity": "sha512-O1GMVKgtf5YspFaRzpmqkVgMtIk2HG9uMAbH6wdAIdNh3TVzCsch/+cRi+6l2//UGDKe6NB9BW3cvuR5ezMQ0w==", + "dev": true, "license": "MIT", "dependencies": { "@lezer/common": "^1.3.0" } }, - "node_modules/@lezer/html": { - "version": "1.3.13", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", - "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/java": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.4.tgz", - "integrity": "sha512-gQylJ2xHV2xXsVqtnAcJrcROvLZJaQiC0tR//+q0k961BxqvyYttusPkJ8d9T1fPDJKHJW2etUH8XbIn9jhVUg==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/javascript": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.5.tgz", - "integrity": "sha512-sWg4yX1J6XW67AaAynVt0iwF0M5c+np36TEu+P2ifAJ8haRYvHnWDV28r1jdwnJehWCwXECutAUy56K4RBZIyg==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "node_modules/@lezer/json": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", - "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, "node_modules/@lezer/lr": { "version": "1.4.10", "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "dev": true, "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } }, - "node_modules/@lezer/markdown": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.7.2.tgz", - "integrity": "sha512-iTkYvoVcKt3WkeL7qUDyXHONZEwLio4wj8KTNi2dnjQEXBZKMV63BpQrPqfsM+OkvuRbiSTAcycYAsQzLhRNoQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0" - } - }, - "node_modules/@lezer/php": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.6.tgz", - "integrity": "sha512-QJ2xNXPmsm/Z3IpThq8fnJrEIVpxhsIoj6GZBW0JYEd/l9zxpJZW216X4fzqQY4vgpdGIumypvI4uQjd4tnYtw==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.1.0" - } - }, - "node_modules/@lezer/python": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.19.tgz", - "integrity": "sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/rust": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.3.tgz", - "integrity": "sha512-XxErOjZzQ7yJt1agUT4fu9qQvESZ3acgoxpPaPTPOiUx+duCjaVAtZGFIgphkHxlN05djdVAIOy/wItShMEjqQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/sass": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lezer/sass/-/sass-1.1.0.tgz", - "integrity": "sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/xml": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", - "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/yaml": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.4.tgz", - "integrity": "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.4.0" - } - }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -3440,296 +2874,9 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.4.tgz", "integrity": "sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==", + "dev": true, "license": "MIT" }, - "node_modules/@monaco-editor/loader": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.7.0.tgz", - "integrity": "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==", - "license": "MIT", - "dependencies": { - "state-local": "^1.0.6" - } - }, - "node_modules/@monaco-editor/react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz", - "integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==", - "license": "MIT", - "dependencies": { - "@monaco-editor/loader": "^1.5.0" - }, - "peerDependencies": { - "monaco-editor": ">= 0.25.0 < 1", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@napi-rs/canvas": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.100.tgz", - "integrity": "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==", - "license": "MIT", - "optional": true, - "workspaces": [ - "e2e/*" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.100", - "@napi-rs/canvas-darwin-arm64": "0.1.100", - "@napi-rs/canvas-darwin-x64": "0.1.100", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", - "@napi-rs/canvas-linux-arm64-musl": "0.1.100", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", - "@napi-rs/canvas-linux-x64-gnu": "0.1.100", - "@napi-rs/canvas-linux-x64-musl": "0.1.100", - "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", - "@napi-rs/canvas-win32-x64-msvc": "0.1.100" - } - }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz", - "integrity": "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz", - "integrity": "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz", - "integrity": "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz", - "integrity": "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz", - "integrity": "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz", - "integrity": "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz", - "integrity": "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==", - "cpu": [ - "riscv64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz", - "integrity": "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz", - "integrity": "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-arm64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz", - "integrity": "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.100", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz", - "integrity": "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, "node_modules/@noble/hashes": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", @@ -3776,20 +2923,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@peculiar/asn1-android": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-android/-/asn1-android-2.10.0.tgz", - "integrity": "sha512-IHKdYL5MRW76DOQCv2HSE/vUMOf2jqH4E7yhtQYe/xx+RxpPiXfkUdhex0N4/s5crsnXCGftRNzy4RJn1s6CZA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.10.0", - "asn1js": "^3.0.10", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/@peculiar/asn1-asym-key": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-asym-key/-/asn1-asym-key-2.10.0.tgz", @@ -3811,6 +2944,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.10.0.tgz", "integrity": "sha512-CkX0H4NCIOMHOU3rh2xXZywinYZ/EnJlaOlJiGI0e5L4XjFqHf4iH30LMbXWChVppdA9ljbanjtzQFOhDNfTWg==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3827,6 +2961,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.10.0.tgz", "integrity": "sha512-jTPTr/9rxKM+niQLMF3jiAMb0lWHUhUHIuSOhdGCIfpB4FAQYf9SoiXOgxP8bS/68IB+bj+1OHvVnDVCQfVUZw==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3842,6 +2977,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.10.0.tgz", "integrity": "sha512-GFd3iOjFrWX+QWH2R2dO5QSJyyRGv9CIBKtRlGlPNCvb2RvmCbBDexe91MJgV76c69d998Xsa+CvuQ98seoiPg==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3857,6 +2993,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.10.0.tgz", "integrity": "sha512-1y3QK9ZH1IPleAMmRoJlPS10eGkAWnULETW8zDFNUK1ffqpG7zmDY+kYBMYQgwlcQZ2iitLm2z2EBP0xzRXpaA==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-cms": "^2.10.0", @@ -3874,6 +3011,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.10.0.tgz", "integrity": "sha512-Ri+BZT9bnwqlHWmhs7lvWjvJRxKev2hA2+4EbZajgeYWCbR8hyv0znF4DhsFouOhMj2nSDV/iGJ6DMQtwITnCw==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3889,6 +3027,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.10.0.tgz", "integrity": "sha512-XIXsbDQFezYk6fudczkuvawkRD4GNpISGCqfYhdkJlvcGF/RFknU+0DDJagOaJqBf+PdPzk4J9oMqDGcvfR9HQ==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-cms": "^2.10.0", @@ -3908,6 +3047,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.10.0.tgz", "integrity": "sha512-4Jvmwlh3gZAhNZ4/u7JSyLuce9hofOFZ4o3PYKAccCImGnyaT5CMym/ATgvAvqpOYFNW531oPZvJlEBhJy9VfA==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3923,6 +3063,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.10.0.tgz", "integrity": "sha512-GhokD41lV4gQrrLYm3wCkHfBOnJrnhDMgt4XeMW8gzfE1UdJqIuSwsE+ggf82XBjUXRJylcm+KIGQFa4utIVLw==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/utils": "^2.0.2", @@ -3937,6 +3078,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.10.0.tgz", "integrity": "sha512-ucNVg8+ANveTpMN3fy9lA2alryONdXc2A4cEG2hMniWbvQt+YOZoe8BI80YYNO8FBcuDY1qbDQ4uQGQVraHxGA==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3952,6 +3094,7 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.10.0.tgz", "integrity": "sha512-/85GtKOKmgvuSJNlaFfwGWNdRSZZ+hpF02NyM01XiCdzpaZONiBltDyfluFPvFx966CR+ZHNSG1jniwpy07oGg==", + "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.10.0", @@ -3997,6 +3140,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.8.1" @@ -5605,121 +4749,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@redis/bloom": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-6.3.0.tgz", - "integrity": "sha512-NQ5poYpZr0jv6zazN1cy6JY5kIipKFZ3R9GTL4kOUTX3a9YAfY29U8bsE4lWhVQEM+G4V0BXVC57mubIdDijeA==", - "license": "MIT", - "engines": { - "node": ">= 20.0.0" - }, - "peerDependencies": { - "@redis/client": "^6.3.0" - } - }, - "node_modules/@redis/client": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-6.3.0.tgz", - "integrity": "sha512-fHrjwGCBvAANGi+TuRuwKi83sIOzgHr8eh+Uf48w3fSX7Hhrz174NrWqfJhAC2vnN0d/tfaZpdr6+o6JC3QHwg==", - "license": "MIT", - "engines": { - "node": ">= 20.0.0" - }, - "peerDependencies": { - "@node-rs/xxhash": "^1.1.0", - "@opentelemetry/api": ">=1 <2" - }, - "peerDependenciesMeta": { - "@node-rs/xxhash": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - } - } - }, - "node_modules/@redis/json": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-6.3.0.tgz", - "integrity": "sha512-yt6vRfPBXtW/3qm53cF81TSQpLMjWdW4/2E9WQ2GgbvsKkuCKCFQIcMiKlil1ZMnezK+IQuS795xKU5psbaegw==", - "license": "MIT", - "engines": { - "node": ">= 20.0.0" - }, - "peerDependencies": { - "@redis/client": "^6.3.0" - } - }, - "node_modules/@redis/search": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-6.3.0.tgz", - "integrity": "sha512-RPXAZmKjZZG7Tm7m1w9keFdXzbL91PiSHQBfQRe9xwW40jNsTRbG0FwhIz4QlHPU4gKDAsCy8IQHj4yZl4H9rw==", - "license": "MIT", - "engines": { - "node": ">= 20.0.0" - }, - "peerDependencies": { - "@redis/client": "^6.3.0" - } - }, - "node_modules/@redis/time-series": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-6.3.0.tgz", - "integrity": "sha512-+fJOB8mN1z5WRakCJNh5lVdz1KqvHGSOistsKeitD24QsCtPwyRQXWC0iq6T/i/ubLE/YcguZqPm8F598MKk4A==", - "license": "MIT", - "engines": { - "node": ">= 20.0.0" - }, - "peerDependencies": { - "@redis/client": "^6.3.0" - } - }, - "node_modules/@replit/codemirror-lang-nix": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-nix/-/codemirror-lang-nix-6.0.1.tgz", - "integrity": "sha512-lvzjoYn9nfJzBD5qdm3Ut6G3+Or2wEacYIDJ49h9+19WSChVnxv4ojf+rNmQ78ncuxIt/bfbMvDLMeMP0xze6g==", - "license": "MIT", - "peerDependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@replit/codemirror-lang-solidity": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-solidity/-/codemirror-lang-solidity-6.0.2.tgz", - "integrity": "sha512-/dpTVH338KFV6SaDYYSadkB4bI/0B0QRF/bkt1XS3t3QtyR49mn6+2k0OUQhvt2ZSO7kt10J+OPilRAtgbmX0w==", - "license": "MIT", - "dependencies": { - "@lezer/highlight": "^1.2.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0" - } - }, - "node_modules/@replit/codemirror-lang-svelte": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@replit/codemirror-lang-svelte/-/codemirror-lang-svelte-6.0.0.tgz", - "integrity": "sha512-U2OqqgMM6jKelL0GNWbAmqlu1S078zZNoBqlJBW+retTc5M4Mha6/Y2cf4SVg6ddgloJvmcSpt4hHrVoM4ePRA==", - "license": "MIT", - "peerDependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.1", - "@codemirror/lang-html": "^6.2.0", - "@codemirror/lang-javascript": "^6.1.1", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/javascript": "^1.2.0", - "@lezer/lr": "^1.0.0" - } - }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.12", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz", @@ -6307,59 +5336,6 @@ "url": "https://ko-fi.com/dangreen" } }, - "node_modules/@simplewebauthn/browser": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.3.0.tgz", - "integrity": "sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==", - "license": "MIT" - }, - "node_modules/@simplewebauthn/server": { - "version": "13.3.3", - "resolved": "https://registry.npmjs.org/@simplewebauthn/server/-/server-13.3.3.tgz", - "integrity": "sha512-LelX/lcy5cjc15A86i/aNxHhB5eU7dd20QsbP0VLAf9e38+SLlsnqCCyecx3xqfGofhmX05h1J9fKRYWxw+luA==", - "license": "MIT", - "dependencies": { - "@hexagon/base64": "^1.1.27", - "@levischuck/tiny-cbor": "^0.2.2", - "@peculiar/asn1-android": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.1", - "@peculiar/asn1-rsa": "^2.6.1", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.1", - "@peculiar/x509": "^1.14.3" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@simplewebauthn/server/node_modules/@peculiar/x509": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", - "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.0", - "@peculiar/asn1-csr": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.0", - "@peculiar/asn1-pkcs9": "^2.6.0", - "@peculiar/asn1-rsa": "^2.6.0", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.0", - "pvtsutils": "^1.3.6", - "reflect-metadata": "^0.2.2", - "tslib": "^2.8.1", - "tsyringe": "^4.10.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@stablelib/base64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", - "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", - "license": "MIT" - }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -6929,142 +5905,6 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, - "node_modules/@termix-plugin/acme-ssl": { - "resolved": "plugins/acme-ssl", - "link": true - }, - "node_modules/@termix-plugin/ai": { - "resolved": "plugins/ai", - "link": true - }, - "node_modules/@termix-plugin/alerts": { - "resolved": "plugins/alerts", - "link": true - }, - "node_modules/@termix-plugin/automations": { - "resolved": "plugins/automations", - "link": true - }, - "node_modules/@termix-plugin/docker": { - "resolved": "plugins/docker", - "link": true - }, - "node_modules/@termix-plugin/file-manager": { - "resolved": "plugins/file-manager", - "link": true - }, - "node_modules/@termix-plugin/fleets": { - "resolved": "plugins/fleets", - "link": true - }, - "node_modules/@termix-plugin/homepage": { - "resolved": "plugins/homepage", - "link": true - }, - "node_modules/@termix-plugin/host-metrics": { - "resolved": "plugins/host-metrics", - "link": true - }, - "node_modules/@termix-plugin/ldap": { - "resolved": "plugins/ldap", - "link": true - }, - "node_modules/@termix-plugin/network-topology": { - "resolved": "plugins/network-topology", - "link": true - }, - "node_modules/@termix-plugin/opkssh": { - "resolved": "plugins/opkssh", - "link": true - }, - "node_modules/@termix-plugin/proxmox": { - "resolved": "plugins/proxmox", - "link": true - }, - "node_modules/@termix-plugin/remote-desktop": { - "resolved": "plugins/remote-desktop", - "link": true - }, - "node_modules/@termix-plugin/secret-sources": { - "resolved": "plugins/secret-sources", - "link": true - }, - "node_modules/@termix-plugin/serial": { - "resolved": "plugins/serial", - "link": true - }, - "node_modules/@termix-plugin/session-recording": { - "resolved": "plugins/session-recording", - "link": true - }, - "node_modules/@termix-plugin/session-sharing": { - "resolved": "plugins/session-sharing", - "link": true - }, - "node_modules/@termix-plugin/snippets": { - "resolved": "plugins/snippets", - "link": true - }, - "node_modules/@termix-plugin/ssh-terminal": { - "resolved": "plugins/ssh-terminal", - "link": true - }, - "node_modules/@termix-plugin/sso": { - "resolved": "plugins/sso", - "link": true - }, - "node_modules/@termix-plugin/step-ca": { - "resolved": "plugins/step-ca", - "link": true - }, - "node_modules/@termix-plugin/tailscale": { - "resolved": "plugins/tailscale", - "link": true - }, - "node_modules/@termix-plugin/telemetry": { - "resolved": "plugins/telemetry", - "link": true - }, - "node_modules/@termix-plugin/termix-identity": { - "resolved": "plugins/termix-identity", - "link": true - }, - "node_modules/@termix-plugin/tmux-monitor": { - "resolved": "plugins/tmux-monitor", - "link": true - }, - "node_modules/@termix-plugin/totp": { - "resolved": "plugins/totp", - "link": true - }, - "node_modules/@termix-plugin/tunnels": { - "resolved": "plugins/tunnels", - "link": true - }, - "node_modules/@termix-plugin/vault": { - "resolved": "plugins/vault", - "link": true - }, - "node_modules/@termix-plugin/wake-on-lan": { - "resolved": "plugins/wake-on-lan", - "link": true - }, - "node_modules/@termix-plugin/warpgate": { - "resolved": "plugins/warpgate", - "link": true - }, - "node_modules/@termix-plugin/web-endpoint": { - "resolved": "plugins/web-endpoint", - "link": true - }, - "node_modules/@termix-plugin/webauthn": { - "resolved": "plugins/webauthn", - "link": true - }, - "node_modules/@termix-plugin/workspaces": { - "resolved": "plugins/workspaces", - "link": true - }, "node_modules/@termix/plugin-sdk": { "resolved": "packages/plugin-sdk", "link": true @@ -7255,6 +6095,7 @@ "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, "license": "MIT", "dependencies": { "@types/ms": "*" @@ -7278,17 +6119,9 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, "license": "MIT" }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", @@ -7322,15 +6155,6 @@ "@types/node": "*" } }, - "node_modules/@types/hast": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", - "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", @@ -7355,15 +6179,6 @@ "@types/node": "*" } }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -7375,6 +6190,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, "license": "MIT" }, "node_modules/@types/multer": { @@ -7408,12 +6224,6 @@ "pg-types": "^2.2.0" } }, - "node_modules/@types/prismjs": { - "version": "1.26.6", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", - "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", - "license": "MIT" - }, "node_modules/@types/qs": { "version": "6.15.1", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", @@ -7522,12 +6332,6 @@ "@types/superagent": "^8.1.0" } }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.18.2", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.2.tgz", @@ -7772,6 +6576,7 @@ "version": "4.25.12", "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.12.tgz", "integrity": "sha512-wR5lz7jbVcBGyiiLEObAmDikcc39KNqrrd4bdUhav85WZu48ttgnSf1YV+grHuMsj5l/WCw6u+r9ZuSfURJgiA==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -7795,50 +6600,11 @@ "@codemirror/view": ">=6.0.0" } }, - "node_modules/@uiw/codemirror-extensions-langs": { - "version": "4.25.12", - "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-langs/-/codemirror-extensions-langs-4.25.12.tgz", - "integrity": "sha512-B6X8FNQv8+nE1oVQ2Vf/8pdzDhceEU0QFcN1GSh1MbKdSZTmbueLjGQxUu1owEPWv8SU7qCVtW4do4dY1UsF7Q==", - "license": "MIT", - "dependencies": { - "@codemirror/lang-cpp": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-go": "^6.0.0", - "@codemirror/lang-html": "^6.0.0", - "@codemirror/lang-java": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/lang-jinja": "^6.0.0", - "@codemirror/lang-json": "^6.0.0", - "@codemirror/lang-less": "^6.0.0", - "@codemirror/lang-liquid": "^6.0.0", - "@codemirror/lang-markdown": "^6.0.0", - "@codemirror/lang-php": "^6.0.0", - "@codemirror/lang-python": "^6.0.0", - "@codemirror/lang-rust": "^6.0.0", - "@codemirror/lang-sass": "^6.0.0", - "@codemirror/lang-sql": "^6.0.0", - "@codemirror/lang-vue": "^0.1.0", - "@codemirror/lang-wast": "^6.0.0", - "@codemirror/lang-xml": "^6.0.0", - "@codemirror/lang-yaml": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/legacy-modes": "^6.0.0", - "@replit/codemirror-lang-nix": "^6.0.1", - "@replit/codemirror-lang-solidity": "^6.0.1", - "@replit/codemirror-lang-svelte": "^6.0.0", - "codemirror-lang-mermaid": "^0.5.0" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@codemirror/language": ">=6.0.0" - } - }, "node_modules/@uiw/react-codemirror": { "version": "4.25.12", "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.12.tgz", "integrity": "sha512-RE0Gj/QesD7/JWPnJREG8Kq8brGv2NAH17iUECcBrLkqXzzEAgsj/4Qqvz/OlK8XWKmzlsenLRkObT9H+gYQ0w==", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.6", @@ -7861,12 +6627,6 @@ "react-dom": ">=17.0.0" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", - "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", - "license": "ISC" - }, "node_modules/@vitejs/plugin-react": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.1.tgz", @@ -8077,44 +6837,12 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0.tgz", "integrity": "sha512-Dl31BCtBhLaUEECUbEiVcCLvLBbaeGYdT7NofB8OJkGTD3MWgBsaLjXvfGAD4tQNHhm6mbKyYkR7XD8kiZsdNg==", + "dev": true, "license": "MIT", "dependencies": { "js-base64": "^3.7.5" } }, - "node_modules/@xterm/addon-fit": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz", - "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==", - "license": "MIT" - }, - "node_modules/@xterm/addon-search": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0.tgz", - "integrity": "sha512-9OeuBFu0/uZJPu+9AHKY6g/w0Czyb/Ut0A5t79I4ULoU4IfU5BEpPFVGQxP4zTTMdfZEYkVIRYbHBX1xWwjeSA==", - "license": "MIT" - }, - "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0.tgz", - "integrity": "sha512-FxDnYcyuXhNl+XSqGZL/t0U9eiNb/q3EWT5rYkQT/zuig8Gz/VagnQANKHdDWFM2lTMk9ly0EFQxxxtZUoRetw==", - "license": "MIT" - }, - "node_modules/@xterm/addon-web-links": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.12.0.tgz", - "integrity": "sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==", - "license": "MIT" - }, - "node_modules/@xterm/xterm": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", - "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", - "license": "MIT", - "workspaces": [ - "addons/*" - ] - }, "node_modules/abbrev": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", @@ -8125,12 +6853,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "license": "MIT" - }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -8160,44 +6882,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/acme-client": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/acme-client/-/acme-client-5.4.0.tgz", - "integrity": "sha512-mORqg60S8iML6XSmVjqjGHJkINrCGLMj2QvDmFzI9vIlv1RGlyjmw3nrzaINJjkNsYXC41XhhD5pfy7CtuGcbA==", - "license": "MIT", - "dependencies": { - "@peculiar/x509": "^1.11.0", - "asn1js": "^3.0.5", - "axios": "^1.7.2", - "debug": "^4.3.5", - "node-forge": "^1.3.1" - }, - "engines": { - "node": ">= 16" - } - }, - "node_modules/acme-client/node_modules/@peculiar/x509": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", - "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-cms": "^2.6.0", - "@peculiar/asn1-csr": "^2.6.0", - "@peculiar/asn1-ecc": "^2.6.0", - "@peculiar/asn1-pkcs9": "^2.6.0", - "@peculiar/asn1-rsa": "^2.6.0", - "@peculiar/asn1-schema": "^2.6.0", - "@peculiar/asn1-x509": "^2.6.0", - "pvtsutils": "^1.3.6", - "reflect-metadata": "^0.2.2", - "tslib": "^2.8.1", - "tsyringe": "^4.10.0" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -8267,6 +6951,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8451,6 +7136,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, "license": "Python-2.0" }, "node_modules/argue-cli": { @@ -8509,6 +7195,7 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "pvtsutils": "^1.3.6", @@ -8519,15 +7206,6 @@ "node": ">=12.0.0" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -8618,28 +7296,6 @@ "proxy-from-env": "^2.1.0" } }, - "node_modules/backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==", - "license": "MIT", - "dependencies": { - "precond": "0.2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -8650,12 +7306,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/base32.js": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", - "integrity": "sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -9054,16 +7704,6 @@ ], "license": "CC-BY-4.0" }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chai": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", @@ -9086,46 +7726,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -9208,6 +7808,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9234,6 +7835,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "dev": true, "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", @@ -9245,21 +7847,11 @@ "@codemirror/view": "^6.0.0" } }, - "node_modules/codemirror-lang-mermaid": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/codemirror-lang-mermaid/-/codemirror-lang-mermaid-0.5.0.tgz", - "integrity": "sha512-Taw/2gPCyNArQJCxIP/HSUif+3zrvD+6Ugt7KJZ2dUKou/8r3ZhcfG8krNTZfV2iu8AuGnymKuo7bLPFyqsh/A==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.9.0", - "@lezer/highlight": "^1.1.6", - "@lezer/lr": "^1.3.10" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -9272,6 +7864,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -9286,16 +7879,6 @@ "node": ">= 0.8" } }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", @@ -9572,6 +8155,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, "license": "MIT" }, "node_modules/cors": { @@ -9664,6 +8248,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", + "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { @@ -9709,15 +8294,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cytoscape": { - "version": "3.34.3", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.34.3.tgz", - "integrity": "sha512-yfYGhRcGAntq6YBD583j4n0Eg3jIxvWmZtz/5uz9UYkeIStSlMxuUja+ec5j3iBD8nv1rwaOAYMW09tBdkSeaQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/data-urls": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", @@ -9764,15 +8340,6 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/decimal.js": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", @@ -9780,28 +8347,6 @@ "dev": true, "license": "MIT" }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", - "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -9831,6 +8376,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9862,19 +8408,6 @@ "dev": true, "license": "MIT" }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", @@ -9886,12 +8419,6 @@ "wrappy": "1" } }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "license": "MIT" - }, "node_modules/dir-compare": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", @@ -10901,16 +9428,6 @@ "node": ">=4.0" } }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -11018,21 +9535,6 @@ "node": ">=6.6.0" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -11061,12 +9563,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", - "license": "Unlicense" - }, "node_modules/fast-uri": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", @@ -11084,19 +9580,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -11303,14 +9786,6 @@ "node": ">= 0.6" } }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", @@ -11432,6 +9907,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -11594,25 +10070,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/guacamole-common-js": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-1.5.0.tgz", - "integrity": "sha512-zxztif3GGhKbg1RgOqwmqot8kXgv2HmHFg1EvWwd4q7UfEKvBcYZ0f+7G8HzvU+FUxF0Psqm9Kl5vCbgfrRgJg==", - "license": "Apache 2.0" - }, - "node_modules/guacamole-lite": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/guacamole-lite/-/guacamole-lite-1.2.0.tgz", - "integrity": "sha512-NeSYgbT5s5rxF0SE/kzJsV5Gg0IvnqoTOCbNIUMl23z1+SshaVfLExpxrEXSGTG0cdvY5lfZC1fOAepYriaXGg==", - "license": "Apache-2.0", - "dependencies": { - "deep-extend": "^0.6.0", - "ws": "^8.15.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -11675,76 +10132,6 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -11762,21 +10149,6 @@ "hermes-estree": "0.25.1" } }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", @@ -11847,16 +10219,6 @@ "url": "https://locize.com" } }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -11997,12 +10359,6 @@ "node": ">= 4" } }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -12066,12 +10422,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/inline-style-parser": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", - "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", - "license": "MIT" - }, "node_modules/ip-address": { "version": "10.7.2", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", @@ -12090,30 +10440,6 @@ "node": ">= 0.10" } }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -12121,16 +10447,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -12145,6 +10461,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12163,20 +10480,11 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12208,6 +10516,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, "license": "MIT" }, "node_modules/isbinaryfile": { @@ -12325,25 +10634,18 @@ "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/jose": { - "version": "6.2.12", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", - "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, "node_modules/js-base64": { "version": "3.9.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.4.tgz", "integrity": "sha512-PtOMXpEGuP0RRiRXsjzHzl44dMHxSu2CPvAhinupR1tBa88me+1DPqsobl7eupn5UukjLkln1ZnAOAOXOrYG0Q==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -12439,19 +10741,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-schema-to-ts": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "ts-algebra": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", @@ -12514,18 +10803,6 @@ "npm": ">=6" } }, - "node_modules/jszip": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz", - "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -12564,29 +10841,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ldapjs": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-3.0.7.tgz", - "integrity": "sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==", - "deprecated": "This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md", - "license": "MIT", - "dependencies": { - "@ldapjs/asn1": "^2.0.0", - "@ldapjs/attribute": "^1.0.0", - "@ldapjs/change": "^1.0.0", - "@ldapjs/controls": "^2.1.0", - "@ldapjs/dn": "^1.1.0", - "@ldapjs/filter": "^2.1.1", - "@ldapjs/messages": "^1.3.0", - "@ldapjs/protocol": "^1.2.1", - "abstract-logging": "^2.0.1", - "assert-plus": "^1.0.0", - "backoff": "^2.5.0", - "once": "^1.4.0", - "vasync": "^2.2.1", - "verror": "^1.10.1" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -12601,15 +10855,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -12990,28 +11235,6 @@ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -13022,20 +11245,6 @@ "tslib": "^2.0.3" } }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -13065,6 +11274,7 @@ "version": "1.49.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.49.0.tgz", "integrity": "sha512-ApfaNM4/7jBrVopqLJ84Jz8TMQ1Q1JeZ1PmKh25aeS3E1FTVcMkSh1bEQB7dfzdIrwXRP8p3z4AJjTIHHb5IIA==", + "dev": true, "license": "ISC", "peerDependencies": { "@types/react": "*", @@ -13107,15 +11317,6 @@ "source-map-js": "^1.2.1" } }, - "node_modules/make-cancellable-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz", - "integrity": "sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/make-cancellable-promise?sponsor=1" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -13132,25 +11333,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-event-props": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-event-props/-/make-event-props-2.0.0.tgz", - "integrity": "sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/make-event-props?sponsor=1" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -13160,290 +11342,6 @@ "node": ">= 0.4" } }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", - "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.1.tgz", - "integrity": "sha512-OuJHqvr455pwu2OaOrir7dbzqs4jlWKOtlu9L6GjcIjjQwNyw02VntQsr/Ek6/A13vgnXfUhBekWRUP1OkNivw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.3.tgz", - "integrity": "sha512-wgyJtgUkUcdU7zci7uuwc/tzoAhm0TswWhaXuvnWolqug+8jY7bgNkBBL0dYBnHgc/tHL7lCzRdPsdUxUKKBqw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdn-data": { "version": "2.27.1", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", @@ -13476,23 +11374,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-refs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", - "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", - "license": "MIT", - "funding": { - "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -13503,590 +11384,6 @@ "node": ">= 0.6" } }, - "node_modules/micromark": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", - "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-edit-map": "^1.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", - "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.1.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-edit-map": "^1.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.3" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.2.tgz", - "integrity": "sha512-pRzm4kDTu0MjlmBkxmS9yYhw60nncfcEwu9NNdPFSQEFXS95ZKyIIyTSHu/o3ReBUrLKYEq+7YaXCRn/bPB4MA==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", - "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-edit-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", - "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", - "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -14413,15 +11710,6 @@ "semver": "^7.3.5" } }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, "node_modules/node-gyp": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", @@ -14527,15 +11815,6 @@ "node": ">=18" } }, - "node_modules/nodemailer": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-10.0.13.tgz", - "integrity": "sha512-SzG86OlvcW/NNhUFC6uROMwRTL4n7MswfQqC/T8mhkmnY1YVa23zUEMYi4ijSeXSl9GLz9ZeTJDUatEDuY5FeQ==", - "license": "MIT-0", - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/nopt": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", @@ -14667,15 +11946,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -14683,12 +11953,6 @@ "dev": true, "license": "BlueOak-1.0.0" }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -14702,31 +11966,6 @@ "node": ">=6" } }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -14772,6 +12011,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -14841,18 +12081,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pdfjs-dist": { - "version": "5.4.296", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz", - "integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=20.16.0 || >=22.3.0" - }, - "optionalDependencies": { - "@napi-rs/canvas": "^0.1.80" - } - }, "node_modules/pe-library": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", @@ -15026,15 +12254,6 @@ "node": ">=10.4.0" } }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/postcss": { "version": "8.5.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", @@ -15132,14 +12351,6 @@ "node": ">=0.10.0" } }, - "node_modules/precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -15181,15 +12392,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/proc-log": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", @@ -15204,12 +12406,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/process-warning": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.3.2.tgz", - "integrity": "sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==", + "dev": true, "license": "MIT" }, "node_modules/progress": { @@ -15236,23 +12433,6 @@ "node": ">=10" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -15272,16 +12452,6 @@ "dev": true, "license": "ISC" }, - "node_modules/property-information": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", - "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/proxy-addr": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", @@ -15322,6 +12492,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.8.1" @@ -15331,6 +12502,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", + "dev": true, "license": "MIT", "engines": { "node": ">=16.0.0" @@ -15356,197 +12528,6 @@ "dev": true, "license": "MIT" }, - "node_modules/qrcode": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", - "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/qrcode/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/qrcode/node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/qrcode/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/qrcode/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/qrcode/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/qrcode/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, - "node_modules/qrcode/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/qs": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", @@ -15679,19 +12660,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-cytoscapejs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/react-cytoscapejs/-/react-cytoscapejs-2.0.0.tgz", - "integrity": "sha512-t3SSl1DQy7+JQjN+8QHi1anEJlM3i3aAeydHTsJwmjo/isyKK7Rs7oCvU6kZsB9NwZidzZQR21Vm2PcBLG/Tjg==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "cytoscape": "^3.2.19", - "react": ">=15.0.0" - } - }, "node_modules/react-dom": { "version": "19.3.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", @@ -15705,23 +12673,6 @@ "react": "^19.3.0" } }, - "node_modules/react-h5-audio-player": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/react-h5-audio-player/-/react-h5-audio-player-3.10.2.tgz", - "integrity": "sha512-2RlP1eF9V0KPZgBzaYM/hwIE1KxKV+E84gOkpaf0+3X1r56USO+V7AuCVa2FSTeWkQnrB03Osay99hR9FbSefQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.2", - "@iconify/react": "^5" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "react": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-i18next": { "version": "17.0.15", "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.15.tgz", @@ -15750,15 +12701,6 @@ } } }, - "node_modules/react-icons": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", - "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -15766,72 +12708,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-pdf": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/react-pdf/-/react-pdf-10.5.0.tgz", - "integrity": "sha512-zGKb6Ph5AYn2OfO4TS8Gg0Go3eW5K/TKy0+0FTm8EwFNbpgbuaWBZoJdYkJt+cRBCkizE9dYvqtMtHRj522Q4w==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "dequal": "^2.0.3", - "make-cancellable-promise": "^2.0.0", - "make-event-props": "^2.0.0", - "merge-refs": "^2.0.0", - "pdfjs-dist": "5.4.296", - "tiny-invariant": "^1.0.0", - "warning": "^4.0.0" - }, - "funding": { - "url": "https://github.com/wojtekmaj/react-pdf?sponsor=1" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-photo-view": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/react-photo-view/-/react-photo-view-1.2.7.tgz", - "integrity": "sha512-MfOWVPxuibncRLaycZUNxqYU8D9IA+rbGDDaq6GM8RIoGJal592hEJoRAyRSI7ZxyyJNJTLMUWWL3UIXHJJOpw==", - "license": "Apache-2.0", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", @@ -15904,35 +12780,6 @@ } } }, - "node_modules/react-syntax-highlighter": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.1.tgz", - "integrity": "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.28.4", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^5.0.0" - }, - "engines": { - "node": ">= 16.20.2" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, - "node_modules/react-xtermjs": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/react-xtermjs/-/react-xtermjs-1.0.12.tgz", - "integrity": "sha512-8uXBzVF4vP6r2NABxt/9heH1Y5Vx2v2hFrAk1kre+HyY3CjM6Prbc8ViqBRia6EBzI57IFBWOicYV1/k05JBtw==", - "license": "ISC", - "peerDependencies": { - "@xterm/xterm": "^5.5.0" - } - }, "node_modules/read-binary-file-arch": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", @@ -15950,6 +12797,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -15965,6 +12813,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, "license": "MIT" }, "node_modules/redent": { @@ -15981,114 +12830,18 @@ "node": ">=8" } }, - "node_modules/redis": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-6.3.0.tgz", - "integrity": "sha512-XFQbPie1lGpKeUZ8ySYY43yLnQy/iIQtHNvIr/1XdaABpb96rUSdm3Mfl+98FEVbv9ZJIzINMno5s0Nh8E5LEw==", - "license": "MIT", - "dependencies": { - "@redis/bloom": "6.3.0", - "@redis/client": "6.3.0", - "@redis/json": "6.3.0", - "@redis/search": "6.3.0", - "@redis/time-series": "6.3.0" - }, - "engines": { - "node": ">= 20.0.0" - } - }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, "license": "Apache-2.0" }, - "node_modules/refractor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz", - "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/prismjs": "^1.0.0", - "hastscript": "^9.0.0", - "parse-entities": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16104,12 +12857,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" - }, "node_modules/resedit": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", @@ -16386,18 +13133,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -16691,28 +13426,6 @@ "source-map": "^0.6.0" } }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/speakeasy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", - "integrity": "sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==", - "license": "MIT", - "dependencies": { - "base32.js": "0.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -16761,16 +13474,6 @@ "dev": true, "license": "MIT" }, - "node_modules/standardwebhooks": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", - "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==", - "license": "MIT", - "dependencies": { - "@stablelib/base64": "^1.0.0", - "fast-sha256": "^1.3.0" - } - }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -16781,12 +13484,6 @@ "node": ">= 6" } }, - "node_modules/state-local": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", - "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==", - "license": "MIT" - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -16815,6 +13512,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" @@ -16824,6 +13522,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, "license": "MIT" }, "node_modules/string-argv": { @@ -16853,20 +13552,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", @@ -16913,26 +13598,9 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.4.tgz", "integrity": "sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==", + "dev": true, "license": "MIT" }, - "node_modules/style-to-js": { - "version": "1.1.21", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", - "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.14" - } - }, - "node_modules/style-to-object": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", - "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.7" - } - }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -17019,6 +13687,7 @@ "version": "3.7.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.7.0.tgz", "integrity": "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -17100,12 +13769,6 @@ "semver": "bin/semver" } }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -17245,26 +13908,6 @@ "tree-kill": "cli.js" } }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", @@ -17275,12 +13918,6 @@ "utf8-byte-length": "^1.0.1" } }, - "node_modules/ts-algebra": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT" - }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -17323,6 +13960,7 @@ "version": "4.10.0", "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^1.9.3" @@ -17335,6 +13973,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, "license": "0BSD" }, "node_modules/tw-animate-css": { @@ -17437,93 +14076,6 @@ "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", "license": "MIT" }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -17679,6 +14231,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, "license": "MIT" }, "node_modules/vary": { @@ -17690,86 +14243,6 @@ "node": ">= 0.8" } }, - "node_modules/vasync": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/vasync/-/vasync-2.2.1.tgz", - "integrity": "sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "verror": "1.10.0" - } - }, - "node_modules/vasync/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" - }, - "node_modules/vasync/node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "engines": [ - "node >=0.6.0" - ], - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "license": "MIT", - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/vite": { "version": "8.3.1", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.1.tgz", @@ -18230,6 +14703,7 @@ "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, "license": "MIT" }, "node_modules/w3c-xmlserializer": { @@ -18245,15 +14719,6 @@ "node": "^22.22.2 || ^24.15.0 || >=26.0.0" } }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/webcrypto-core": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.9.2.tgz", @@ -18319,12 +14784,6 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC" - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -18555,16 +15014,6 @@ "zod": "^3.25.0 || ^4.0.0" } }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "packages/plugin-sdk": { "name": "@termix/plugin-sdk", "version": "1.0.0", @@ -18619,6 +15068,7 @@ "plugins/acme-ssl": { "name": "@termix-plugin/acme-ssl", "version": "1.0.0", + "extraneous": true, "dependencies": { "acme-client": "^5.4.0" }, @@ -18647,6 +15097,7 @@ "plugins/ai": { "name": "@termix-plugin/ai", "version": "1.0.0", + "extraneous": true, "dependencies": { "@anthropic-ai/sdk": "^0.120.0", "lucide-react": "^1.33.0", @@ -18681,6 +15132,7 @@ "plugins/alerts": { "name": "@termix-plugin/alerts", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0", "nodemailer": "^10.0.10" @@ -18712,6 +15164,7 @@ "plugins/automations": { "name": "@termix-plugin/automations", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -18743,6 +15196,7 @@ "plugins/docker": { "name": "@termix-plugin/docker", "version": "1.0.0", + "extraneous": true, "dependencies": { "@xterm/addon-clipboard": "^0.2.0", "@xterm/addon-fit": "^0.11.0", @@ -18778,6 +15232,7 @@ "plugins/file-manager": { "name": "@termix-plugin/file-manager", "version": "1.0.0", + "extraneous": true, "dependencies": { "@codemirror/autocomplete": "^6.20.3", "@codemirror/commands": "^6.11.1", @@ -18836,6 +15291,7 @@ "plugins/fleets": { "name": "@termix-plugin/fleets", "version": "1.0.0", + "extraneous": true, "dependencies": { "clsx": "^2.1.1", "jszip": "^3.10.2", @@ -18869,6 +15325,7 @@ "plugins/homepage": { "name": "@termix-plugin/homepage", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -18898,6 +15355,7 @@ "plugins/host-metrics": { "name": "@termix-plugin/host-metrics", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -18930,6 +15388,7 @@ "plugins/ldap": { "name": "@termix-plugin/ldap", "version": "1.0.0", + "extraneous": true, "dependencies": { "ldapjs": "^3.0.7", "lucide-react": "^1.33.0" @@ -18961,6 +15420,7 @@ "plugins/network-topology": { "name": "@termix-plugin/network-topology", "version": "1.0.0", + "extraneous": true, "dependencies": { "cytoscape": "^3.34.3", "lucide-react": "^1.33.0", @@ -18990,6 +15450,7 @@ "plugins/opkssh": { "name": "@termix-plugin/opkssh", "version": "1.0.0", + "extraneous": true, "dependencies": { "js-yaml": "^5.2.3", "lucide-react": "^1.33.0" @@ -19019,31 +15480,10 @@ "sonner": "^2.0.8" } }, - "plugins/opkssh/node_modules/js-yaml": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.2.tgz", - "integrity": "sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.mjs" - } - }, "plugins/proxmox": { "name": "@termix-plugin/proxmox", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19073,6 +15513,7 @@ "plugins/remote-desktop": { "name": "@termix-plugin/remote-desktop", "version": "1.0.0", + "extraneous": true, "dependencies": { "guacamole-common-js": "^1.5.0", "guacamole-lite": "^1.2.0", @@ -19105,6 +15546,7 @@ "plugins/secret-sources": { "name": "@termix-plugin/secret-sources", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19135,6 +15577,7 @@ "plugins/serial": { "name": "@termix-plugin/serial", "version": "1.0.0", + "extraneous": true, "dependencies": { "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", @@ -19166,6 +15609,7 @@ "plugins/session-recording": { "name": "@termix-plugin/session-recording", "version": "1.0.0", + "extraneous": true, "dependencies": { "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", @@ -19196,6 +15640,7 @@ "plugins/session-sharing": { "name": "@termix-plugin/session-sharing", "version": "1.0.0", + "extraneous": true, "dependencies": { "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", @@ -19231,6 +15676,7 @@ "plugins/snippets": { "name": "@termix-plugin/snippets", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19262,6 +15708,7 @@ "plugins/ssh-terminal": { "name": "@termix-plugin/ssh-terminal", "version": "1.0.0", + "extraneous": true, "dependencies": { "@xterm/addon-clipboard": "^0.2.0", "@xterm/addon-fit": "^0.11.0", @@ -19306,6 +15753,7 @@ "plugins/sso": { "name": "@termix-plugin/sso", "version": "1.0.0", + "extraneous": true, "dependencies": { "jose": "^6.2.12", "lucide-react": "^1.33.0", @@ -19338,6 +15786,7 @@ "plugins/step-ca": { "name": "@termix-plugin/step-ca", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0", "redis": "^6.2.1" @@ -19369,6 +15818,7 @@ "plugins/tailscale": { "name": "@termix-plugin/tailscale", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19399,6 +15849,7 @@ "plugins/telemetry": { "name": "@termix-plugin/telemetry", "version": "1.0.0", + "extraneous": true, "dependencies": { "drizzle-orm": "^0.45.2" }, @@ -19427,6 +15878,7 @@ "plugins/termix-identity": { "name": "@termix-plugin/termix-identity", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19458,6 +15910,7 @@ "plugins/tmux-monitor": { "name": "@termix-plugin/tmux-monitor", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19488,6 +15941,7 @@ "plugins/totp": { "name": "@termix-plugin/totp", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0", "qrcode": "^1.5.4", @@ -19520,6 +15974,7 @@ "plugins/tunnels": { "name": "@termix-plugin/tunnels", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19552,6 +16007,7 @@ "plugins/vault": { "name": "@termix-plugin/vault", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19583,6 +16039,7 @@ "plugins/wake-on-lan": { "name": "@termix-plugin/wake-on-lan", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19608,6 +16065,7 @@ "plugins/warpgate": { "name": "@termix-plugin/warpgate", "version": "1.0.0", + "extraneous": true, "devDependencies": { "@commitlint/cli": "^21.2.2", "@commitlint/config-conventional": "^21.2.0", @@ -19627,6 +16085,7 @@ "plugins/web-endpoint": { "name": "@termix-plugin/web-endpoint", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, @@ -19658,6 +16117,7 @@ "plugins/webauthn": { "name": "@termix-plugin/webauthn", "version": "1.0.0", + "extraneous": true, "dependencies": { "@simplewebauthn/browser": "^13.3.0", "@simplewebauthn/server": "^13.3.2", @@ -19690,6 +16150,7 @@ "plugins/workspaces": { "name": "@termix-plugin/workspaces", "version": "1.0.0", + "extraneous": true, "dependencies": { "lucide-react": "^1.33.0" }, diff --git a/package.json b/package.json index 10fd4263e..e32f06fed 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,7 @@ "name": "termix", "private": true, "workspaces": [ - "packages/*", - "plugins/*" + "packages/*" ], "version": "26.10.0", "description": "Self-hosted SSH and remote desktop management.", @@ -16,10 +15,10 @@ "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", - "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-node-pty.cjs && node scripts/patch-ssh2-agent.cjs && node scripts/apply-plugin-patches.cjs", + "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-better-sqlite3.cjs && node scripts/patch-nan.cjs && node scripts/patch-node-pty.cjs && node scripts/patch-ssh2-agent.cjs", "prepare": "husky || true", "prebuild": "node scripts/write-electron-build-info.cjs", - "lint": "node scripts/generate-dialect-schema.cjs --check && npm run plugin:validate-manifest && eslint . && node scripts/check-importmap-csp.cjs && node scripts/check-core-plugin-ids.cjs && node scripts/check-sdk-ui-exports.cjs && node scripts/check-plugin-locales.cjs && node scripts/check-plugin-uses.cjs && node scripts/check-test-locations.cjs", + "lint": "node scripts/generate-dialect-schema.cjs --check && eslint . && node scripts/check-importmap-csp.cjs && node scripts/check-core-plugin-ids.cjs && node scripts/check-sdk-ui-exports.cjs && node scripts/check-test-locations.cjs", "lint:fix": "eslint --fix .", "type-check": "npm run build:sdk && tsc -b --force", "test": "vitest run", @@ -48,11 +47,8 @@ "schema:generate": "node scripts/generate-dialect-schema.cjs", "schema:check": "node scripts/generate-dialect-schema.cjs --check", "schema:migrations": "drizzle-kit generate --config=drizzle.config.sqlite.ts && drizzle-kit generate --config=drizzle.config.pg.ts && drizzle-kit generate --config=drizzle.config.mysql.ts", - "plugin:validate-manifest": "node scripts/validate-plugin-manifest.cjs", "build:sdk": "npm run build --workspace @termix/plugin-sdk && node scripts/build-sdk-host.mjs", - "build:plugins": "node scripts/build-plugins.cjs", - "test:plugins": "node scripts/run-plugin-tests.cjs", - "test:all": "npm run test && npm run test:plugins" + "build:plugins": "node scripts/build-plugins.cjs" }, "dependencies": { "@tanstack/react-virtual": "^3.14.13", diff --git a/packages/plugin-sdk/cli/index.mjs b/packages/plugin-sdk/cli/index.mjs index 122f192be..fbef5dd43 100755 --- a/packages/plugin-sdk/cli/index.mjs +++ b/packages/plugin-sdk/cli/index.mjs @@ -2,7 +2,7 @@ /** * termix-plugin: build, validate, test, pack and sign a Termix plugin. * - * Run from a plugin directory (npm run build inside plugins// does). + * Run from a plugin directory (a plugin repo's npm run build does). */ import process from "node:process"; diff --git a/plugins/acme-ssl/.commitlintrc.json b/plugins/acme-ssl/.commitlintrc.json deleted file mode 100644 index cba65ea51..000000000 --- a/plugins/acme-ssl/.commitlintrc.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": ["@commitlint/config-conventional"], - "rules": { - "type-enum": [ - 2, - "always", - [ - "feat", - "fix", - "docs", - "style", - "refactor", - "perf", - "test", - "chore", - "revert" - ] - ], - "subject-case": [0] - } -} diff --git a/plugins/acme-ssl/.github/funding.yml b/plugins/acme-ssl/.github/funding.yml deleted file mode 100644 index c14d56027..000000000 --- a/plugins/acme-ssl/.github/funding.yml +++ /dev/null @@ -1 +0,0 @@ -custom: https://donate.termix.site/ diff --git a/plugins/acme-ssl/.github/pull_request_template.md b/plugins/acme-ssl/.github/pull_request_template.md deleted file mode 100644 index 9482e0908..000000000 --- a/plugins/acme-ssl/.github/pull_request_template.md +++ /dev/null @@ -1,34 +0,0 @@ -# Overview - -_Short summary of what this PR does_ - -- [ ] Added: ... -- [ ] Updated: ... -- [ ] Removed: ... -- [ ] Fixed: ... - -# Changes Made - -_Detailed explanation of changes (if needed)_ - -- ... - -# Related Issues - -_Link any issues this PR addresses_ - -- Closes #ISSUE_NUMBER -- Related to #ISSUE_NUMBER - -# Screenshots / Demos - -_(Optional: add before/after screenshots, GIFs, or console output)_ - -# Checklist - -- [ ] Code follows project style guidelines -- [ ] Supports mobile and desktop UI/app (if applicable) -- [ ] Added the change to `CHANGELOG.json` -- [ ] `npm run test`, `npm run typecheck` and `npm run validate` pass -- [ ] I have read `CONTRIBUTING.md` -- [ ] This is not a translation request. See [docs](https://docs.termix.site/translations) diff --git a/plugins/acme-ssl/.gitignore b/plugins/acme-ssl/.gitignore deleted file mode 100644 index e78aa8134..000000000 --- a/plugins/acme-ssl/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules -dist -coverage -*.log -.env -*.tmxplug -*.tmxplug.sig -termix-plugin-signing.key -.termix-tables-* diff --git a/plugins/acme-ssl/.husky/commit-msg b/plugins/acme-ssl/.husky/commit-msg deleted file mode 100644 index da9948310..000000000 --- a/plugins/acme-ssl/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -npx --no -- commitlint --edit "$1" diff --git a/plugins/acme-ssl/.husky/pre-commit b/plugins/acme-ssl/.husky/pre-commit deleted file mode 100644 index 2312dc587..000000000 --- a/plugins/acme-ssl/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -npx lint-staged diff --git a/plugins/acme-ssl/.prettierignore b/plugins/acme-ssl/.prettierignore deleted file mode 100644 index 012fbaa45..000000000 --- a/plugins/acme-ssl/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -coverage -node_modules -package-lock.json diff --git a/plugins/acme-ssl/.prettierrc b/plugins/acme-ssl/.prettierrc deleted file mode 100644 index fd873cbb3..000000000 --- a/plugins/acme-ssl/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "all", - "printWidth": 80, - "arrowParens": "always", - "endOfLine": "lf" -} diff --git a/plugins/acme-ssl/CHANGELOG.json b/plugins/acme-ssl/CHANGELOG.json deleted file mode 100644 index 8521464fe..000000000 --- a/plugins/acme-ssl/CHANGELOG.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/Termix-SSH/Termix/main/packages/plugin-sdk/schema/changelog.schema.json", - "releases": [ - { - "version": "1.0.0", - "changes": [ - { - "type": "added", - "text": "Certificates from Let's Encrypt or any ACME directory" - }, - { - "type": "added", - "text": "HTTP challenge on port 80, or a Cloudflare DNS challenge when port 80 is closed" - }, - { - "type": "added", - "text": "Renews before the certificate expires" - }, - { - "type": "added", - "text": "Swaps in the new certificate without a restart" - } - ] - } - ] -} diff --git a/plugins/acme-ssl/CONTRIBUTING.md b/plugins/acme-ssl/CONTRIBUTING.md deleted file mode 100644 index e887072a5..000000000 --- a/plugins/acme-ssl/CONTRIBUTING.md +++ /dev/null @@ -1,27 +0,0 @@ -# Contributing to ACME Certificates - -## Development - -```bash -npm run build # build into dist/ -npm run test # run this plugin's tests -npm run typecheck # type-check this plugin -npm run validate # check manifest.json -npm run format # format the code with Prettier -``` - -## Settings - -### Admin - -- **Renew automatically:** checks twice a day and renews when there is no certificate, it is self-signed, it does not cover the domain, or it expires within 30 days -- **Domain:** the public domain name the certificate covers -- **Email:** the contact email for the ACME account -- **Certificate authority:** Let's Encrypt, Let's Encrypt staging, or a custom ACME directory -- **ACME directory URL:** only for a custom authority, and must be https -- **Challenge type:** HTTP or DNS (Cloudflare) -- **Cloudflare API token:** used by the DNS challenge, stored encrypted - -## Permissions - -- `acme-ssl.manage`: see the certificate status and request a new certificate. Only admins have it by default. diff --git a/plugins/acme-ssl/README.md b/plugins/acme-ssl/README.md deleted file mode 100644 index f418624ab..000000000 --- a/plugins/acme-ssl/README.md +++ /dev/null @@ -1,38 +0,0 @@ -
- -Termix Logo - -

ACME Certificates

- -

Free HTTPS certificates for your Termix server, renewed for you

- -
- -
- -## Overview - -ACME Certificates gets the HTTPS certificate Termix serves from Let's Encrypt or any other ACME provider and keeps it renewed. - -
- -## Features - -- Certificates from Let's Encrypt or any ACME directory -- HTTP challenge on port 80, or a Cloudflare DNS challenge when port 80 is closed -- Renews before the certificate expires -- Swaps in the new certificate without a restart - -
- -## Support - -To report a bug or request a feature, open a [support ticket](https://github.com/Termix-SSH/Support/issues/new/choose). You need to be logged in to GitHub. Please be as detailed as possible, preferably in English. - -For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server. - -
- -## License - -Distributed under the Apache License Version 2.0. See [LICENSE](https://github.com/Termix-SSH/Termix/blob/main/LICENSE) for more information. diff --git a/plugins/acme-ssl/locales/en.json b/plugins/acme-ssl/locales/en.json deleted file mode 100644 index cc7e8300a..000000000 --- a/plugins/acme-ssl/locales/en.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Manage ACME certificates", - "description": "See the certificate status and request a new certificate now." - } - }, - "settings": { - "autoRenew": { - "label": "Renew automatically", - "description": "Check twice a day and get a new certificate when there is none, it does not cover the domain, or it expires within 30 days." - }, - "domain": { - "label": "Domain", - "description": "The public domain name for the certificate.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Email", - "description": "Contact email for the ACME account and expiry notices.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Certificate authority", - "description": "Where to get the certificate. Use staging to test without hitting Let's Encrypt rate limits.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (staging)", - "custom": "Custom ACME directory" - }, - "directoryUrl": { - "label": "ACME directory URL", - "description": "Only for a custom certificate authority. Must be an https URL." - }, - "challengeType": { - "label": "Challenge type", - "description": "How to prove you own the domain.", - "http": "HTTP - needs port 80 reachable from the internet", - "cloudflare": "DNS (Cloudflare) - needs a Cloudflare API token" - }, - "cloudflareToken": { - "label": "Cloudflare API token", - "description": "Scoped token with Zone:DNS:Edit permission for your domain." - } - }, - "status": { - "title": "Certificate status", - "none": "No certificate", - "names": "Covers {{names}}", - "issuer": "Issued by {{issuer}}", - "expires": "Expires {{date}}", - "lastIssued": "Last issued {{date}}", - "lastError": "Last attempt failed: {{error}}", - "missing": "Set the domain, email and the fields your challenge type needs, then save, before requesting a certificate.", - "request": "Request certificate now", - "requesting": "Requesting certificate...", - "issued": "Certificate issued and installed" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/af_ZA.json b/plugins/acme-ssl/locales/translated/af_ZA.json deleted file mode 100644 index eb23329c7..000000000 --- a/plugins/acme-ssl/locales/translated/af_ZA.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Bestuur ACME-sertifikate", - "description": "Sien die sertifikaatstatus en versoek nou 'n nuwe sertifikaat." - } - }, - "settings": { - "autoRenew": { - "label": "Hernu outomaties", - "description": "Kontroleer twee keer per dag en kry 'n nuwe sertifikaat wanneer daar geen is nie, dit nie die domein dek nie, of dit binne 30 dae verval." - }, - "domain": { - "label": "Domein", - "description": "Die publieke domeinnaam vir die sertifikaat.", - "placeholder": "termix.voorbeeld.com" - }, - "email": { - "label": "E-pos", - "description": "Kontak-e-posadres vir die ACME-rekening en vervalkennisgewings.", - "placeholder": "admin@voorbeeld.com" - }, - "provider": { - "label": "Sertifikaatowerheid", - "description": "Waar om die sertifikaat te kry. Gebruik staging om te toets sonder om Let's Encrypt-tarieflimiete te bereik.", - "letsencrypt": "Kom ons enkripteer", - "letsencryptStaging": "Kom ons enkripteer (opvoering)", - "custom": "Pasgemaakte ACME-gids" - }, - "directoryUrl": { - "label": "ACME-gids-URL", - "description": "Slegs vir 'n persoonlike sertifikaatowerheid. Moet 'n https-URL wees." - }, - "challengeType": { - "label": "Uitdagingsoort", - "description": "Hoe om te bewys dat jy die domein besit.", - "http": "HTTP - benodig poort 80 wat vanaf die internet bereikbaar is", - "cloudflare": "DNS (Cloudflare) - benodig 'n Cloudflare API-token" - }, - "cloudflareToken": { - "label": "Cloudflare API-token", - "description": "Omvangryke token met Zone:DNS:Wysig toestemming vir jou domein." - } - }, - "status": { - "title": "Sertifikaatstatus", - "none": "Geen sertifikaat nie", - "names": "Omslae {{names}}", - "issuer": "Uitgereik deur {{issuer}}", - "expires": "Vervaldatum {{date}}", - "lastIssued": "Laas uitgereik {{date}}", - "lastError": "Laaste poging het misluk: {{error}}", - "missing": "Stel die domein, e-posadres en die velde wat jou uitdagingsoort benodig, en stoor dit dan voordat jy 'n sertifikaat aanvra.", - "request": "Versoek nou 'n sertifikaat", - "requesting": "Versoek sertifikaat...", - "issued": "Sertifikaat uitgereik en geïnstalleer" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ar_SA.json b/plugins/acme-ssl/locales/translated/ar_SA.json deleted file mode 100644 index d66dc72e2..000000000 --- a/plugins/acme-ssl/locales/translated/ar_SA.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "إدارة شهادات ACME", - "description": "اطلع على حالة الشهادة واطلب شهادة جديدة الآن." - } - }, - "settings": { - "autoRenew": { - "label": "يتم التجديد تلقائياً", - "description": "تحقق مرتين في اليوم واحصل على شهادة جديدة عندما لا تكون هناك شهادة، أو عندما لا تغطي النطاق، أو عندما تنتهي صلاحيتها في غضون 30 يومًا." - }, - "domain": { - "label": "النطاق", - "description": "اسم النطاق العام للشهادة.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "البريد الإلكتروني", - "description": "البريد الإلكتروني للتواصل بخصوص حساب ACME وإشعارات انتهاء الصلاحية.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "المرجع المصدق (CA)", - "description": "من أين أحصل على الشهادة؟ استخدم بيئة الاختبار لإجراء الاختبارات دون تجاوز حدود معدل Let's Encrypt.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (مرحلة تجريبية)", - "custom": "دليل ACME مخصص" - }, - "directoryUrl": { - "label": "رابط دليل ACME", - "description": "مخصص فقط لهيئة إصدار شهادات مخصصة. يجب أن يكون عنوان URL من نوع https." - }, - "challengeType": { - "label": "نوع التحدي", - "description": "كيفية إثبات ملكيتك للنطاق.", - "http": "بروتوكول HTTP - يتطلب منفذ 80 يمكن الوصول إليه من الإنترنت", - "cloudflare": "نظام أسماء النطاقات (Cloudflare) - يتطلب رمز API من Cloudflare" - }, - "cloudflareToken": { - "label": "رمز واجهة برمجة تطبيقات Cloudflare", - "description": "رمز مقيد بصلاحية Zone:DNS:Edit للنطاق الخاص بك." - } - }, - "status": { - "title": "حالة الشهادة", - "none": "لا توجد شهادة", - "names": "يغطي {{names}}", - "issuer": "صادر عن {{issuer}}", - "expires": "تنتهي في {{date}}", - "lastIssued": "آخر إصدار {{date}}", - "lastError": "فشلت المحاولة الأخيرة: {{error}}", - "missing": "قم بتعيين النطاق والبريد الإلكتروني والحقول التي يحتاجها نوع التحدي الخاص بك، ثم احفظ التغييرات قبل طلب الشهادة.", - "request": "اطلب الشهادة الآن", - "requesting": "جارٍ طلب الشهادة...", - "issued": "تم إصدار الشهادة وتركيبها" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/bg_BG.json b/plugins/acme-ssl/locales/translated/bg_BG.json deleted file mode 100644 index d327bfcb4..000000000 --- a/plugins/acme-ssl/locales/translated/bg_BG.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Управление на ACME сертификати", - "description": "Вижте състоянието на сертификата и заявете нов сертификат сега." - } - }, - "settings": { - "autoRenew": { - "label": "Подновяване автоматично", - "description": "Проверявайте два пъти на ден и получавайте нов сертификат, когато няма такъв, не покрива домейна или изтича в рамките на 30 дни." - }, - "domain": { - "label": "Домейн", - "description": "Публичното домейн име за сертификата.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Имейл", - "description": "Контактен имейл за ACME акаунта и известия за изтичане.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Сертифициращ орган", - "description": "Къде да взема сертификата. Използвайте етапно тестване, за да не се достигат ограниченията на скоростта на Let's Encrypt.", - "letsencrypt": "Нека криптираме", - "letsencryptStaging": "Нека криптираме (подготовка)", - "custom": "Персонализирана ACME директория" - }, - "directoryUrl": { - "label": "URL адрес на директорията на ACME", - "description": "Само за персонализиран сертифициращ орган. Трябва да е https URL адрес." - }, - "challengeType": { - "label": "Тип предизвикателство", - "description": "Как да докажете, че притежавате домейна.", - "http": "HTTP - изисква порт 80, достъпен от интернет", - "cloudflare": "DNS (Cloudflare) - изисква Cloudflare API токен" - }, - "cloudflareToken": { - "label": "Cloudflare API токен", - "description": "Ограничен токен с разрешение Zone:DNS:Edit за вашия домейн." - } - }, - "status": { - "title": "Състояние на сертификата", - "none": "Няма сертификат", - "names": "Обхваща {{names}}", - "issuer": "Издадено от {{issuer}}", - "expires": "Изтича {{date}}", - "lastIssued": "Последно издадено {{date}}", - "lastError": "Последният опит не беше успешен: {{error}}", - "missing": "Задайте домейна, имейла и полетата, необходими за вашия тип предизвикателство, след което ги запазете, преди да заявите сертификат.", - "request": "Заявете сертификат сега", - "requesting": "Заявка за сертификат...", - "issued": "Сертификат издаден и инсталиран" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/bn_BD.json b/plugins/acme-ssl/locales/translated/bn_BD.json deleted file mode 100644 index 5bf348b83..000000000 --- a/plugins/acme-ssl/locales/translated/bn_BD.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME সার্টিফিকেটগুলি পরিচালনা করুন", - "description": "সার্টিফিকেটের অবস্থা দেখুন এবং এখনই একটি নতুন সার্টিফিকেটের জন্য অনুরোধ করুন।" - } - }, - "settings": { - "autoRenew": { - "label": "স্বয়ংক্রিয়ভাবে নবায়ন করুন", - "description": "দিনে দুইবার পরীক্ষা করুন এবং যখন কোনো সার্টিফিকেট থাকবে না, সেটি ডোমেইনকে কভার করবে না, অথবা ৩০ দিনের মধ্যে এর মেয়াদ শেষ হয়ে যাবে, তখন একটি নতুন সার্টিফিকেট সংগ্রহ করুন।" - }, - "domain": { - "label": "ডোমেইন", - "description": "সার্টিফিকেটটির পাবলিক ডোমেইন নাম।", - "placeholder": "termix.example.com" - }, - "email": { - "label": "ইমেল", - "description": "ACME অ্যাকাউন্ট এবং মেয়াদ শেষ হওয়ার বিজ্ঞপ্তির জন্য যোগাযোগের ইমেল।", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "শংসাপত্র কর্তৃপক্ষ", - "description": "সার্টিফিকেটটি কোথা থেকে পাবেন। Let's Encrypt-এর রেট লিমিট অতিক্রম না করে পরীক্ষা করার জন্য স্টেজিং ব্যবহার করুন।", - "letsencrypt": "আসুন এনক্রিপ্ট করি", - "letsencryptStaging": "লেটস এনক্রিপ্ট (স্টেজিং)", - "custom": "কাস্টম ACME ডিরেক্টরি" - }, - "directoryUrl": { - "label": "ACME ডিরেক্টরি URL", - "description": "শুধুমাত্র কাস্টম সার্টিফিকেট অথরিটির জন্য। অবশ্যই একটি https URL হতে হবে।" - }, - "challengeType": { - "label": "চ্যালেঞ্জের ধরণ", - "description": "ডোমেইনটির মালিকানা কীভাবে প্রমাণ করবেন।", - "http": "HTTP - এর জন্য ইন্টারনেট থেকে পোর্ট ৮০ অ্যাক্সেসযোগ্য হতে হবে।", - "cloudflare": "ডিএনএস (ক্লাউডফ্লেয়ার) - একটি ক্লাউডফ্লেয়ার এপিআই টোকেন প্রয়োজন" - }, - "cloudflareToken": { - "label": "ক্লাউডফ্লেয়ার এপিআই টোকেন", - "description": "আপনার ডোমেনের জন্য Zone:DNS:Edit অনুমতিসহ স্কোপড টোকেন।" - } - }, - "status": { - "title": "সার্টিফিকেটের অবস্থা", - "none": "কোন সার্টিফিকেট নেই", - "names": "{{names}} কভার করে", - "issuer": "{{issuer}} দ্বারা জারি করা হয়েছে", - "expires": "মেয়াদ শেষ হবে {{date}}", - "lastIssued": "সর্বশেষ প্রকাশিত {{date}}", - "lastError": "শেষ চেষ্টা ব্যর্থ: {{error}}", - "missing": "সার্টিফিকেটের জন্য অনুরোধ করার আগে, ডোমেইন, ইমেল এবং আপনার চ্যালেঞ্জ টাইপের জন্য প্রয়োজনীয় ফিল্ডগুলো সেট করুন, তারপর সেভ করুন।", - "request": "এখনই সার্টিফিকেটের জন্য অনুরোধ করুন", - "requesting": "সার্টিফিকেটের জন্য অনুরোধ...", - "issued": "সার্টিফিকেট ইস্যু এবং ইনস্টল করা হয়েছে" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ca_ES.json b/plugins/acme-ssl/locales/translated/ca_ES.json deleted file mode 100644 index 3c15830af..000000000 --- a/plugins/acme-ssl/locales/translated/ca_ES.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gestionar certificats ACME", - "description": "Consulta l'estat del certificat i sol·licita'n un de nou ara." - } - }, - "settings": { - "autoRenew": { - "label": "Renovar automàticament", - "description": "Revisa dues vegades al dia i aconsegueix un certificat nou quan no n'hi ha cap, no cobreix el domini o caduca en 30 dies." - }, - "domain": { - "label": "Domini", - "description": "El nom de domini públic del certificat.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Correu electrònic", - "description": "Correu electrònic de contacte per al compte ACME i avisos de caducitat.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autoritat de certificació", - "description": "On s'obté el certificat. Utilitzeu la configuració estadística per fer proves sense arribar als límits de velocitat de Let's Encrypt.", - "letsencrypt": "Xifrem", - "letsencryptStaging": "Xifrem (posada en escena)", - "custom": "Directori ACME personalitzat" - }, - "directoryUrl": { - "label": "URL del directori ACME", - "description": "Només per a una autoritat de certificació personalitzada. Ha de ser una URL https." - }, - "challengeType": { - "label": "Tipus de repte", - "description": "Com demostrar que ets el propietari del domini.", - "http": "HTTP: necessita que el port 80 sigui accessible des d'Internet", - "cloudflare": "DNS (Cloudflare): necessita un token d'API de Cloudflare" - }, - "cloudflareToken": { - "label": "Token de l'API de Cloudflare", - "description": "Token amb àmbit i permís Zone:DNS:Edit per al vostre domini." - } - }, - "status": { - "title": "Estat del certificat", - "none": "Sense certificat", - "names": "Cobertes {{names}}", - "issuer": "Publicat per {{issuer}}", - "expires": "Caduca {{date}}", - "lastIssued": "Última emissió {{date}}", - "lastError": "Últim intent fallit: {{error}}", - "missing": "Definiu el domini, el correu electrònic i els camps que necessita el vostre tipus de repte i, a continuació, deseu-ho abans de sol·licitar un certificat.", - "request": "Sol·licita el certificat ara", - "requesting": "Sol·licitant certificat...", - "issued": "Certificat emès i instal·lat" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/cs_CZ.json b/plugins/acme-ssl/locales/translated/cs_CZ.json deleted file mode 100644 index b55e1f14e..000000000 --- a/plugins/acme-ssl/locales/translated/cs_CZ.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Správa certifikátů ACME", - "description": "Zkontrolujte stav certifikátu a požádejte o nový certifikát." - } - }, - "settings": { - "autoRenew": { - "label": "Automaticky obnovit", - "description": "Kontrolujte dvakrát denně a požádejte o nový certifikát, pokud žádný neexistuje, pokud se nevztahuje na danou doménu nebo pokud jeho platnost vyprší do 30 dnů." - }, - "domain": { - "label": "Doména", - "description": "Název veřejné domény pro certifikát.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Kontaktní e-mail pro účet ACME a oznámení o vypršení platnosti.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Certifikační autorita", - "description": "Kde získat certifikát. Použijte staging k testování bez překročení limitů rychlosti Let's Encrypt.", - "letsencrypt": "Pojďme šifrovat", - "letsencryptStaging": "Pojďme šifrovat (příprava)", - "custom": "Vlastní adresář ACME" - }, - "directoryUrl": { - "label": "URL adresáře ACME", - "description": "Pouze pro vlastní certifikační autoritu. Musí se jednat o URL adresu typu https." - }, - "challengeType": { - "label": "Typ výzvy", - "description": "Jak prokázat, že jste vlastníkem domény.", - "http": "HTTP - vyžaduje dostupnost portu 80 z internetu", - "cloudflare": "DNS (Cloudflare) – vyžaduje token Cloudflare API" - }, - "cloudflareToken": { - "label": "Token API Cloudflare", - "description": "Token s vymezeným rozsahem a oprávněním Zóna:DNS:Úpravy pro vaši doménu." - } - }, - "status": { - "title": "Stav certifikátu", - "none": "Žádný certifikát", - "names": "Kryty {{names}}", - "issuer": "Vydáno uživatelem {{issuer}}", - "expires": "Platnost vyprší {{date}}", - "lastIssued": "Naposledy vydáno {{date}}", - "lastError": "Poslední pokus se nezdařil: {{error}}", - "missing": "Před vyžádáním certifikátu nastavte doménu, e-mail a pole, která váš typ výzvy vyžaduje, a poté je uložte.", - "request": "Vyžádejte si certifikát nyní", - "requesting": "Žádost o certifikát...", - "issued": "Certifikát vydán a nainstalován" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/da_DK.json b/plugins/acme-ssl/locales/translated/da_DK.json deleted file mode 100644 index 258e1ac89..000000000 --- a/plugins/acme-ssl/locales/translated/da_DK.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Administrer ACME-certifikater", - "description": "Se certifikatstatussen, og anmod om et nyt certifikat nu." - } - }, - "settings": { - "autoRenew": { - "label": "Forny automatisk", - "description": "Tjek to gange om dagen, og få et nyt certifikat, hvis der ikke er noget, det ikke dækker domænet, eller det udløber inden for 30 dage." - }, - "domain": { - "label": "Domæne", - "description": "Det offentlige domænenavn for certifikatet.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Kontakt-e-mailadresse for ACME-kontoen og udløbsmeddelelser.", - "placeholder": "admin@eksempel.com" - }, - "provider": { - "label": "Certifikatmyndighed", - "description": "Hvor kan man få certifikatet? Brug staging til at teste uden at nå Let's Encrypt-hastighedsgrænserne.", - "letsencrypt": "Lad os kryptere", - "letsencryptStaging": "Lad os kryptere (staging)", - "custom": "Brugerdefineret ACME-mappe" - }, - "directoryUrl": { - "label": "ACME-katalogets URL", - "description": "Kun for en brugerdefineret certifikatmyndighed. Skal være en https-URL." - }, - "challengeType": { - "label": "Udfordringstype", - "description": "Sådan beviser du, at du ejer domænet.", - "http": "HTTP - kræver port 80, der kan nås fra internettet", - "cloudflare": "DNS (Cloudflare) - kræver et Cloudflare API-token" - }, - "cloudflareToken": { - "label": "Cloudflare API-token", - "description": "Scoped-token med Zone:DNS:Rediger-tilladelse for dit domæne." - } - }, - "status": { - "title": "Certifikatstatus", - "none": "Intet certifikat", - "names": "Dækker {{names}}", - "issuer": "Udstedt af {{issuer}}", - "expires": "Udløber {{date}}", - "lastIssued": "Sidst udstedt {{date}}", - "lastError": "Sidste forsøg mislykkedes: {{error}}", - "missing": "Angiv domæne, e-mail og de felter, som din udfordringstype kræver, og gem derefter, før du anmoder om et certifikat.", - "request": "Anmod om certifikat nu", - "requesting": "Anmoder om certifikat...", - "issued": "Certifikat udstedt og installeret" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/de_DE.json b/plugins/acme-ssl/locales/translated/de_DE.json deleted file mode 100644 index 6ecadc449..000000000 --- a/plugins/acme-ssl/locales/translated/de_DE.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME-Zertifikate verwalten", - "description": "Prüfen Sie den Zertifikatsstatus und fordern Sie jetzt ein neues Zertifikat an." - } - }, - "settings": { - "autoRenew": { - "label": "Automatische Verlängerung", - "description": "Prüfen Sie zweimal täglich, ob ein Zertifikat vorhanden ist, ob das aktuelle Zertifikat die Domain nicht abdeckt oder ob es innerhalb von 30 Tagen abläuft." - }, - "domain": { - "label": "Domain", - "description": "Der öffentliche Domainname für das Zertifikat.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-Mail", - "description": "Kontakt-E-Mail-Adresse für das ACME-Konto und Benachrichtigungen zum Ablaufdatum.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Zertifizierungsstelle", - "description": "Wo Sie das Zertifikat erhalten. Nutzen Sie die Staging-Umgebung, um zu testen, ohne die Ratenbegrenzungen von Let's Encrypt zu erreichen.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (Staging)", - "custom": "Benutzerdefiniertes ACME-Verzeichnis" - }, - "directoryUrl": { - "label": "ACME-Verzeichnis-URL", - "description": "Nur für eine benutzerdefinierte Zertifizierungsstelle. Muss eine HTTPS-URL sein." - }, - "challengeType": { - "label": "Herausforderungstyp", - "description": "Wie Sie nachweisen, dass Sie die Domain besitzen.", - "http": "HTTP – benötigt Port 80, der aus dem Internet erreichbar sein muss.", - "cloudflare": "DNS (Cloudflare) – benötigt ein Cloudflare-API-Token" - }, - "cloudflareToken": { - "label": "Cloudflare API-Token", - "description": "Bereichsbezogener Token mit Zone:DNS:Edit-Berechtigung für Ihre Domain." - } - }, - "status": { - "title": "Zertifikatsstatus", - "none": "Kein Zertifikat", - "names": "Cover {{names}}", - "issuer": "Herausgegeben von {{issuer}}", - "expires": "Läuft ab {{date}}", - "lastIssued": "Zuletzt ausgestellt {{date}}", - "lastError": "Letzter Versuch fehlgeschlagen: {{error}}", - "missing": "Legen Sie die Domain, die E-Mail-Adresse und die Felder fest, die Ihr Herausforderungstyp benötigt, und speichern Sie die Einstellungen, bevor Sie ein Zertifikat anfordern.", - "request": "Zertifikat jetzt anfordern", - "requesting": "Zertifikat wird angefordert...", - "issued": "Zertifikat ausgestellt und installiert" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/el_GR.json b/plugins/acme-ssl/locales/translated/el_GR.json deleted file mode 100644 index 265c46201..000000000 --- a/plugins/acme-ssl/locales/translated/el_GR.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Διαχείριση πιστοποιητικών ACME", - "description": "Δείτε την κατάσταση του πιστοποιητικού και ζητήστε ένα νέο πιστοποιητικό τώρα." - } - }, - "settings": { - "autoRenew": { - "label": "Αυτόματη ανανέωση", - "description": "Ελέγχετε δύο φορές την ημέρα και λαμβάνετε ένα νέο πιστοποιητικό όταν δεν υπάρχει, δεν καλύπτει τον τομέα ή λήγει εντός 30 ημερών." - }, - "domain": { - "label": "Πεδίο ορισμού", - "description": "Το όνομα δημόσιου τομέα για το πιστοποιητικό.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Ηλεκτρονική διεύθυνση ηλεκτρονικού ταχυδρομείου επικοινωνίας για τον λογαριασμό ACME και τις ειδοποιήσεις λήξης.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Αρχή έκδοσης πιστοποιητικών", - "description": "Πού να λάβετε το πιστοποιητικό. Χρησιμοποιήστε τη σταδιακή επεξεργασία για να δοκιμάσετε χωρίς να υπερβείτε τα όρια ρυθμού του Let's Encrypt.", - "letsencrypt": "Ας κρυπτογραφήσουμε", - "letsencryptStaging": "Ας κρυπτογραφήσουμε (σταδιοποίηση)", - "custom": "Προσαρμοσμένος κατάλογος ACME" - }, - "directoryUrl": { - "label": "URL καταλόγου ACME", - "description": "Μόνο για μια προσαρμοσμένη αρχή έκδοσης πιστοποιητικών. Πρέπει να είναι μια διεύθυνση URL https." - }, - "challengeType": { - "label": "Τύπος πρόκλησης", - "description": "Πώς να αποδείξετε ότι είστε ο κάτοχος του domain.", - "http": "HTTP - χρειάζεται θύρα 80 προσβάσιμη από το διαδίκτυο", - "cloudflare": "DNS (Cloudflare) - χρειάζεται ένα διακριτικό API Cloudflare" - }, - "cloudflareToken": { - "label": "Διακριτικό API Cloudflare", - "description": "Διακριτικό πεδίου εφαρμογής με δικαίωμα Zone:DNS:Edit για τον τομέα σας." - } - }, - "status": { - "title": "Κατάσταση πιστοποιητικού", - "none": "Δεν υπάρχει πιστοποιητικό", - "names": "Καλύμματα {{names}}", - "issuer": "Εκδόθηκε από {{issuer}}", - "expires": "Λήγει {{date}}", - "lastIssued": "Τελευταία έκδοση {{date}}", - "lastError": "Η τελευταία προσπάθεια απέτυχε: {{error}}", - "missing": "Ορίστε τον τομέα, το email και τα πεδία που χρειάζεται ο τύπος της πρόκλησής σας και, στη συνέχεια, αποθηκεύστε το, πριν ζητήσετε πιστοποιητικό.", - "request": "Ζητήστε πιστοποιητικό τώρα", - "requesting": "Αίτημα πιστοποιητικού...", - "issued": "Έκδοση και εγκατάσταση πιστοποιητικού" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/es_ES.json b/plugins/acme-ssl/locales/translated/es_ES.json deleted file mode 100644 index eda5c2f95..000000000 --- a/plugins/acme-ssl/locales/translated/es_ES.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gestionar certificados ACME", - "description": "Consulte el estado del certificado y solicite uno nuevo ahora." - } - }, - "settings": { - "autoRenew": { - "label": "Renovación automática", - "description": "Compruebe dos veces al día y obtenga un nuevo certificado si no tiene ninguno, si no cubre el dominio o si caduca en los próximos 30 días." - }, - "domain": { - "label": "Dominio", - "description": "El nombre de dominio público para el certificado.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Correo electrónico", - "description": "Correo electrónico de contacto para la cuenta ACME y avisos de vencimiento.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autoridad de certificación", - "description": "Dónde obtener el certificado. Utilice el entorno de pruebas para realizar pruebas sin alcanzar los límites de uso de Let's Encrypt.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (en fase de pruebas)", - "custom": "Directorio ACME personalizado" - }, - "directoryUrl": { - "label": "URL del directorio ACME", - "description": "Solo para una autoridad de certificación personalizada. Debe ser una URL HTTPS." - }, - "challengeType": { - "label": "Tipo de desafío", - "description": "Cómo demostrar que usted es el propietario del dominio.", - "http": "HTTP: necesita que el puerto 80 sea accesible desde Internet.", - "cloudflare": "DNS (Cloudflare): requiere un token de API de Cloudflare." - }, - "cloudflareToken": { - "label": "Token de API de Cloudflare", - "description": "Token con permiso Zone:DNS:Edit limitado para tu dominio." - } - }, - "status": { - "title": "Estado del certificado", - "none": "Sin certificado", - "names": "Portadas {{names}}", - "issuer": "Emitido por {{issuer}}", - "expires": "Caduca {{date}}", - "lastIssued": "Última emisión {{date}}", - "lastError": "Último intento fallido: {{error}}", - "missing": "Configure el dominio, el correo electrónico y los campos que requiere su tipo de desafío, luego guarde los cambios y, a continuación, solicite un certificado.", - "request": "Solicita tu certificado ahora", - "requesting": "Solicitando certificado...", - "issued": "Certificado emitido e instalado" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/fi_FI.json b/plugins/acme-ssl/locales/translated/fi_FI.json deleted file mode 100644 index e21cd61f2..000000000 --- a/plugins/acme-ssl/locales/translated/fi_FI.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Hallinnoi ACME-sertifikaatteja", - "description": "Tarkista varmenteen tila ja pyydä uusi varmenne nyt." - } - }, - "settings": { - "autoRenew": { - "label": "Uudista automaattisesti", - "description": "Tarkista kahdesti päivässä ja hanki uusi varmenne, jos sitä ei ole, se ei kata verkkotunnusta tai se vanhenee 30 päivän kuluessa." - }, - "domain": { - "label": "Verkkotunnus", - "description": "Varmenteen julkinen verkkotunnus.", - "placeholder": "termix.esimerkki.com" - }, - "email": { - "label": "Sähköposti", - "description": "ACME-tilin yhteystiedot ja vanhenemisilmoitukset.", - "placeholder": "admin@esimerkki.com" - }, - "provider": { - "label": "Varmentaja", - "description": "Mistä varmenteen saa? Käytä testausympäristöä Let's Encrypt -nopeusrajoitusten ylittämättä.", - "letsencrypt": "Salataanpa", - "letsencryptStaging": "Let's Encrypt (vaiheistus)", - "custom": "Mukautettu ACME-hakemisto" - }, - "directoryUrl": { - "label": "ACME-hakemiston URL-osoite", - "description": "Vain mukautetulle varmentajalle. URL-osoitteen on oltava https." - }, - "challengeType": { - "label": "Haasteen tyyppi", - "description": "Kuinka todistaa omistavasi verkkotunnuksen.", - "http": "HTTP - vaatii portin 80, johon internetistä voi päästä käsiksi", - "cloudflare": "DNS (Cloudflare) – vaatii Cloudflare API -tunnuksen" - }, - "cloudflareToken": { - "label": "Cloudflare API -tunnus", - "description": "Laajuusaluetunnus, jolla on Zone:DNS:Edit-käyttöoikeus verkkotunnuksellesi." - } - }, - "status": { - "title": "Sertifikaatin tila", - "none": "Ei todistusta", - "names": "Kannet {{names}}", - "issuer": "Myöntäjä {{issuer}}", - "expires": "Vanhenee {{date}}", - "lastIssued": "Viimeksi myönnetty {{date}}", - "lastError": "Viimeisin yritys epäonnistui: {{error}}", - "missing": "Aseta verkkotunnus, sähköpostiosoite ja haastetyyppisi tarvitsemat kentät ja tallenna ne ennen varmenteen pyytämistä.", - "request": "Pyydä todistus nyt", - "requesting": "Pyydetään varmennetta...", - "issued": "Sertifikaatti myönnetty ja asennettu" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/fr_FR.json b/plugins/acme-ssl/locales/translated/fr_FR.json deleted file mode 100644 index 864abbd3f..000000000 --- a/plugins/acme-ssl/locales/translated/fr_FR.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gérer les certificats ACME", - "description": "Consultez l'état du certificat et demandez un nouveau certificat dès maintenant." - } - }, - "settings": { - "autoRenew": { - "label": "Renouvellement automatique", - "description": "Vérifiez deux fois par jour et obtenez un nouveau certificat s'il n'y en a pas, s'il ne couvre pas le domaine ou s'il expire dans les 30 jours." - }, - "domain": { - "label": "Domaine", - "description": "Le nom de domaine public pour le certificat.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Adresse électronique de contact pour le compte ACME et les avis d'expiration.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autorité de certification", - "description": "Où obtenir le certificat ? Utilisez un environnement de test pour effectuer des tests sans atteindre les limites de requêtes de Let's Encrypt.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (phase de test)", - "custom": "Répertoire ACME personnalisé" - }, - "directoryUrl": { - "label": "URL du répertoire ACME", - "description": "Uniquement pour une autorité de certification personnalisée. Doit être une URL HTTPS." - }, - "challengeType": { - "label": "Type de défi", - "description": "Comment prouver que vous êtes propriétaire du domaine.", - "http": "HTTP – nécessite que le port 80 soit accessible depuis Internet", - "cloudflare": "DNS (Cloudflare) - nécessite un jeton d'API Cloudflare" - }, - "cloudflareToken": { - "label": "Jeton d'API Cloudflare", - "description": "Jeton limité avec l'autorisation Zone:DNS:Edit pour votre domaine." - } - }, - "status": { - "title": "Statut du certificat", - "none": "Aucun certificat", - "names": "Couvertures {{names}}", - "issuer": "Émis par {{issuer}}", - "expires": "Expire le {{date}}", - "lastIssued": "Dernière émission le {{date}}", - "lastError": "Dernière tentative échouée : {{error}}", - "missing": "Configurez le domaine, l'adresse e-mail et les champs requis par votre type de défi, puis enregistrez, avant de demander un certificat.", - "request": "Demandez votre certificat dès maintenant", - "requesting": "Demande de certificat en cours...", - "issued": "Certificat délivré et installé" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/he_IL.json b/plugins/acme-ssl/locales/translated/he_IL.json deleted file mode 100644 index 1f858a0b2..000000000 --- a/plugins/acme-ssl/locales/translated/he_IL.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ניהול אישורי ACME", - "description": "ראה את סטטוס האישור ובקש אישור חדש כעת." - } - }, - "settings": { - "autoRenew": { - "label": "חידוש אוטומטי", - "description": "בדקו פעמיים ביום וקבלו תעודה חדשה אם אין, אם היא לא מכסה את הדומיין, או אם היא פגה תוך 30 יום." - }, - "domain": { - "label": "תְחוּם", - "description": "שם התחום הציבורי עבור התעודה.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "אֶלֶקטרוֹנִי", - "description": "כתובת דוא\"ל ליצירת קשר עבור חשבון ACME והודעות תפוגה.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "רשות אישורים", - "description": "היכן ניתן להשיג את האישור. השתמשו ב-staging כדי לבדוק מבלי להגיע למגבלות התעריף של Let's Encrypt.", - "letsencrypt": "בואו נצפין", - "letsencryptStaging": "בואו נצפין (שלבי)", - "custom": "ספריית ACME מותאמת אישית" - }, - "directoryUrl": { - "label": "כתובת אתר של ספריית ACME", - "description": "רק עבור רשות אישורים מותאמת אישית. חייבת להיות כתובת URL מסוג https." - }, - "challengeType": { - "label": "סוג האתגר", - "description": "איך להוכיח שאתה הבעלים של הדומיין.", - "http": "HTTP - דורש גישה לפורט 80 מהאינטרנט", - "cloudflare": "DNS (Cloudflare) - דורש אסימון API של Cloudflare" - }, - "cloudflareToken": { - "label": "אסימון API של Cloudflare", - "description": "אסימון בטווח עם הרשאת עריכה של אזור:DNS: עבור הדומיין שלך." - } - }, - "status": { - "title": "סטטוס התעודה", - "none": "אין תעודה", - "names": "כריכות {{names}}", - "issuer": "הונפק על ידי {{issuer}}", - "expires": "פג תוקף {{date}}", - "lastIssued": "הוצאה אחרונה {{date}}", - "lastError": "ניסיון אחרון נכשל: {{error}}", - "missing": "הגדר את הדומיין, האימייל והשדות הנדרשים עבור סוג האתגר שלך, ולאחר מכן שמור, לפני בקשת האישור.", - "request": "בקשת תעודה עכשיו", - "requesting": "מבקש תעודה...", - "issued": "תעודה הונפקה והותקנה" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/hi_IN.json b/plugins/acme-ssl/locales/translated/hi_IN.json deleted file mode 100644 index aee4104c0..000000000 --- a/plugins/acme-ssl/locales/translated/hi_IN.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME प्रमाणपत्रों का प्रबंधन करें", - "description": "प्रमाणपत्र की स्थिति देखें और अभी नए प्रमाणपत्र के लिए अनुरोध करें।" - } - }, - "settings": { - "autoRenew": { - "label": "स्वचालित रूप से नवीनीकरण करें", - "description": "दिन में दो बार जांच करें और यदि कोई प्रमाणपत्र मौजूद नहीं है, या वह डोमेन को कवर नहीं करता है, या 30 दिनों के भीतर समाप्त हो रहा है, तो नया प्रमाणपत्र प्राप्त करें।" - }, - "domain": { - "label": "डोमेन", - "description": "प्रमाणपत्र के लिए सार्वजनिक डोमेन नाम।", - "placeholder": "termix.example.com" - }, - "email": { - "label": "ईमेल", - "description": "ACME खाते और समाप्ति सूचनाओं के लिए संपर्क ईमेल पता।", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "प्रमाणपत्र प्राधिकरण", - "description": "सर्टिफिकेट कहां से प्राप्त करें। Let's Encrypt की दर सीमा का उल्लंघन किए बिना परीक्षण के लिए स्टेजिंग का उपयोग करें।", - "letsencrypt": "आइए एन्क्रिप्ट करें", - "letsencryptStaging": "लेट्स एनक्रिप्ट (चरणबद्ध)", - "custom": "कस्टम एसीएमई निर्देशिका" - }, - "directoryUrl": { - "label": "ACME निर्देशिका URL", - "description": "केवल कस्टम सर्टिफिकेट अथॉरिटी के लिए। यूआरएल HTTPS होना चाहिए।" - }, - "challengeType": { - "label": "चुनौती का प्रकार", - "description": "आप डोमेन के मालिक होने का प्रमाण कैसे दे सकते हैं?", - "http": "HTTP - इसके लिए इंटरनेट से पोर्ट 80 तक पहुंच आवश्यक है।", - "cloudflare": "DNS (Cloudflare) - इसके लिए Cloudflare API टोकन की आवश्यकता होती है।" - }, - "cloudflareToken": { - "label": "क्लाउडफ्लेयर एपीआई टोकन", - "description": "आपके डोमेन के लिए Zone:DNS:Edit अनुमति वाला स्कोप्ड टोकन।" - } - }, - "status": { - "title": "प्रमाणपत्र की स्थिति", - "none": "कोई प्रमाणपत्र नहीं", - "names": "कवर {{names}}", - "issuer": "{{issuer}} द्वारा जारी किया गया", - "expires": "{{date}} को समाप्त होगा", - "lastIssued": "अंतिम जारी: {{date}}", - "lastError": "अंतिम प्रयास विफल रहा: {{error}}", - "missing": "डोमेन, ईमेल और आपके चैलेंज टाइप के लिए आवश्यक फ़ील्ड सेट करें, फिर प्रमाणपत्र का अनुरोध करने से पहले सेव करें।", - "request": "अभी प्रमाणपत्र का अनुरोध करें", - "requesting": "प्रमाणपत्र अनुरोध किया जा रहा है...", - "issued": "प्रमाणपत्र जारी और स्थापित किया गया" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/hu_HU.json b/plugins/acme-ssl/locales/translated/hu_HU.json deleted file mode 100644 index c313fcff3..000000000 --- a/plugins/acme-ssl/locales/translated/hu_HU.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME tanúsítványok kezelése", - "description": "Tekintse meg a tanúsítvány állapotát, és igényeljen új tanúsítványt most." - } - }, - "settings": { - "autoRenew": { - "label": "Automatikus megújítás", - "description": "Naponta kétszer ellenőrizd, és szerezz be új tanúsítványt, ha nincs még tanúsítvány, ha nem fedi le a domaint, vagy ha 30 napon belül lejár." - }, - "domain": { - "label": "Domain", - "description": "A tanúsítvány nyilvános domainneve.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Email", - "description": "Az ACME fiókkal és a lejárati értesítésekkel kapcsolatos elérhetőségi e-mail cím.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Tanúsítványkibocsátó", - "description": "Hol lehet beszerezni a tanúsítványt? Használjon előzetes tesztelést a Let's Encrypt sebességkorlátainak elérése nélkül.", - "letsencrypt": "Titkosítsunk", - "letsencryptStaging": "Titkosítsuk (előkészítés)", - "custom": "Egyéni ACME címtár" - }, - "directoryUrl": { - "label": "ACME könyvtár URL-címe", - "description": "Csak egyéni hitelesítésszolgáltató esetén. Https URL-címnek kell lennie." - }, - "challengeType": { - "label": "Kihívás típusa", - "description": "Hogyan bizonyíthatod, hogy te vagy a domain tulajdonosa?", - "http": "HTTP – internetről elérhető 80-as port szükséges", - "cloudflare": "DNS (Cloudflare) – Cloudflare API token szükséges" - }, - "cloudflareToken": { - "label": "Cloudflare API token", - "description": "Hatókörbe tartozó token Zóna:DNS:Szerkesztés engedéllyel a domainhez." - } - }, - "status": { - "title": "Tanúsítvány állapota", - "none": "Nincs tanúsítvány", - "names": "Borítók {{names}}", - "issuer": "Kibocsátó: {{issuer}}", - "expires": "Lejár {{date}}", - "lastIssued": "Utolsó kiadás {{date}}", - "lastError": "Utolsó kísérlet sikertelen: {{error}}", - "missing": "Állítsa be a domaint, az e-mail címet és a kihívástípushoz szükséges mezőket, majd mentse el a beállításokat a tanúsítvány igénylése előtt.", - "request": "Igényeljen tanúsítványt most", - "requesting": "Tanúsítvány kérése...", - "issued": "Tanúsítvány kiállítva és telepítve" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/id_ID.json b/plugins/acme-ssl/locales/translated/id_ID.json deleted file mode 100644 index 9848edb05..000000000 --- a/plugins/acme-ssl/locales/translated/id_ID.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Kelola sertifikat ACME", - "description": "Lihat status sertifikat dan ajukan permohonan sertifikat baru sekarang." - } - }, - "settings": { - "autoRenew": { - "label": "Perpanjang secara otomatis", - "description": "Periksa dua kali sehari dan dapatkan sertifikat baru jika tidak ada, tidak mencakup domain, atau akan kedaluwarsa dalam 30 hari." - }, - "domain": { - "label": "Domain", - "description": "Nama domain publik untuk sertifikat tersebut.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Alamat email kontak untuk akun ACME dan pemberitahuan masa berlaku.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Lembaga sertifikasi", - "description": "Di mana mendapatkan sertifikat? Gunakan staging untuk pengujian tanpa melampaui batas kuota Let's Encrypt.", - "letsencrypt": "Mari Enkripsi", - "letsencryptStaging": "Let's Encrypt (pementasan)", - "custom": "Direktori ACME khusus" - }, - "directoryUrl": { - "label": "URL direktori ACME", - "description": "Hanya untuk otoritas sertifikat khusus. Harus berupa URL https." - }, - "challengeType": { - "label": "Jenis tantangan", - "description": "Bagaimana cara membuktikan bahwa Anda pemilik domain tersebut.", - "http": "HTTP - memerlukan port 80 yang dapat diakses dari internet.", - "cloudflare": "DNS (Cloudflare) - memerlukan token API Cloudflare" - }, - "cloudflareToken": { - "label": "Token API Cloudflare", - "description": "Token terlingkup dengan izin Zone:DNS:Edit untuk domain Anda." - } - }, - "status": { - "title": "Status sertifikat", - "none": "Tidak ada sertifikat", - "names": "Sampul {{names}}", - "issuer": "Dikeluarkan oleh {{issuer}}", - "expires": "Berakhir {{date}}", - "lastIssued": "Terakhir diterbitkan {{date}}", - "lastError": "Upaya terakhir gagal: {{error}}", - "missing": "Atur domain, email, dan kolom yang dibutuhkan oleh tipe tantangan Anda, lalu simpan, sebelum meminta sertifikat.", - "request": "Ajukan sertifikat sekarang", - "requesting": "Meminta sertifikat...", - "issued": "Sertifikat telah diterbitkan dan dipasang." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/it_IT.json b/plugins/acme-ssl/locales/translated/it_IT.json deleted file mode 100644 index ac8f8ad4a..000000000 --- a/plugins/acme-ssl/locales/translated/it_IT.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gestire i certificati ACME", - "description": "Verifica lo stato del certificato e richiedine uno nuovo ora." - } - }, - "settings": { - "autoRenew": { - "label": "Rinnovo automatico", - "description": "Controlla due volte al giorno e richiedi un nuovo certificato se non ne trovi uno, se non copre il dominio o se scade entro 30 giorni." - }, - "domain": { - "label": "Dominio", - "description": "Il nome di dominio pubblico per il certificato.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Email", - "description": "Indirizzo email per contattare l'account ACME e per ricevere avvisi di scadenza.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autorità di certificazione", - "description": "Dove ottenere il certificato. Utilizza un ambiente di staging per effettuare dei test senza superare i limiti di frequenza di Let's Encrypt.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (staging)", - "custom": "Elenco personalizzato ACME" - }, - "directoryUrl": { - "label": "URL della directory ACME", - "description": "Solo per un'autorità di certificazione personalizzata. Deve essere un URL HTTPS." - }, - "challengeType": { - "label": "Tipo di sfida", - "description": "Come dimostrare di essere il proprietario del dominio.", - "http": "HTTP - richiede che la porta 80 sia raggiungibile da Internet", - "cloudflare": "DNS (Cloudflare) - richiede un token API di Cloudflare" - }, - "cloudflareToken": { - "label": "Token API di Cloudflare", - "description": "Token con autorizzazione Zone:DNS:Edit per il tuo dominio." - } - }, - "status": { - "title": "Stato del certificato", - "none": "Nessun certificato", - "names": "Copertine {{names}}", - "issuer": "Emesso da {{issuer}}", - "expires": "Scade il {{date}}", - "lastIssued": "Ultimo rilascio il {{date}}", - "lastError": "Ultimo tentativo fallito: {{error}}", - "missing": "Imposta il dominio, l'indirizzo email e i campi richiesti dal tuo tipo di sfida, quindi salva prima di richiedere un certificato.", - "request": "Richiedi subito il certificato", - "requesting": "Richiesta certificato in corso...", - "issued": "Certificato rilasciato e installato" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ja_JP.json b/plugins/acme-ssl/locales/translated/ja_JP.json deleted file mode 100644 index de1f8f8fa..000000000 --- a/plugins/acme-ssl/locales/translated/ja_JP.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME証明書を管理する", - "description": "証明書のステータスを確認し、新しい証明書を今すぐリクエストしてください。" - } - }, - "settings": { - "autoRenew": { - "label": "自動更新", - "description": "1日に2回確認し、証明書がない場合、ドメインをカバーしていない場合、または有効期限が30日以内に切れる場合は、新しい証明書を取得してください。" - }, - "domain": { - "label": "ドメイン", - "description": "証明書の公開ドメイン名。", - "placeholder": "termix.example.com" - }, - "email": { - "label": "メールアドレス", - "description": "ACMEアカウントおよび有効期限通知に関する連絡先メールアドレス。", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "認証局", - "description": "証明書の入手方法。Let's Encryptのレート制限に抵触しないよう、ステージング環境を使用してテストしてください。", - "letsencrypt": "レッツ・エンクリプト", - "letsencryptStaging": "Let's Encrypt(ステージング環境)", - "custom": "カスタムACMEディレクトリ" - }, - "directoryUrl": { - "label": "ACMEディレクトリURL", - "description": "カスタム認証局のみ対象です。URLはhttpsでなければなりません。" - }, - "challengeType": { - "label": "チャレンジの種類", - "description": "ドメインの所有権を証明する方法。", - "http": "HTTP - インターネットからアクセス可能なポート80が必要です", - "cloudflare": "DNS(Cloudflare) - Cloudflare APIトークンが必要です" - }, - "cloudflareToken": { - "label": "Cloudflare APIトークン", - "description": "ドメインに対するZone:DNS:Edit権限を持つスコープ付きトークン。" - } - }, - "status": { - "title": "証明書のステータス", - "none": "証明書なし", - "names": "カバー {{names}}", - "issuer": "{{issuer}} によって発行されました", - "expires": "有効期限 {{date}}", - "lastIssued": "最終発行日 {{date}}", - "lastError": "最後の試行が失敗しました: {{error}}", - "missing": "証明書をリクエストする前に、ドメイン、メールアドレス、およびチャレンジタイプに必要なフィールドを設定して保存してください。", - "request": "今すぐ証明書を申請する", - "requesting": "証明書をリクエスト中...", - "issued": "証明書が発行され、インストールされました。" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ko_KR.json b/plugins/acme-ssl/locales/translated/ko_KR.json deleted file mode 100644 index dc6e54edc..000000000 --- a/plugins/acme-ssl/locales/translated/ko_KR.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME 인증서를 관리하세요", - "description": "인증서 상태를 확인하고 지금 바로 새 인증서를 신청하세요." - } - }, - "settings": { - "autoRenew": { - "label": "자동으로 갱신됩니다", - "description": "하루에 두 번 확인하고, 인증서가 없거나, 도메인을 포함하지 않거나, 30일 이내에 만료되는 경우 새 인증서를 발급받으세요." - }, - "domain": { - "label": "도메인", - "description": "인증서에 사용할 공개 도메인 이름입니다.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "이메일", - "description": "ACME 계정 및 만료 알림을 받을 수 있는 이메일 주소입니다.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "인증 기관", - "description": "인증서는 어디서 발급받나요? Let's Encrypt의 사용량 제한에 걸리지 않고 테스트하려면 스테이징 환경을 사용하세요.", - "letsencrypt": "렛츠인크립트", - "letsencryptStaging": "렛츠인크립트(스테이징)", - "custom": "사용자 지정 ACME 디렉토리" - }, - "directoryUrl": { - "label": "ACME 디렉토리 URL", - "description": "사용자 지정 인증 기관에만 해당됩니다. URL은 HTTPS여야 합니다." - }, - "challengeType": { - "label": "도전 유형", - "description": "도메인 소유권을 증명하는 방법.", - "http": "HTTP - 인터넷에서 접근 가능한 80번 포트가 필요합니다.", - "cloudflare": "DNS(클라우드플레어) - 클라우드플레어 API 토큰이 필요합니다." - }, - "cloudflareToken": { - "label": "클라우드플레어 API 토큰", - "description": "도메인에 대한 Zone:DNS:Edit 권한이 있는 범위 지정 토큰입니다." - } - }, - "status": { - "title": "인증서 상태", - "none": "인증서 없음", - "names": "{{names}}을 덮습니다.", - "issuer": "{{issuer}}에서 발행함", - "expires": "만료일: {{date}}", - "lastIssued": "마지막 발급일: {{date}}", - "lastError": "마지막 시도가 실패했습니다: {{error}}", - "missing": "인증서를 요청하기 전에 도메인, 이메일 및 인증 유형에 필요한 필드를 설정하고 저장하세요.", - "request": "지금 인증서를 요청하세요", - "requesting": "인증서 요청 중...", - "issued": "인증서가 발급 및 설치되었습니다." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/nl_NL.json b/plugins/acme-ssl/locales/translated/nl_NL.json deleted file mode 100644 index a27215a35..000000000 --- a/plugins/acme-ssl/locales/translated/nl_NL.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Beheer ACME-certificaten", - "description": "Bekijk de certificaatstatus en vraag nu een nieuw certificaat aan." - } - }, - "settings": { - "autoRenew": { - "label": "Automatisch verlengen", - "description": "Controleer tweemaal per dag en vraag een nieuw certificaat aan als er geen certificaat is, het huidige certificaat het domein niet dekt of het huidige certificaat binnen 30 dagen verloopt." - }, - "domain": { - "label": "Domein", - "description": "De publieke domeinnaam voor het certificaat.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Contact-e-mailadres voor het ACME-account en meldingen over vervaldatums.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Certificeringsinstantie", - "description": "Waar kan ik het certificaat verkrijgen? Gebruik een testomgeving om te testen zonder de limieten van Let's Encrypt te overschrijden.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (testomgeving)", - "custom": "Aangepaste ACME-directory" - }, - "directoryUrl": { - "label": "ACME-directory-URL", - "description": "Alleen voor een aangepaste certificeringsinstantie. Moet een https-URL zijn." - }, - "challengeType": { - "label": "Uitdagingstype", - "description": "Hoe bewijs je dat je de domeinnaam bezit?", - "http": "HTTP - vereist poort 80 die bereikbaar is vanaf internet.", - "cloudflare": "DNS (Cloudflare) - vereist een Cloudflare API-token" - }, - "cloudflareToken": { - "label": "Cloudflare API-token", - "description": "Een scoped token met Zone:DNS:Edit-machtiging voor uw domein." - } - }, - "status": { - "title": "Certificaatstatus", - "none": "Geen certificaat", - "names": "Omslagen {{names}}", - "issuer": "Uitgegeven door {{issuer}}", - "expires": "Verloopt {{date}}", - "lastIssued": "Laatst uitgegeven {{date}}", - "lastError": "Laatste poging mislukt: {{error}}", - "missing": "Stel het domein, het e-mailadres en de velden in die uw uitdagingstype vereist, sla de wijzigingen op en vraag vervolgens een certificaat aan.", - "request": "Vraag nu uw certificaat aan", - "requesting": "Certificaat aanvragen...", - "issued": "Certificaat afgegeven en geïnstalleerd" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/no_NO.json b/plugins/acme-ssl/locales/translated/no_NO.json deleted file mode 100644 index f38a4279a..000000000 --- a/plugins/acme-ssl/locales/translated/no_NO.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Administrer ACME-sertifikater", - "description": "Se sertifikatstatusen og be om et nytt sertifikat nå." - } - }, - "settings": { - "autoRenew": { - "label": "Forny automatisk", - "description": "Sjekk to ganger om dagen og få et nytt sertifikat når det ikke finnes noe, det ikke dekker domenet, eller det utløper innen 30 dager." - }, - "domain": { - "label": "Domene", - "description": "Det offentlige domenenavnet for sertifikatet.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-post", - "description": "Kontakt-e-postadresse for ACME-kontoen og utløpsvarsler.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Sertifikatmyndighet", - "description": "Hvor får man tak i sertifikatet? Bruk staging for å teste uten å nå hastighetsgrensene til Let's Encrypt.", - "letsencrypt": "La oss kryptere", - "letsencryptStaging": "La oss kryptere (staging)", - "custom": "Tilpasset ACME-katalog" - }, - "directoryUrl": { - "label": "URL-adressen til ACME-katalogen", - "description": "Kun for en tilpasset sertifiseringsinstans. Må være en https-URL." - }, - "challengeType": { - "label": "Utfordringstype", - "description": "Hvordan bevise at du eier domenet.", - "http": "HTTP - trenger port 80 som kan nås fra internett", - "cloudflare": "DNS (Cloudflare) – trenger et Cloudflare API-token" - }, - "cloudflareToken": { - "label": "Cloudflare API-token", - "description": "Scope-token med Zone:DNS:Rediger-tillatelse for domenet ditt." - } - }, - "status": { - "title": "Sertifikatstatus", - "none": "Ingen sertifikat", - "names": "Omslag {{names}}", - "issuer": "Utstedt av {{issuer}}", - "expires": "Utløper {{date}}", - "lastIssued": "Sist utstedt {{date}}", - "lastError": "Siste forsøk mislyktes: {{error}}", - "missing": "Angi domenet, e-postadressen og feltene utfordringstypen din trenger, og lagre deretter før du ber om et sertifikat.", - "request": "Be om sertifikat nå", - "requesting": "Ber om sertifikat...", - "issued": "Sertifikat utstedt og installert" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/pl_PL.json b/plugins/acme-ssl/locales/translated/pl_PL.json deleted file mode 100644 index 97f83ca88..000000000 --- a/plugins/acme-ssl/locales/translated/pl_PL.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Zarządzaj certyfikatami ACME", - "description": "Sprawdź status certyfikatu i poproś o nowy certyfikat już teraz." - } - }, - "settings": { - "autoRenew": { - "label": "Odnów automatycznie", - "description": "Sprawdzaj dwa razy dziennie i wykup nowy certyfikat, jeśli go nie masz, nie obejmuje on tej domeny lub wygasa w ciągu 30 dni." - }, - "domain": { - "label": "Domena", - "description": "Publiczna nazwa domeny certyfikatu.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Adres e-mail kontaktowy do konta ACME i powiadomień o wygaśnięciu.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Urząd certyfikacji (CA)", - "description": "Gdzie uzyskać certyfikat? Użyj środowiska testowego do testowania bez przekraczania limitów przepustowości Let's Encrypt.", - "letsencrypt": "Szyfrujmy", - "letsencryptStaging": "Let's Encrypt (etap przygotowawczy)", - "custom": "Niestandardowy katalog ACME" - }, - "directoryUrl": { - "label": "Adres URL katalogu ACME", - "description": "Tylko dla niestandardowego urzędu certyfikacji. Adres URL musi być w formacie https." - }, - "challengeType": { - "label": "Typ wyzwania", - "description": "Jak udowodnić, że jesteś właścicielem domeny.", - "http": "HTTP - wymaga dostępu do portu 80 z Internetu", - "cloudflare": "DNS (Cloudflare) – wymaga tokenu API Cloudflare" - }, - "cloudflareToken": { - "label": "Token API Cloudflare", - "description": "Token zakresowy z uprawnieniem Zone:DNS:Edit dla Twojej domeny." - } - }, - "status": { - "title": "Status certyfikatu", - "none": "Brak certyfikatu", - "names": "Obejmuje {{names}}", - "issuer": "Wydane przez {{issuer}}", - "expires": "Wygasa {{date}}", - "lastIssued": "Ostatnio wystawiono {{date}}", - "lastError": "Ostatnia próba nieudana: {{error}}", - "missing": "Ustaw domenę, adres e-mail i pola, których wymaga Twój typ wyzwania, a następnie zapisz, zanim poprosisz o certyfikat.", - "request": "Złóż wniosek o certyfikat już teraz", - "requesting": "Żądanie certyfikatu...", - "issued": "Certyfikat wydany i zainstalowany" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/pt_BR.json b/plugins/acme-ssl/locales/translated/pt_BR.json deleted file mode 100644 index cd9a43f50..000000000 --- a/plugins/acme-ssl/locales/translated/pt_BR.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gerenciar certificados ACME", - "description": "Veja o status do certificado e solicite um novo agora mesmo." - } - }, - "settings": { - "autoRenew": { - "label": "Renovar automaticamente", - "description": "Verifique duas vezes ao dia e obtenha um novo certificado quando não houver nenhum, se ele não cobrir o domínio ou se expirar em 30 dias." - }, - "domain": { - "label": "Domínio", - "description": "O nome de domínio público do certificado.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "E-mail de contato para a conta ACME e avisos de vencimento.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autoridade certificadora", - "description": "Onde obter o certificado. Use o ambiente de teste para realizar os testes sem atingir os limites de requisição do Let's Encrypt.", - "letsencrypt": "Vamos criptografar", - "letsencryptStaging": "Let's Encrypt (em fase de testes)", - "custom": "Diretório ACME personalizado" - }, - "directoryUrl": { - "label": "URL do diretório ACME", - "description": "Somente para uma autoridade de certificação personalizada. Deve ser um URL https." - }, - "challengeType": { - "label": "Tipo de desafio", - "description": "Como comprovar que você é o proprietário do domínio.", - "http": "HTTP - requer que a porta 80 esteja acessível pela internet.", - "cloudflare": "DNS (Cloudflare) - requer um token de API do Cloudflare" - }, - "cloudflareToken": { - "label": "Token da API Cloudflare", - "description": "Token com escopo definido e permissão Zone:DNS:Edit para o seu domínio." - } - }, - "status": { - "title": "Status do certificado", - "none": "Sem certificado", - "names": "Capas {{names}}", - "issuer": "Emitido por {{issuer}}", - "expires": "Expira em {{date}}", - "lastIssued": "Última edição {{date}}", - "lastError": "Última tentativa falhou: {{error}}", - "missing": "Defina o domínio, o e-mail e os campos necessários para o seu tipo de desafio e salve as alterações antes de solicitar um certificado.", - "request": "Solicite o certificado agora", - "requesting": "Solicitar certificado...", - "issued": "Certificado emitido e instalado." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/pt_PT.json b/plugins/acme-ssl/locales/translated/pt_PT.json deleted file mode 100644 index 975b670f9..000000000 --- a/plugins/acme-ssl/locales/translated/pt_PT.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gerir certificados ACME", - "description": "Veja o estado do certificado e solicite um novo agora mesmo." - } - }, - "settings": { - "autoRenew": { - "label": "Renovar automaticamente", - "description": "Verifique duas vezes por dia e obtenha um novo certificado quando não houver nenhum, se não cobrir o domínio ou se expirar dentro de 30 dias." - }, - "domain": { - "label": "Domínio", - "description": "O nome de domínio público para o certificado.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "E-mail de contacto para a conta ACME e avisos de vencimento.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autoridade de certificação", - "description": "Onde obter o certificado. Utilize o ambiente de teste para realizar os testes sem atingir os limites de requisição do Let's Encrypt.", - "letsencrypt": "Vamos encriptar", - "letsencryptStaging": "Let's Encrypt (em fase de testes)", - "custom": "Diretório ACME personalizado" - }, - "directoryUrl": { - "label": "URL do diretório ACME", - "description": "Apenas para uma autoridade de certificação personalizada. Deve ser um URL https." - }, - "challengeType": { - "label": "Tipo de desafio", - "description": "Como comprovar que é o proprietário do domínio.", - "http": "HTTP - requer que a porta 80 esteja acessível através da internet.", - "cloudflare": "DNS (Cloudflare) - requer um token API do Cloudflare" - }, - "cloudflareToken": { - "label": "Token da API Cloudflare", - "description": "Token com âmbito e permissão Zone:DNS:Edit para o seu domínio." - } - }, - "status": { - "title": "Estado do certificado", - "none": "Sem certificado", - "names": "Capas {{names}}", - "issuer": "Emitido por {{issuer}}", - "expires": "Expira a {{date}}", - "lastIssued": "Última emissão a {{date}}", - "lastError": "Última tentativa falhada: {{error}}", - "missing": "Defina o domínio, o e-mail e os campos necessários para o seu tipo de desafio e guarde as alterações antes de solicitar um certificado.", - "request": "Solicite já o certificado", - "requesting": "A solicitar certificado...", - "issued": "Certificado emitido e instalado." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ro_RO.json b/plugins/acme-ssl/locales/translated/ro_RO.json deleted file mode 100644 index 2243be8a9..000000000 --- a/plugins/acme-ssl/locales/translated/ro_RO.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Gestionați certificatele ACME", - "description": "Vedeți starea certificatului și solicitați un certificat nou acum." - } - }, - "settings": { - "autoRenew": { - "label": "Reînnoire automată", - "description": "Verificați de două ori pe zi și obțineți un certificat nou atunci când nu există, nu acoperă domeniul sau expiră în termen de 30 de zile." - }, - "domain": { - "label": "Domeniu", - "description": "Numele de domeniu public pentru certificat.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-mail", - "description": "Adresă de e-mail de contact pentru contul ACME și notificări de expirare.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Autoritatea de certificare", - "description": "Unde se obține certificatul. Folosește staging-ul pentru a testa fără a atinge limitele de rată Let's Encrypt.", - "letsencrypt": "Să criptăm", - "letsencryptStaging": "Să criptăm (punct în scenă)", - "custom": "Director ACME personalizat" - }, - "directoryUrl": { - "label": "URL-ul directorului ACME", - "description": "Numai pentru o autoritate de certificare personalizată. Trebuie să fie o adresă URL https." - }, - "challengeType": { - "label": "Tipul de provocare", - "description": "Cum să dovedești că deții domeniul.", - "http": "HTTP - necesită ca portul 80 să fie accesibil de pe internet", - "cloudflare": "DNS (Cloudflare) - necesită un token API Cloudflare" - }, - "cloudflareToken": { - "label": "Tokenul API Cloudflare", - "description": "Token cu scop specific și permisiune Zone:DNS:Edit pentru domeniul dvs." - } - }, - "status": { - "title": "Starea certificatului", - "none": "Fără certificat", - "names": "Acoperă {{names}}", - "issuer": "Emis de {{issuer}}", - "expires": "Expiră la {{date}}", - "lastIssued": "Ultima emisiune {{date}}", - "lastError": "Ultima încercare a eșuat: {{error}}", - "missing": "Setați domeniul, adresa de e-mail și câmpurile necesare tipului dvs. de provocare, apoi salvați, înainte de a solicita un certificat.", - "request": "Solicitați certificatul acum", - "requesting": "Se solicită certificatul...", - "issued": "Certificat emis și instalat" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/ru_RU.json b/plugins/acme-ssl/locales/translated/ru_RU.json deleted file mode 100644 index 145ea9b07..000000000 --- a/plugins/acme-ssl/locales/translated/ru_RU.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Управление сертификатами ACME", - "description": "Проверьте статус сертификата и запросите новый сертификат прямо сейчас." - } - }, - "settings": { - "autoRenew": { - "label": "Автоматическое продление", - "description": "Проверяйте дважды в день и получайте новый сертификат, если его нет, если он не покрывает домен или если срок действия старого истекает в течение 30 дней." - }, - "domain": { - "label": "Домен", - "description": "Общедоступное доменное имя для сертификата.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Email", - "description": "Адрес электронной почты, указанный в учетной записи ACME, для получения уведомлений об истечении срока действия.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Центр сертификации", - "description": "Где получить сертификат. Используйте тестовую среду (staging) для проверки без превышения лимитов запросов Let's Encrypt.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (тестовая версия)", - "custom": "Пользовательский каталог ACME" - }, - "directoryUrl": { - "label": "URL-адрес каталога ACME", - "description": "Только для пользовательского центра сертификации. URL-адрес должен быть HTTPS." - }, - "challengeType": { - "label": "Тип задачи", - "description": "Как доказать, что вы являетесь владельцем домена.", - "http": "HTTP — требуется доступ к порту 80 из интернета.", - "cloudflare": "DNS (Cloudflare) — требуется токен API Cloudflare." - }, - "cloudflareToken": { - "label": "API-токен Cloudflare", - "description": "Токен с ограниченной областью действия и разрешением Zone:DNS:Edit для вашего домена." - } - }, - "status": { - "title": "Статус сертификата", - "none": "Сертификат отсутствует", - "names": "Обложки {{names}}", - "issuer": "Выпущено {{issuer}}", - "expires": "Истекает {{date}}", - "lastIssued": "Последний выпуск {{date}}", - "lastError": "Последняя попытка не удалась: {{error}}", - "missing": "Укажите домен, адрес электронной почты и поля, необходимые для типа проверки подлинности, затем сохраните изменения, прежде чем запрашивать сертификат.", - "request": "Запросить сертификат сейчас", - "requesting": "Запрос сертификата...", - "issued": "Сертификат выдан и установлен." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/sr_SP.json b/plugins/acme-ssl/locales/translated/sr_SP.json deleted file mode 100644 index 2a7bd1420..000000000 --- a/plugins/acme-ssl/locales/translated/sr_SP.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Управљање ACME сертификатима", - "description": "Погледајте статус сертификата и одмах затражите нови сертификат." - } - }, - "settings": { - "autoRenew": { - "label": "Аутоматски обнови", - "description": "Проверавајте два пута дневно и набавите нови сертификат када га нема, када не покрива домен или када истиче у року од 30 дана." - }, - "domain": { - "label": "Домен", - "description": "Јавно име домена за сертификат.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Е-пошта", - "description": "Контакт имејл за ACME налог и обавештења о истеку.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Сертификатски ауторитет", - "description": "Где добити сертификат. Користите припрему за тестирање без достизања ограничења брзине Let's Encrypt-а.", - "letsencrypt": "Хајде да шифрујемо", - "letsencryptStaging": "Хајде да шифрујемо (припрема)", - "custom": "Прилагођени ACME директоријум" - }, - "directoryUrl": { - "label": "URL ACME директоријума", - "description": "Само за прилагођени ауторитет за сертификате. Мора бити https URL." - }, - "challengeType": { - "label": "Тип изазова", - "description": "Како доказати да сте власник домена.", - "http": "HTTP - потребан је порт 80 доступан са интернета", - "cloudflare": "DNS (Cloudflare) - потребан је Cloudflare API токен" - }, - "cloudflareToken": { - "label": "Клаудфлејр АПИ токен", - "description": "Ограничени токен са дозволом Zone:DNS:Edit за ваш домен." - } - }, - "status": { - "title": "Статус сертификата", - "none": "Без сертификата", - "names": "Навлаке {{names}}", - "issuer": "Издао/ла {{issuer}}", - "expires": "Истиче {{date}}", - "lastIssued": "Последње издање {{date}}", - "lastError": "Последњи покушај није успео: {{error}}", - "missing": "Подесите домен, имејл адресу и поља која су потребна вашем типу изазова, а затим сачувајте подешавања пре него што затражите сертификат.", - "request": "Затражите сертификат сада", - "requesting": "Захтева се сертификат...", - "issued": "Сертификат издат и инсталиран" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/sv_SE.json b/plugins/acme-ssl/locales/translated/sv_SE.json deleted file mode 100644 index 00154b1ad..000000000 --- a/plugins/acme-ssl/locales/translated/sv_SE.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Hantera ACME-certifikat", - "description": "Se certifikatets status och begär ett nytt certifikat nu." - } - }, - "settings": { - "autoRenew": { - "label": "Förnya automatiskt", - "description": "Kontrollera två gånger om dagen och skaffa ett nytt certifikat om det inte finns något, om det inte täcker domänen eller om det löper ut inom 30 dagar." - }, - "domain": { - "label": "Domän", - "description": "Det offentliga domännamnet för certifikatet.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-post", - "description": "Kontakta e-postadressen för ACME-kontot och meddelanden om utgångsdatum.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Certifikatutfärdare", - "description": "Var man kan hämta certifikatet. Använd staging för att testa utan att nå hastighetsgränserna för Let's Encrypt.", - "letsencrypt": "Låt oss kryptera", - "letsencryptStaging": "Låt oss kryptera (staging)", - "custom": "Anpassad ACME-katalog" - }, - "directoryUrl": { - "label": "ACME-katalogens URL", - "description": "Endast för en anpassad certifikatutfärdare. Måste vara en https-URL." - }, - "challengeType": { - "label": "Utmaningstyp", - "description": "Hur man bevisar att man äger domänen.", - "http": "HTTP - behöver port 80 som kan nås från internet", - "cloudflare": "DNS (Cloudflare) - behöver en Cloudflare API-token" - }, - "cloudflareToken": { - "label": "Cloudflare API-token", - "description": "Scoped-token med Zone:DNS:Edit-behörighet för din domän." - } - }, - "status": { - "title": "Certifikatstatus", - "none": "Inget certifikat", - "names": "Omslag {{names}}", - "issuer": "Utfärdad av {{issuer}}", - "expires": "Utgångsdatum {{date}}", - "lastIssued": "Senast utfärdad {{date}}", - "lastError": "Senaste försöket misslyckades: {{error}}", - "missing": "Ange domän, e-postadress och de fält som din utmaningstyp behöver, spara sedan innan du begär ett certifikat.", - "request": "Begär certifikat nu", - "requesting": "Begär certifikat...", - "issued": "Certifikat utfärdat och installerat" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/th_TH.json b/plugins/acme-ssl/locales/translated/th_TH.json deleted file mode 100644 index 489315489..000000000 --- a/plugins/acme-ssl/locales/translated/th_TH.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "จัดการใบรับรอง ACME", - "description": "ตรวจสอบสถานะใบรับรองและขอใบรับรองใหม่ได้เลยตอนนี้" - } - }, - "settings": { - "autoRenew": { - "label": "ต่ออายุอัตโนมัติ", - "description": "ตรวจสอบวันละสองครั้ง และขอใบรับรองใหม่เมื่อไม่มีใบรับรอง หากใบรับรองไม่ครอบคลุมโดเมน หรือหากใบรับรองหมดอายุภายใน 30 วัน" - }, - "domain": { - "label": "โดเมน", - "description": "ชื่อโดเมนสาธารณะสำหรับใบรับรอง", - "placeholder": "เทอร์มิกซ์.ตัวอย่าง.com" - }, - "email": { - "label": "อีเมล", - "description": "อีเมลสำหรับติดต่อเพื่อรับข้อมูลเกี่ยวกับบัญชี ACME และประกาศแจ้งเตือนวันหมดอายุ", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "หน่วยงานออกใบรับรอง", - "description": "จะขอรับใบรับรองได้จากที่ไหน ใช้ระบบทดสอบ (staging) เพื่อทดสอบโดยไม่ให้เกินขีดจำกัดการใช้งานของ Let's Encrypt", - "letsencrypt": "เลทส์ เอนคริปท์", - "letsencryptStaging": "Let's Encrypt (รุ่นทดสอบ)", - "custom": "ไดเร็กทอรี ACME แบบกำหนดเอง" - }, - "directoryUrl": { - "label": "URL ของไดเร็กทอรี ACME", - "description": "ใช้ได้เฉพาะกับหน่วยงานออกใบรับรองที่กำหนดเองเท่านั้น ต้องเป็น URL ที่ใช้ HTTPS" - }, - "challengeType": { - "label": "ประเภทความท้าทาย", - "description": "วิธีพิสูจน์ว่าคุณเป็นเจ้าของโดเมน", - "http": "HTTP - ต้องมีพอร์ต 80 ที่สามารถเข้าถึงได้จากอินเทอร์เน็ต", - "cloudflare": "DNS (Cloudflare) - ต้องใช้โทเค็น API ของ Cloudflare" - }, - "cloudflareToken": { - "label": "โทเค็น API ของ Cloudflare", - "description": "โทเค็นแบบจำกัดขอบเขตพร้อมสิทธิ์ Zone:DNS:Edit สำหรับโดเมนของคุณ" - } - }, - "status": { - "title": "สถานะใบรับรอง", - "none": "ไม่มีใบรับรอง", - "names": "ปก {{names}}", - "issuer": "ออกโดย {{issuer}}", - "expires": "หมดอายุ {{date}}", - "lastIssued": "ออกครั้งล่าสุด {{date}}", - "lastError": "ความพยายามครั้งล่าสุดล้มเหลว: {{error}}", - "missing": "ตั้งค่าโดเมน อีเมล และข้อมูลที่จำเป็นสำหรับประเภทการตรวจสอบสิทธิ์ของคุณ จากนั้นบันทึกก่อนที่จะขอใบรับรอง", - "request": "ขอใบรับรองตอนนี้", - "requesting": "ขอใบรับรอง...", - "issued": "ออกและติดตั้งใบรับรองแล้ว" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/tr_TR.json b/plugins/acme-ssl/locales/translated/tr_TR.json deleted file mode 100644 index a58645b94..000000000 --- a/plugins/acme-ssl/locales/translated/tr_TR.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "ACME sertifikalarını yönetin", - "description": "Sertifika durumunu kontrol edin ve şimdi yeni bir sertifika talep edin." - } - }, - "settings": { - "autoRenew": { - "label": "Otomatik olarak yenile", - "description": "Sertifikayı günde iki kez kontrol edin ve yoksa, alan adını kapsamıyorsa veya 30 gün içinde süresi dolacaksa yenisini alın." - }, - "domain": { - "label": "Alan Adı", - "description": "Sertifika için genel alan adı.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "E-posta", - "description": "ACME hesabı ve sona erme bildirimleri için iletişim e-posta adresi.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Sertifika yetkilisi", - "description": "Sertifikayı nereden alabilirsiniz? Let's Encrypt'in hız sınırlamalarına takılmadan test etmek için hazırlık ortamını kullanın.", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt (test aşaması)", - "custom": "Özel ACME dizini" - }, - "directoryUrl": { - "label": "ACME dizin URL'si", - "description": "Yalnızca özel sertifika yetkilisi için geçerlidir. HTTPS URL'si olmalıdır." - }, - "challengeType": { - "label": "Meydan okuma türü", - "description": "Alan adının size ait olduğunu nasıl kanıtlayabilirsiniz?", - "http": "HTTP - internetten erişilebilir 80 numaralı porta ihtiyaç duyar.", - "cloudflare": "DNS (Cloudflare) - Cloudflare API belirteci gerektirir." - }, - "cloudflareToken": { - "label": "Cloudflare API belirteci", - "description": "Alan adınız için Zone:DNS:Edit iznine sahip kapsamlı belirteç." - } - }, - "status": { - "title": "Sertifika durumu", - "none": "Sertifika yok", - "names": "Kapaklar {{names}}", - "issuer": "{{issuer}} tarafından yayınlandı", - "expires": "Son kullanma: {{date}}", - "lastIssued": "Son veriliş: {{date}}", - "lastError": "Son deneme başarısız oldu: {{error}}", - "missing": "Sertifika talebinde bulunmadan önce etki alanını, e-postayı ve doğrulama türünüzün gerektirdiği alanları ayarlayın, ardından kaydedin.", - "request": "Şimdi sertifika talep edin", - "requesting": "Sertifika isteniyor...", - "issued": "Sertifika düzenlendi ve kuruldu." - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/uk_UA.json b/plugins/acme-ssl/locales/translated/uk_UA.json deleted file mode 100644 index c5c87b5ca..000000000 --- a/plugins/acme-ssl/locales/translated/uk_UA.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Керування сертифікатами ACME", - "description": "Перегляньте статус сертифіката та запросіть новий сертифікат зараз." - } - }, - "settings": { - "autoRenew": { - "label": "Автоматичне поновлення", - "description": "Перевіряйте двічі на день та отримуйте новий сертифікат, коли його немає, він не поширюється на домен або термін його дії закінчується протягом 30 днів." - }, - "domain": { - "label": "Домен", - "description": "Ім'я публічного домену для сертифіката.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Електронна пошта", - "description": "Контактна електронна адреса для облікового запису ACME та повідомлень про закінчення терміну дії.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Центр сертифікації", - "description": "Де отримати сертифікат. Використовуйте проміжну версію для тестування без перевищення обмежень швидкості Let's Encrypt.", - "letsencrypt": "Давайте зашифруємо", - "letsencryptStaging": "Давайте зашифруємо (підготовка)", - "custom": "Користувацький каталог ACME" - }, - "directoryUrl": { - "label": "URL-адреса каталогу ACME", - "description": "Тільки для власного центру сертифікації. Має бути URL-адреса https." - }, - "challengeType": { - "label": "Тип виклику", - "description": "Як довести, що ви є власником домену.", - "http": "HTTP – потрібен порт 80, доступний з Інтернету", - "cloudflare": "DNS (Cloudflare) – потрібен токен API Cloudflare" - }, - "cloudflareToken": { - "label": "Токен API Cloudflare", - "description": "Токен з обмеженою областю дії та дозволом Zone:DNS:Edit для вашого домену." - } - }, - "status": { - "title": "Статус сертифіката", - "none": "Без сертифіката", - "names": "Обкладинки {{names}}", - "issuer": "Видано {{issuer}}", - "expires": "Термін дії закінчується {{date}}", - "lastIssued": "Останній виданий {{date}}", - "lastError": "Остання спроба не вдалася: {{error}}", - "missing": "Вкажіть домен, електронну пошту та поля, необхідні для вашого типу завдання, а потім збережіть зміни, перш ніж запитувати сертифікат.", - "request": "Замовити сертифікат зараз", - "requesting": "Запит сертифіката...", - "issued": "Сертифікат видано та встановлено" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/vi_VN.json b/plugins/acme-ssl/locales/translated/vi_VN.json deleted file mode 100644 index 4a7605c61..000000000 --- a/plugins/acme-ssl/locales/translated/vi_VN.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "Quản lý chứng chỉ ACME", - "description": "Xem trạng thái chứng chỉ và yêu cầu chứng chỉ mới ngay bây giờ." - } - }, - "settings": { - "autoRenew": { - "label": "Tự động gia hạn", - "description": "Hãy kiểm tra hai lần một ngày và lấy chứng chỉ mới nếu chứng chỉ hiện không có, không bao phủ tên miền hoặc hết hạn trong vòng 30 ngày." - }, - "domain": { - "label": "Tên miền", - "description": "Tên miền công khai để cấp chứng chỉ.", - "placeholder": "termix.example.com" - }, - "email": { - "label": "Email", - "description": "Địa chỉ email liên hệ cho tài khoản ACME và thông báo hết hạn.", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "Cơ quan chứng thực", - "description": "Lấy chứng chỉ ở đâu? Sử dụng môi trường thử nghiệm để kiểm tra mà không vượt quá giới hạn số lượng yêu cầu của Let's Encrypt.", - "letsencrypt": "Hãy mã hóa", - "letsencryptStaging": "Let's Encrypt (phiên bản thử nghiệm)", - "custom": "Danh mục ACME tùy chỉnh" - }, - "directoryUrl": { - "label": "URL thư mục ACME", - "description": "Chỉ dành cho cơ quan cấp chứng chỉ tùy chỉnh. Phải là URL HTTPS." - }, - "challengeType": { - "label": "Loại thử thách", - "description": "Cách chứng minh bạn sở hữu tên miền.", - "http": "HTTP - cần cổng 80 có thể truy cập được từ internet", - "cloudflare": "DNS (Cloudflare) - cần mã thông báo API của Cloudflare." - }, - "cloudflareToken": { - "label": "Mã thông báo API Cloudflare", - "description": "Token có phạm vi với quyền Zone:DNS:Edit cho tên miền của bạn." - } - }, - "status": { - "title": "Trạng thái chứng chỉ", - "none": "Không có chứng chỉ", - "names": "Ảnh bìa {{names}}", - "issuer": "Được phát hành bởi {{issuer}}", - "expires": "Hết hạn {{date}}", - "lastIssued": "Cấp lần cuối {{date}}", - "lastError": "Lần thử cuối cùng thất bại: {{error}}", - "missing": "Hãy thiết lập tên miền, email và các trường thông tin mà loại thử thách của bạn yêu cầu, sau đó lưu lại trước khi yêu cầu chứng chỉ.", - "request": "Yêu cầu chứng chỉ ngay bây giờ", - "requesting": "Đang yêu cầu chứng chỉ...", - "issued": "Giấy chứng nhận đã được cấp và cài đặt" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/zh_CN.json b/plugins/acme-ssl/locales/translated/zh_CN.json deleted file mode 100644 index abdf553a5..000000000 --- a/plugins/acme-ssl/locales/translated/zh_CN.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "管理 ACME 证书", - "description": "查看证书状态并立即申请新证书。" - } - }, - "settings": { - "autoRenew": { - "label": "自动续订", - "description": "每天检查两次,如果没有证书、证书不涵盖该域名或证书将在 30 天内过期,则获取新证书。" - }, - "domain": { - "label": "域名", - "description": "证书的公共域名。", - "placeholder": "termix.example.com" - }, - "email": { - "label": "邮箱", - "description": "ACME账户及到期通知的联系邮箱。", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "证书颁发机构", - "description": "证书获取途径。使用测试环境进行测试,避免超出 Let's Encrypt 的速率限制。", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt(测试版)", - "custom": "自定义 ACME 目录" - }, - "directoryUrl": { - "label": "ACME目录URL", - "description": "仅适用于自定义证书颁发机构。必须是 https URL。" - }, - "challengeType": { - "label": "挑战类型", - "description": "如何证明您拥有该域名。", - "http": "HTTP协议需要可通过互联网访问的80端口。", - "cloudflare": "DNS(Cloudflare)——需要 Cloudflare API 令牌" - }, - "cloudflareToken": { - "label": "Cloudflare API 令牌", - "description": "具有您域名的 Zone:DNS:Edit 权限的限定作用域令牌。" - } - }, - "status": { - "title": "证书状态", - "none": "无证书", - "names": "封面 {{names}}", - "issuer": "由 {{issuer}} 发行", - "expires": "过期于 {{date}}", - "lastIssued": "上次签发于 {{date}}", - "lastError": "最后一次尝试失败: {{error}}", - "missing": "设置域名、电子邮件和您的质询类型所需的字段,然后保存,再申请证书。", - "request": "立即申请证书", - "requesting": "正在请求证书...", - "issued": "证书已颁发并安装" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/locales/translated/zh_TW.json b/plugins/acme-ssl/locales/translated/zh_TW.json deleted file mode 100644 index 22122ab8d..000000000 --- a/plugins/acme-ssl/locales/translated/zh_TW.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "permissions": { - "manage": { - "title": "管理 ACME 證書", - "description": "查看證書狀態並立即申請新證書。" - } - }, - "settings": { - "autoRenew": { - "label": "自動續訂", - "description": "每天檢查兩次,如果沒有證書、證書不涵蓋該網域或證書將在 30 天內過期,則取得新證書。" - }, - "domain": { - "label": "網域", - "description": "憑證使用的公開網域名稱。", - "placeholder": "termix.example.com" - }, - "email": { - "label": "電子郵件", - "description": "ACME帳戶及到期通知的聯絡信箱。", - "placeholder": "admin@example.com" - }, - "provider": { - "label": "憑證授權中心", - "description": "證書取得途徑。使用測試環境進行測試,避免超出 Let's Encrypt 的速率限制。", - "letsencrypt": "Let's Encrypt", - "letsencryptStaging": "Let's Encrypt(測試版)", - "custom": "自訂 ACME 目錄" - }, - "directoryUrl": { - "label": "ACME目錄URL", - "description": "僅適用於自訂憑證授權單位。必須是 https URL。" - }, - "challengeType": { - "label": "挑戰類型", - "description": "如何證明您擁有該網域。", - "http": "HTTP協定需要80個可透過網際網路存取的連接埠。", - "cloudflare": "DNS(Cloudflare)-需要 Cloudflare API 令牌" - }, - "cloudflareToken": { - "label": "Cloudflare API 令牌", - "description": "具有您網域 Zone:DNS:Edit 權限的限定範圍權杖。" - } - }, - "status": { - "title": "證書狀態", - "none": "無憑證", - "names": "封面 {{names}}", - "issuer": "由 {{issuer}} 發行", - "expires": "到期日 {{date}}", - "lastIssued": "上次簽發日期 {{date}}", - "lastError": "最後嘗試失敗: {{error}}", - "missing": "設定網域名稱、電子郵件和您的質詢類型所需的字段,然後儲存,再申請憑證。", - "request": "立即申請證書", - "requesting": "請求憑證中...", - "issued": "證書已頒發並安裝" - }, - "plugin": { - "name": "ACME Certificates" - } -} diff --git a/plugins/acme-ssl/manifest.json b/plugins/acme-ssl/manifest.json deleted file mode 100644 index af58690fe..000000000 --- a/plugins/acme-ssl/manifest.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "id": "acme-ssl", - "name": "ACME Certificates", - "version": "1.0.0", - "description": "Gets the Termix HTTPS certificate from Let's Encrypt or any ACME provider and renews it for you.", - "author": { - "name": "Termix" - }, - "license": "Apache-2.0", - "repository": "https://github.com/Termix-SSH/Termix", - "category": "Access & Security", - "icon": "Lock", - "engine": { - "termix": ">=26.10.0", - "api": "1" - }, - "capabilities": [ - "system:tls", - "kv:own", - "secrets:own", - "network:outbound", - "network:serve", - "notify:send", - "ui:surface" - ], - "contributes": { - "permissions": [ - { - "name": "manage", - "titleKey": "permissions.manage.title", - "descriptionKey": "permissions.manage.description", - "defaultRoles": ["admin"] - } - ], - "settings": { - "admin": [ - { - "key": "status", - "type": "custom", - "component": "status" - }, - { - "key": "autoRenew", - "type": "boolean", - "labelKey": "settings.autoRenew.label", - "descriptionKey": "settings.autoRenew.description", - "default": false - }, - { - "key": "domain", - "type": "string", - "labelKey": "settings.domain.label", - "descriptionKey": "settings.domain.description", - "placeholderKey": "settings.domain.placeholder", - "default": "" - }, - { - "key": "email", - "type": "string", - "labelKey": "settings.email.label", - "descriptionKey": "settings.email.description", - "placeholderKey": "settings.email.placeholder", - "default": "" - }, - { - "key": "provider", - "type": "select", - "labelKey": "settings.provider.label", - "descriptionKey": "settings.provider.description", - "default": "letsencrypt", - "options": [ - { - "value": "letsencrypt", - "labelKey": "settings.provider.letsencrypt" - }, - { - "value": "letsencrypt-staging", - "labelKey": "settings.provider.letsencryptStaging" - }, - { - "value": "custom", - "labelKey": "settings.provider.custom" - } - ] - }, - { - "key": "directoryUrl", - "type": "string", - "labelKey": "settings.directoryUrl.label", - "descriptionKey": "settings.directoryUrl.description", - "default": "" - }, - { - "key": "challengeType", - "type": "select", - "labelKey": "settings.challengeType.label", - "descriptionKey": "settings.challengeType.description", - "default": "http-01", - "options": [ - { - "value": "http-01", - "labelKey": "settings.challengeType.http" - }, - { - "value": "dns-cloudflare", - "labelKey": "settings.challengeType.cloudflare" - } - ] - }, - { - "key": "cloudflareToken", - "type": "secret", - "labelKey": "settings.cloudflareToken.label", - "descriptionKey": "settings.cloudflareToken.description" - } - ] - } - }, - "locales": "locales", - "platforms": ["linux", "win32", "darwin"], - "desktop": "server" -} diff --git a/plugins/acme-ssl/package.json b/plugins/acme-ssl/package.json deleted file mode 100644 index 23dc55ca1..000000000 --- a/plugins/acme-ssl/package.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "@termix-plugin/acme-ssl", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "build": "termix-plugin build", - "test": "termix-plugin test", - "typecheck": "tsc -p .", - "validate": "termix-plugin validate", - "format": "prettier --write .", - "format:check": "prettier --check .", - "prepare": "husky || true" - }, - "dependencies": { - "acme-client": "^5.4.0" - }, - "devDependencies": { - "@commitlint/cli": "^21.2.2", - "@commitlint/config-conventional": "^21.2.0", - "@termix/plugin-sdk": "^1.0.0", - "@testing-library/dom": "^10.4.2", - "@testing-library/react": "^16.3.3", - "@types/node": "^26.2.0", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", - "husky": "^9.1.7", - "jsdom": "^30.0.1", - "lint-staged": "^17.2.0", - "prettier": "3.9.9", - "vitest": "^4.1.11" - }, - "peerDependencies": { - "@termix/plugin-sdk": "^1.0.0", - "express": "^5.2.1", - "react": "^19.2.8", - "sonner": "^2.0.8" - }, - "lint-staged": { - "*.{ts,tsx,js,jsx,mjs,cjs,json,css,md}": [ - "prettier --write" - ] - } -} diff --git a/plugins/acme-ssl/src/backend/acme.ts b/plugins/acme-ssl/src/backend/acme.ts deleted file mode 100644 index 665b5315a..000000000 --- a/plugins/acme-ssl/src/backend/acme.ts +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Issuing a certificate with acme-client. Every request it makes goes through - * ctx.fetch, so ACME traffic gets core's outbound guard and audit like any - * other plugin request. - */ - -import * as acme from "acme-client"; -import type { PluginContext, PluginFetch } from "@termix/plugin-sdk/backend"; -import { createTxtRecord } from "./cloudflare.js"; -import { directoryUrlFor, type AcmeSettings } from "./settings.js"; - -const ACCOUNT_KEY = "account"; -const DNS_PROPAGATION_MS = 30_000; - -interface StoredAccount { - directoryUrl: string; - sealedKey: string; - url: string | null; -} - -export interface IssuedCertificate { - certificate: string; - privateKey: string; -} - -type AxiosLikeConfig = { - url?: string; - method?: string; - headers?: unknown; - data?: unknown; - timeout?: number; - validateStatus?: ((status: number) => boolean) | null; -}; - -function plainHeaders(headers: unknown): Record { - const source = - headers && typeof (headers as { toJSON?: unknown }).toJSON === "function" - ? (headers as { toJSON: () => Record }).toJSON() - : ((headers ?? {}) as Record); - const out: Record = {}; - for (const [key, value] of Object.entries(source)) { - if (typeof value === "string" || typeof value === "number") { - out[key] = String(value); - } - } - return out; -} - -/** - * An axios adapter over ctx.fetch. It settles the way axios' own adapters do, - * because acme-client's retry interceptor relies on a rejected error that - * carries the response. - */ -export function createFetchAdapter( - fetch: PluginFetch, - allowPrivateHosts: readonly string[] = [], -) { - return async (config: AxiosLikeConfig) => { - const method = (config.method ?? "get").toUpperCase(); - const response = await fetch(String(config.url), { - method, - headers: plainHeaders(config.headers), - body: - config.data == null - ? undefined - : typeof config.data === "string" - ? config.data - : JSON.stringify(config.data), - timeoutMs: config.timeout || 30_000, - allowPrivateHosts, - }); - const headers: Record = {}; - response.headers.forEach((value, key) => { - headers[key] = value; - }); - const result = { - data: method === "HEAD" ? "" : await response.text(), - status: response.status, - statusText: response.statusText, - headers, - config, - request: {}, - }; - const validate = config.validateStatus; - if (!result.status || !validate || validate(result.status)) return result; - throw Object.assign( - new Error(`Request failed with status code ${result.status}`), - { - isAxiosError: true, - code: result.status >= 500 ? "ERR_BAD_RESPONSE" : "ERR_BAD_REQUEST", - config, - response: result, - }, - ); - }; -} - -/** Private hosts a custom directory on the local network may use. */ -function privateHostsFor(settings: AcmeSettings): string[] { - if (settings.provider !== "custom") return []; - try { - return [new URL(settings.directoryUrl).hostname]; - } catch { - return []; - } -} - -async function loadAccount( - ctx: PluginContext, - directoryUrl: string, -): Promise<{ key: string; url: string | null }> { - const stored = (await ctx.kv.get(ACCOUNT_KEY)) as StoredAccount | null; - if (stored && stored.directoryUrl === directoryUrl) { - const key = await ctx.secrets.unseal(stored.sealedKey); - if (key) return { key, url: stored.url }; - } - const key = (await acme.crypto.createPrivateKey()).toString(); - await ctx.kv.set(ACCOUNT_KEY, { - directoryUrl, - sealedKey: await ctx.secrets.seal(key), - url: null, - } satisfies StoredAccount); - return { key, url: null }; -} - -async function saveAccountUrl( - ctx: PluginContext, - directoryUrl: string, - url: string, -): Promise { - const stored = (await ctx.kv.get(ACCOUNT_KEY)) as StoredAccount | null; - if (!stored || stored.directoryUrl !== directoryUrl || stored.url === url) { - return; - } - await ctx.kv.set(ACCOUNT_KEY, { ...stored, url }); -} - -const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); - -/** Runs one ACME order for `settings.domain` and returns the new pair. */ -export async function issueCertificate( - ctx: PluginContext, - settings: AcmeSettings, - options: { propagationMs?: number } = {}, -): Promise { - const directoryUrl = directoryUrlFor(settings); - acme.axios.defaults.adapter = createFetchAdapter( - ctx.fetch, - privateHostsFor(settings), - ) as never; - - const account = await loadAccount(ctx, directoryUrl); - const client = new acme.Client({ - directoryUrl, - accountKey: account.key, - ...(account.url ? { accountUrl: account.url } : {}), - }); - - const [key, csr] = await acme.crypto.createCsr({ - commonName: settings.domain, - altNames: [settings.domain], - }); - - const cleanups = new Map void | Promise>(); - const certificate = await client.auto({ - csr, - email: settings.email, - termsOfServiceAgreed: true, - // Checking our own answer first needs port 80 or DNS from inside the - // container, which often is not how the CA sees it. - skipChallengeVerification: true, - challengePriority: - settings.challengeType === "dns-cloudflare" ? ["dns-01"] : ["http-01"], - challengeCreateFn: async (authz, challenge, keyAuthorization) => { - if (challenge.type === "http-01") { - cleanups.set( - challenge.token, - await ctx.system.publishHttpChallenge( - challenge.token, - keyAuthorization, - ), - ); - return; - } - if (challenge.type === "dns-01") { - cleanups.set( - challenge.token, - await createTxtRecord( - ctx.fetch, - settings.cloudflareToken, - `_acme-challenge.${authz.identifier.value}`, - keyAuthorization, - ), - ); - await sleep(options.propagationMs ?? DNS_PROPAGATION_MS); - return; - } - throw new Error( - `Unsupported challenge type ${(challenge as { type: string }).type}`, - ); - }, - challengeRemoveFn: async (_authz, challenge) => { - const cleanup = cleanups.get(challenge.token); - cleanups.delete(challenge.token); - try { - await cleanup?.(); - } catch (error) { - ctx.log.warn( - `Could not clean up the ${challenge.type} challenge: ${error instanceof Error ? error.message : String(error)}`, - ); - } - }, - }); - - await saveAccountUrl(ctx, directoryUrl, client.getAccountUrl()); - return { certificate, privateKey: key.toString() }; -} diff --git a/plugins/acme-ssl/src/backend/cloudflare.ts b/plugins/acme-ssl/src/backend/cloudflare.ts deleted file mode 100644 index fdc73b679..000000000 --- a/plugins/acme-ssl/src/backend/cloudflare.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { PluginFetch } from "@termix/plugin-sdk/backend"; - -const API = "https://api.cloudflare.com/client/v4"; - -interface CloudflareEnvelope { - success: boolean; - errors?: Array<{ message?: string }>; - result: T; -} - -async function call( - fetch: PluginFetch, - token: string, - path: string, - init: { method?: string; body?: unknown } = {}, -): Promise { - const response = await fetch(`${API}${path}`, { - method: init.method ?? "GET", - headers: { - Authorization: `Bearer ${token}`, - "Content-Type": "application/json", - }, - body: init.body === undefined ? undefined : JSON.stringify(init.body), - }); - const payload = (await response - .json() - .catch(() => null)) as CloudflareEnvelope | null; - if (!response.ok || !payload?.success) { - const reason = - payload?.errors - ?.map((error) => error.message) - .filter(Boolean) - .join("; ") || `HTTP ${response.status}`; - throw new Error(`Cloudflare API error: ${reason}`); - } - return payload.result; -} - -/** The zone holding `name`, found by trying each parent domain. */ -export async function findZoneId( - fetch: PluginFetch, - token: string, - name: string, -): Promise { - const labels = name.replace(/\.$/, "").split("."); - for (let i = 0; i < labels.length - 1; i++) { - const candidate = labels.slice(i).join("."); - const zones = await call>( - fetch, - token, - `/zones?name=${encodeURIComponent(candidate)}`, - ); - const zone = zones.find((entry) => entry.name === candidate); - if (zone) return zone.id; - } - throw new Error(`No Cloudflare zone found for ${name}`); -} - -/** Creates the TXT record and returns a function that deletes it. */ -export async function createTxtRecord( - fetch: PluginFetch, - token: string, - name: string, - content: string, -): Promise<() => Promise> { - const zoneId = await findZoneId(fetch, token, name); - const record = await call<{ id: string }>( - fetch, - token, - `/zones/${zoneId}/dns_records`, - { method: "POST", body: { type: "TXT", name, content, ttl: 120 } }, - ); - return async () => { - await call(fetch, token, `/zones/${zoneId}/dns_records/${record.id}`, { - method: "DELETE", - }); - }; -} diff --git a/plugins/acme-ssl/src/backend/index.ts b/plugins/acme-ssl/src/backend/index.ts deleted file mode 100644 index 7f8507374..000000000 --- a/plugins/acme-ssl/src/backend/index.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { Router } from "express"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { issueCertificate } from "./acme.js"; -import { - CHECK_INTERVAL_MS, - createAcmeRunner, - watchSettings, - type IssueFn, -} from "./runner.js"; -import { registerRoutes } from "./routes.js"; - -/** `issue` is swapped out by tests; the plugin always uses acme-client. */ -export async function activateWith(ctx: PluginContext, issue: IssueFn) { - const runner = createAcmeRunner(ctx, issue); - - await runner.syncRenewer(); - watchSettings(ctx, () => { - runner - .syncRenewer() - .catch((error) => - ctx.log.warn( - `Could not update the renewal registration: ${error instanceof Error ? error.message : String(error)}`, - ), - ); - }); - - // The first check lands within a minute of boot, after core's boot-time - // settings migrations, so it also picks up settings moved from 2.8. - ctx.schedule.every( - CHECK_INTERVAL_MS, - async () => { - await runner.syncRenewer(); - await runner.check(); - }, - { jitterMs: 60_000 }, - ); - - registerRoutes(ctx, ctx.http.router(), runner); - return runner; -} - -export async function activate(ctx: PluginContext) { - await activateWith(ctx, (pluginCtx, settings) => - issueCertificate(pluginCtx, settings), - ); -} - -export async function deactivate() { - // Everything above was registered through ctx and is disposed by core. -} diff --git a/plugins/acme-ssl/src/backend/renewal.ts b/plugins/acme-ssl/src/backend/renewal.ts deleted file mode 100644 index 08cb3a187..000000000 --- a/plugins/acme-ssl/src/backend/renewal.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { PluginTlsStatus } from "@termix/plugin-sdk/backend"; - -export const RENEW_BEFORE_MS = 30 * 86_400_000; - -export type RenewalReason = "missing" | "self-signed" | "domain" | "expiring"; - -/** - * Whether the served certificate should be replaced for `domain`: there is - * none, it is core's self-signed one, it does not cover the domain, or it - * expires within 30 days. - */ -export function renewalReason( - status: PluginTlsStatus, - domain: string, - now: Date = new Date(), -): RenewalReason | null { - const cert = status.certificate; - if (!cert) return "missing"; - if (cert.selfSigned) return "self-signed"; - const wanted = domain.toLowerCase(); - if (!cert.names.some((name) => name.toLowerCase() === wanted)) { - return "domain"; - } - if (new Date(cert.notAfter).getTime() - now.getTime() < RENEW_BEFORE_MS) { - return "expiring"; - } - return null; -} diff --git a/plugins/acme-ssl/src/backend/routes.ts b/plugins/acme-ssl/src/backend/routes.ts deleted file mode 100644 index 1b624a822..000000000 --- a/plugins/acme-ssl/src/backend/routes.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { Request, RequestHandler, Response, Router } from "express"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { AcmeNotConfiguredError, type AcmeRunner } from "./runner.js"; -import { missingSetting, readSettings } from "./settings.js"; - -export function registerRoutes( - ctx: PluginContext, - router: Router, - runner: AcmeRunner, -): void { - router.use(ctx.rbac.require("manage") as unknown as RequestHandler); - - /** - * @openapi - * /plugin-api/acme-ssl/status: - * get: - * summary: ACME certificate status - * description: The certificate core serves, whether ACME is configured, and the last issue attempt. - * tags: [ACME Certificates] - * responses: - * 200: - * description: Status. - * 403: - * description: Missing the acme-ssl.manage permission. - */ - router.get("/status", async (_req: Request, res: Response) => { - const settings = await readSettings(ctx); - res.json({ - tls: await ctx.system.tlsStatus(), - state: await runner.state(), - autoRenew: settings.autoRenew, - missing: missingSetting(settings), - }); - }); - - /** - * @openapi - * /plugin-api/acme-ssl/request: - * post: - * summary: Request a certificate now - * description: Runs an ACME order for the configured domain, installs the certificate and reloads HTTPS. - * tags: [ACME Certificates] - * responses: - * 200: - * description: Certificate issued and installed. - * 400: - * description: A required setting is missing. - * 403: - * description: Missing the acme-ssl.manage permission. - * 500: - * description: The ACME order failed. - */ - router.post("/request", async (_req: Request, res: Response) => { - try { - const result = await runner.issue(); - res.json({ - success: true, - reloadMessage: result.reload.message, - tls: result.tls, - state: await runner.state(), - }); - } catch (error) { - if (error instanceof AcmeNotConfiguredError) { - return res - .status(400) - .json({ error: error.message, missing: error.field }); - } - const message = error instanceof Error ? error.message : String(error); - ctx.log.warn(`Certificate request failed: ${message}`); - res.status(500).json({ error: `Certificate request failed: ${message}` }); - } - }); -} diff --git a/plugins/acme-ssl/src/backend/runner.ts b/plugins/acme-ssl/src/backend/runner.ts deleted file mode 100644 index 9c05180d2..000000000 --- a/plugins/acme-ssl/src/backend/runner.ts +++ /dev/null @@ -1,173 +0,0 @@ -import type { - PluginContext, - PluginTlsReloadResult, - PluginTlsStatus, -} from "@termix/plugin-sdk/backend"; -import type { IssuedCertificate } from "./acme.js"; -import { renewalReason } from "./renewal.js"; -import { - SETTING_KEYS, - missingSetting, - readSettings, - type AcmeSettings, -} from "./settings.js"; - -export const CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000; -/** After a failed attempt, the scheduled check waits this long to retry. */ -export const RETRY_AFTER_MS = 6 * 60 * 60 * 1000; - -const STATE_KEY = "state"; - -export interface AcmeState { - lastAttemptAt: string | null; - lastIssuedAt: string | null; - lastError: string | null; -} - -export type IssueFn = ( - ctx: PluginContext, - settings: AcmeSettings, -) => Promise; - -export class AcmeNotConfiguredError extends Error { - constructor(readonly field: string) { - super(`ACME is not configured: ${field} is missing`); - } -} - -export interface IssueResult { - tls: PluginTlsStatus; - reload: PluginTlsReloadResult; -} - -export interface AcmeRunner { - state: () => Promise; - /** Issues now, whatever the served certificate looks like. */ - issue: () => Promise; - /** The scheduled check: issues only when renewal is on and due. */ - check: (now?: Date) => Promise; - /** Registers or drops the renewer to match the current settings. */ - syncRenewer: () => Promise; -} - -export function createAcmeRunner( - ctx: PluginContext, - issue: IssueFn, -): AcmeRunner { - let running: Promise | null = null; - let dropRenewer: (() => void) | null = null; - - const state = async (): Promise => { - const stored = (await ctx.kv.get(STATE_KEY)) as Partial | null; - return { - lastAttemptAt: stored?.lastAttemptAt ?? null, - lastIssuedAt: stored?.lastIssuedAt ?? null, - lastError: stored?.lastError ?? null, - }; - }; - - const record = async (patch: Partial) => { - await ctx.kv.set(STATE_KEY, { ...(await state()), ...patch }); - }; - - const runIssue = async (): Promise => { - const settings = await readSettings(ctx); - const missing = missingSetting(settings); - if (missing) throw new AcmeNotConfiguredError(missing); - - const attemptAt = new Date().toISOString(); - try { - const issued = await issue(ctx, settings); - await ctx.system.writeTlsCertificate( - issued.certificate, - issued.privateKey, - ); - const reload = await ctx.system.reloadTls(); - await record({ - lastAttemptAt: attemptAt, - lastIssuedAt: new Date().toISOString(), - lastError: null, - }); - ctx.log.info(`Certificate issued for ${settings.domain}`); - return { tls: await ctx.system.tlsStatus(), reload }; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - await record({ lastAttemptAt: attemptAt, lastError: message }); - throw error; - } - }; - - const runner: AcmeRunner = { - state, - - issue: () => { - running ??= runIssue().finally(() => { - running = null; - }); - return running; - }, - - check: async (now = new Date()) => { - const settings = await readSettings(ctx); - if (!settings.autoRenew || missingSetting(settings)) return false; - - const last = await state(); - if ( - last.lastError && - last.lastAttemptAt && - now.getTime() - new Date(last.lastAttemptAt).getTime() < RETRY_AFTER_MS - ) { - return false; - } - - const reason = renewalReason( - await ctx.system.tlsStatus(), - settings.domain, - now, - ); - if (!reason) return false; - - ctx.log.info( - `Renewing the certificate for ${settings.domain} (${reason})`, - ); - try { - await runner.issue(); - return true; - } catch (error) { - const message = error instanceof Error ? error.message : String(error); - ctx.log.error( - "Certificate renewal failed", - error instanceof Error ? error : new Error(message), - ); - await ctx.notify - .send({ - title: `Certificate renewal failed for ${settings.domain}`, - body: message, - severity: "critical", - category: "acme-ssl.renewal_failed", - audience: "admins", - dedupeKey: `acme-ssl.renewal_failed:${settings.domain}`, - }) - .catch(() => {}); - return false; - } - }, - - syncRenewer: async () => { - const settings = await readSettings(ctx); - const wanted = settings.autoRenew && !missingSetting(settings); - if (wanted && !dropRenewer) { - dropRenewer = await ctx.system.registerTlsRenewer(); - } else if (!wanted && dropRenewer) { - dropRenewer(); - dropRenewer = null; - } - }, - }; - - return runner; -} - -export function watchSettings(ctx: PluginContext, onChange: () => void): void { - for (const key of SETTING_KEYS) ctx.settings.onChange(key, onChange); -} diff --git a/plugins/acme-ssl/src/backend/settings.ts b/plugins/acme-ssl/src/backend/settings.ts deleted file mode 100644 index e71a01b4d..000000000 --- a/plugins/acme-ssl/src/backend/settings.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { PluginContext } from "@termix/plugin-sdk/backend"; - -export type AcmeProvider = "letsencrypt" | "letsencrypt-staging" | "custom"; -export type AcmeChallengeType = "http-01" | "dns-cloudflare"; - -export interface AcmeSettings { - autoRenew: boolean; - domain: string; - email: string; - provider: AcmeProvider; - directoryUrl: string; - challengeType: AcmeChallengeType; - cloudflareToken: string; -} - -export const SETTING_KEYS = [ - "autoRenew", - "domain", - "email", - "provider", - "directoryUrl", - "challengeType", - "cloudflareToken", -] as const; - -export const DIRECTORIES: Record, string> = { - letsencrypt: "https://acme-v02.api.letsencrypt.org/directory", - "letsencrypt-staging": - "https://acme-staging-v02.api.letsencrypt.org/directory", -}; - -const text = (value: unknown) => - typeof value === "string" ? value.trim() : ""; - -export async function readSettings(ctx: PluginContext): Promise { - const all = await ctx.settings.getAll("admin"); - const provider = text(all.provider); - const challengeType = text(all.challengeType); - return { - autoRenew: all.autoRenew === true, - domain: text(all.domain).toLowerCase(), - email: text(all.email), - provider: - provider === "letsencrypt-staging" || provider === "custom" - ? provider - : "letsencrypt", - directoryUrl: text(all.directoryUrl), - challengeType: - challengeType === "dns-cloudflare" ? challengeType : "http-01", - cloudflareToken: text(all.cloudflareToken), - }; -} - -export function directoryUrlFor(settings: AcmeSettings): string { - return settings.provider === "custom" - ? settings.directoryUrl - : DIRECTORIES[settings.provider]; -} - -/** The reason issuing cannot start, or null when everything is set. */ -export function missingSetting(settings: AcmeSettings): string | null { - if (!settings.domain) return "domain"; - if (!settings.email) return "email"; - if ( - settings.provider === "custom" && - !/^https:\/\//i.test(settings.directoryUrl) - ) { - return "directoryUrl"; - } - if ( - settings.challengeType === "dns-cloudflare" && - !settings.cloudflareToken - ) { - return "cloudflareToken"; - } - return null; -} diff --git a/plugins/acme-ssl/src/frontend/AcmeStatusSetting.tsx b/plugins/acme-ssl/src/frontend/AcmeStatusSetting.tsx deleted file mode 100644 index 1999b7798..000000000 --- a/plugins/acme-ssl/src/frontend/AcmeStatusSetting.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { useCallback, useEffect, useState } from "react"; -import { toast } from "sonner"; -import { - usePluginApi, - useTranslation, - type SettingsComponentProps, -} from "@termix/plugin-sdk/frontend"; -import { Button } from "@termix/plugin-sdk/ui"; - -export interface AcmeStatus { - tls: { - enabled: boolean; - certificate: { - issuer: string; - names: string[]; - notAfter: string; - selfSigned: boolean; - } | null; - }; - state: { - lastAttemptAt: string | null; - lastIssuedAt: string | null; - lastError: string | null; - }; - autoRenew: boolean; - missing: string | null; -} - -function errorMessage(error: unknown): string { - const data = (error as { response?: { data?: { error?: string } } })?.response - ?.data; - if (data?.error) return data.error; - return error instanceof Error ? error.message : String(error); -} - -export function AcmeStatusSetting({ running }: SettingsComponentProps) { - const { t } = useTranslation(); - const api = usePluginApi(); - const [status, setStatus] = useState(null); - const [requesting, setRequesting] = useState(false); - - const load = useCallback(async () => { - try { - const { data } = await api.get("/status"); - setStatus(data); - } catch { - setStatus(null); - } - }, [api]); - - useEffect(() => { - if (running) void load(); - }, [running, load]); - - async function request() { - setRequesting(true); - try { - const { data } = await api.post<{ reloadMessage?: string }>("/request"); - toast.success(t("status.issued")); - if (data.reloadMessage) toast.info(data.reloadMessage); - } catch (error) { - toast.error(errorMessage(error)); - } finally { - setRequesting(false); - await load(); - } - } - - const cert = status?.tls.certificate; - - return ( -
- {t("status.title")} - {cert ? ( - <> - - {t("status.names", { names: cert.names.join(", ") || "-" })} - - - {t("status.issuer", { issuer: cert.issuer })} - - - {t("status.expires", { - date: new Date(cert.notAfter).toLocaleDateString(), - })} - - - ) : ( - - {t("status.none")} - - )} - {status?.state.lastIssuedAt && ( - - {t("status.lastIssued", { - date: new Date(status.state.lastIssuedAt).toLocaleString(), - })} - - )} - {status?.state.lastError && ( - - {t("status.lastError", { error: status.state.lastError })} - - )} - {status?.missing && ( - - {t("status.missing")} - - )} - -
- ); -} diff --git a/plugins/acme-ssl/src/frontend/index.tsx b/plugins/acme-ssl/src/frontend/index.tsx deleted file mode 100644 index 7c4d05f82..000000000 --- a/plugins/acme-ssl/src/frontend/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { TermixApp } from "@termix/plugin-sdk/frontend"; -import { AcmeStatusSetting } from "./AcmeStatusSetting"; - -export function activate(app: TermixApp): void { - app.registerSettingsComponent("status", AcmeStatusSetting); -} - -export function deactivate(): void { - // Registrations through app are disposed by core. -} diff --git a/plugins/acme-ssl/tests/backend/acme-ssl.test.ts b/plugins/acme-ssl/tests/backend/acme-ssl.test.ts deleted file mode 100644 index f2bf76f9e..000000000 --- a/plugins/acme-ssl/tests/backend/acme-ssl.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -import http from "node:http"; -import type { AddressInfo } from "node:net"; -import express, { type Router } from "express"; -import { afterEach, describe, expect, it, vi } from "vitest"; -import { - createMockCtx, - type MockPluginContext, -} from "@termix/plugin-sdk/testing"; -import type { - PluginCapabilityError as CapabilityError, - PluginTlsCertificateInfo, - PluginTlsStatus, -} from "@termix/plugin-sdk/backend"; -import type { PluginManifest } from "@termix/plugin-sdk/manifest"; -import manifestJson from "../../manifest.json"; -import { activateWith } from "../../src/backend/index.js"; -import { CHECK_INTERVAL_MS, RETRY_AFTER_MS } from "../../src/backend/runner.js"; -import { renewalReason } from "../../src/backend/renewal.js"; - -const manifest = manifestJson as unknown as PluginManifest; -const DAY = 86_400_000; - -const CONFIGURED = { - autoRenew: true, - domain: "termix.example.com", - email: "admin@example.com", -}; - -function cert( - overrides: Partial = {}, -): PluginTlsCertificateInfo { - return { - subject: "CN=termix.example.com", - issuer: "CN=R11, O=Let's Encrypt", - names: ["termix.example.com"], - notBefore: new Date(Date.now() - 10 * DAY).toISOString(), - notAfter: new Date(Date.now() + 60 * DAY).toISOString(), - selfSigned: false, - fingerprint: "AA", - ...overrides, - }; -} - -const issue = vi.fn(async () => ({ - certificate: "-----BEGIN CERTIFICATE-----new", - privateKey: "-----BEGIN PRIVATE KEY-----new", -})); - -let servers: http.Server[] = []; - -afterEach(async () => { - issue.mockClear(); - await Promise.all( - servers.map( - (s) => new Promise((resolve) => s.close(() => resolve())), - ), - ); - servers = []; -}); - -async function setup( - options: { - settings?: Record; - certificate?: PluginTlsCertificateInfo | null; - permissions?: string[]; - capabilities?: string[]; - validateTls?: () => PluginTlsCertificateInfo; - } = {}, -) { - let router: Router | null = null; - const tlsStatus: Omit = { - enabled: true, - certificate: options.certificate === undefined ? null : options.certificate, - }; - const mock: MockPluginContext = createMockCtx({ - pluginId: manifest.id, - manifest, - capabilities: options.capabilities ?? manifest.capabilities, - router: () => (router = express.Router()), - settings: options.settings, - permissions: options.permissions ?? ["manage"], - tlsStatus, - validateTls: options.validateTls, - }); - const runner = await activateWith(mock.ctx, issue); - - const app = express(); - app.use((req, res, next) => router!(req, res, next)); - const server = http.createServer(app); - servers.push(server); - await new Promise((resolve) => server.listen(0, resolve)); - const { port } = server.address() as AddressInfo; - const call = async (method: string, path: string) => { - const response = await fetch(`http://127.0.0.1:${port}${path}`, { - method, - }); - return { status: response.status, body: await response.json() }; - }; - return { mock, runner, call }; -} - -describe("renewalReason", () => { - const status = (certificate: PluginTlsCertificateInfo | null) => ({ - enabled: true, - certificate, - renewal: null, - }); - - it("renews when missing, self-signed, for another domain or expiring", () => { - expect(renewalReason(status(null), "termix.example.com")).toBe("missing"); - expect( - renewalReason(status(cert({ selfSigned: true })), "termix.example.com"), - ).toBe("self-signed"); - expect(renewalReason(status(cert()), "other.example.com")).toBe("domain"); - expect( - renewalReason( - status( - cert({ notAfter: new Date(Date.now() + 10 * DAY).toISOString() }), - ), - "termix.example.com", - ), - ).toBe("expiring"); - expect(renewalReason(status(cert()), "TERMIX.example.com")).toBeNull(); - }); -}); - -describe("scheduled renewal", () => { - it("checks on a 12 hour timer and renews an expiring certificate", async () => { - const { mock } = await setup({ - settings: CONFIGURED, - certificate: cert({ - notAfter: new Date(Date.now() + 5 * DAY).toISOString(), - }), - }); - const job = mock.scheduled.find((entry) => entry.kind === "every"); - expect(job?.ms).toBe(CHECK_INTERVAL_MS); - - await mock.runScheduled(); - - expect(issue).toHaveBeenCalledOnce(); - expect(mock.tls.writes).toEqual([ - { - certificatePem: "-----BEGIN CERTIFICATE-----new", - privateKeyPem: "-----BEGIN PRIVATE KEY-----new", - }, - ]); - const write = mock.tls.calls.indexOf("writeTlsCertificate"); - expect(mock.tls.calls.indexOf("reloadTls")).toBeGreaterThan(write); - expect(mock.tls.reloads).toBe(1); - }); - - it("leaves a valid certificate alone", async () => { - const { mock } = await setup({ settings: CONFIGURED, certificate: cert() }); - await mock.runScheduled(); - expect(issue).not.toHaveBeenCalled(); - expect(mock.tls.writes).toEqual([]); - }); - - it("does nothing and registers no renewer while renewal is off", async () => { - const { mock } = await setup({ - settings: { ...CONFIGURED, autoRenew: false }, - }); - await mock.runScheduled(); - expect(issue).not.toHaveBeenCalled(); - expect(mock.tls.renewers).toBe(0); - }); - - it("registers as the renewer when configured and drops it when turned off", async () => { - const { mock } = await setup({ settings: CONFIGURED, certificate: cert() }); - expect(mock.tls.renewers).toBe(1); - expect((await mock.ctx.system.tlsStatus()).renewal?.pluginId).toBe( - "acme-ssl", - ); - - await mock.ctx.settings.set("autoRenew", false); - await vi.waitFor(() => expect(mock.tls.renewers).toBe(0)); - }); - - it("alerts every admin when a renewal fails", async () => { - const { mock, runner } = await setup({ settings: CONFIGURED }); - issue.mockRejectedValueOnce(new Error("rate limited")); - expect(await runner.check()).toBe(false); - expect(mock.notifications).toEqual([ - { - actor: undefined, - notification: expect.objectContaining({ - body: "rate limited", - severity: "critical", - category: "acme-ssl.renewal_failed", - audience: "admins", - }), - }, - ]); - }); - - it("waits before retrying after a failed attempt", async () => { - const { mock, runner } = await setup({ settings: CONFIGURED }); - issue.mockRejectedValueOnce(new Error("rate limited")); - expect(await runner.check()).toBe(false); - expect((await runner.state()).lastError).toBe("rate limited"); - - expect(await runner.check()).toBe(false); - expect(issue).toHaveBeenCalledOnce(); - - expect( - await runner.check(new Date(Date.now() + RETRY_AFTER_MS + 1000)), - ).toBe(true); - expect(issue).toHaveBeenCalledTimes(2); - expect(mock.tls.reloads).toBe(1); - }); - - it("does not reload when core rejects the certificate", async () => { - const { mock, runner } = await setup({ - settings: CONFIGURED, - validateTls: () => { - throw new Error("The certificate and private key do not match"); - }, - }); - await expect(runner.issue()).rejects.toThrow(/do not match/); - expect(mock.tls.reloads).toBe(0); - expect((await runner.state()).lastError).toMatch(/do not match/); - }); -}); - -describe("routes", () => { - it("reports status and issues on request", async () => { - const { call, mock } = await setup({ settings: CONFIGURED }); - const status = await call("GET", "/status"); - expect(status.status).toBe(200); - expect(status.body.missing).toBeNull(); - expect(status.body.tls.certificate).toBeNull(); - - const issued = await call("POST", "/request"); - expect(issued.status).toBe(200); - expect(issued.body.success).toBe(true); - expect(issued.body.tls.certificate).not.toBeNull(); - expect(mock.tls.reloads).toBe(1); - }); - - it("answers 400 when a setting is missing", async () => { - const { call } = await setup({ settings: { domain: "a.example" } }); - const result = await call("POST", "/request"); - expect(result.status).toBe(400); - expect(result.body.missing).toBe("email"); - expect(issue).not.toHaveBeenCalled(); - }); - - it("refuses a user without acme-ssl.manage on every route", async () => { - const { call } = await setup({ settings: CONFIGURED, permissions: [] }); - expect((await call("GET", "/status")).status).toBe(403); - expect((await call("POST", "/request")).status).toBe(403); - expect(issue).not.toHaveBeenCalled(); - }); -}); - -describe("capabilities", () => { - it("fails closed without system:tls", async () => { - await expect( - setup({ - settings: CONFIGURED, - capabilities: manifest.capabilities.filter( - (capability) => capability !== "system:tls", - ), - }), - ).rejects.toMatchObject({ - capability: "system:tls", - } satisfies Partial); - }); -}); diff --git a/plugins/acme-ssl/tests/backend/transport.test.ts b/plugins/acme-ssl/tests/backend/transport.test.ts deleted file mode 100644 index edcbe9623..000000000 --- a/plugins/acme-ssl/tests/backend/transport.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import type { PluginFetchInit } from "@termix/plugin-sdk/backend"; -import { createFetchAdapter } from "../../src/backend/acme.js"; -import { createTxtRecord } from "../../src/backend/cloudflare.js"; - -describe("createFetchAdapter", () => { - it("sends acme-client requests through ctx.fetch", async () => { - const fetch = vi.fn( - async (_url: string, _init?: PluginFetchInit) => - new Response('{"ok":true}', { - status: 201, - headers: { "replay-nonce": "n1", location: "https://ca/acct/1" }, - }), - ); - const adapter = createFetchAdapter(fetch, ["ca.internal"]); - const result = await adapter({ - url: "https://ca.internal/new-acct", - method: "post", - headers: { "Content-Type": "application/jose+json" }, - data: '{"payload":"x"}', - validateStatus: null, - }); - - expect(fetch).toHaveBeenCalledWith( - "https://ca.internal/new-acct", - expect.objectContaining({ - method: "POST", - body: '{"payload":"x"}', - allowPrivateHosts: ["ca.internal"], - headers: { "Content-Type": "application/jose+json" }, - }), - ); - expect(result.status).toBe(201); - expect(result.data).toBe('{"ok":true}'); - expect(result.headers["replay-nonce"]).toBe("n1"); - }); - - it("rejects with the response attached when validateStatus says no", async () => { - const adapter = createFetchAdapter( - async () => new Response("slow down", { status: 429 }), - ); - await expect( - adapter({ url: "https://ca/x", validateStatus: () => false }), - ).rejects.toMatchObject({ - code: "ERR_BAD_REQUEST", - response: { status: 429, data: "slow down" }, - }); - }); -}); - -describe("createTxtRecord", () => { - it("finds the zone by parent domain, creates the record and deletes it", async () => { - const calls: Array<{ url: string; method: string; body?: string }> = []; - const fetch = vi.fn(async (url: string, init?: PluginFetchInit) => { - calls.push({ url, method: init?.method ?? "GET", body: init?.body }); - if (url.includes("/zones?name=example.com")) { - return Response.json({ - success: true, - result: [{ id: "zone-1", name: "example.com" }], - }); - } - if (url.includes("/zones?name=")) { - return Response.json({ success: true, result: [] }); - } - if (init?.method === "POST") { - return Response.json({ success: true, result: { id: "rec-1" } }); - } - return Response.json({ success: true, result: {} }); - }); - - const remove = await createTxtRecord( - fetch, - "token", - "_acme-challenge.termix.example.com", - "digest", - ); - const post = calls.find((call) => call.method === "POST")!; - expect(post.url).toContain("/zones/zone-1/dns_records"); - expect(JSON.parse(post.body!)).toMatchObject({ - type: "TXT", - name: "_acme-challenge.termix.example.com", - content: "digest", - }); - - await remove(); - expect(calls.at(-1)).toMatchObject({ - method: "DELETE", - url: expect.stringContaining("/zones/zone-1/dns_records/rec-1"), - }); - }); - - it("surfaces the Cloudflare error message", async () => { - const fetch = async () => - Response.json( - { success: false, errors: [{ message: "Invalid token" }] }, - { status: 403 }, - ); - await expect( - createTxtRecord(fetch, "bad", "_acme-challenge.a.example", "d"), - ).rejects.toThrow(/Invalid token/); - }); -}); diff --git a/plugins/acme-ssl/tests/frontend/activate.test.tsx b/plugins/acme-ssl/tests/frontend/activate.test.tsx deleted file mode 100644 index 1c71c3c34..000000000 --- a/plugins/acme-ssl/tests/frontend/activate.test.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; -import { render, screen, waitFor } from "@testing-library/react"; -import { - renderWithApp, - type RenderedPluginApp, -} from "@termix/plugin-sdk/testing"; -import type { PluginManifest } from "@termix/plugin-sdk/manifest"; -import * as plugin from "../../src/frontend/index"; -import manifestJson from "../../manifest.json"; -import locales from "../../locales/en.json"; - -const api = vi.hoisted(() => ({ - get: vi.fn(), - post: vi.fn(), -})); - -vi.mock("@termix/plugin-sdk/frontend", async (importOriginal) => ({ - ...(await importOriginal()), - useTranslation: () => ({ - t: (key: string, values?: Record) => - values ? `${key} ${Object.values(values).join(" ")}` : key, - }), - usePluginApi: () => api, -})); - -const { AcmeStatusSetting } = - await import("../../src/frontend/AcmeStatusSetting"); - -const manifest = manifestJson as unknown as PluginManifest; - -let rendered: RenderedPluginApp | null = null; - -afterEach(async () => { - await rendered?.deactivate(); - rendered = null; - api.get.mockReset(); -}); - -describe("acme-ssl activate", () => { - it("registers the status settings component and removes it on deactivate", async () => { - rendered = await renderWithApp(plugin, { manifest, locales }); - expect(rendered.registered.settingsComponents()).toEqual(["status"]); - await rendered.deactivate(); - expect(rendered.registered.settingsComponents()).toEqual([]); - rendered = null; - }); -}); - -describe("AcmeStatusSetting", () => { - it("shows the served certificate and the last error", async () => { - api.get.mockResolvedValue({ - data: { - tls: { - enabled: true, - certificate: { - issuer: "CN=R11", - names: ["termix.example.com"], - notAfter: "2030-01-01T00:00:00.000Z", - selfSigned: false, - }, - }, - state: { - lastAttemptAt: null, - lastIssuedAt: null, - lastError: "rate limited", - }, - autoRenew: true, - missing: null, - }, - }); - render( - {}} - running - />, - ); - await waitFor(() => - expect(screen.getByText("status.names termix.example.com")).toBeTruthy(), - ); - expect(screen.getByText("status.lastError rate limited")).toBeTruthy(); - expect(api.get).toHaveBeenCalledWith("/status"); - }); -}); diff --git a/plugins/acme-ssl/tsconfig.json b/plugins/acme-ssl/tsconfig.json deleted file mode 100644 index 7b35314c3..000000000 --- a/plugins/acme-ssl/tsconfig.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "@termix/plugin-sdk/tsconfig.plugin.json", - "include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts", "tests/**/*.tsx"] -} diff --git a/plugins/acme-ssl/vitest.config.ts b/plugins/acme-ssl/vitest.config.ts deleted file mode 100644 index 31ba004fc..000000000 --- a/plugins/acme-ssl/vitest.config.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { pluginVitestConfig } from "@termix/plugin-sdk/vitest-preset"; - -export default pluginVitestConfig(import.meta.url); diff --git a/plugins/ai/.commitlintrc.json b/plugins/ai/.commitlintrc.json deleted file mode 100644 index cba65ea51..000000000 --- a/plugins/ai/.commitlintrc.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extends": ["@commitlint/config-conventional"], - "rules": { - "type-enum": [ - 2, - "always", - [ - "feat", - "fix", - "docs", - "style", - "refactor", - "perf", - "test", - "chore", - "revert" - ] - ], - "subject-case": [0] - } -} diff --git a/plugins/ai/.github/funding.yml b/plugins/ai/.github/funding.yml deleted file mode 100644 index c14d56027..000000000 --- a/plugins/ai/.github/funding.yml +++ /dev/null @@ -1 +0,0 @@ -custom: https://donate.termix.site/ diff --git a/plugins/ai/.github/pull_request_template.md b/plugins/ai/.github/pull_request_template.md deleted file mode 100644 index 9482e0908..000000000 --- a/plugins/ai/.github/pull_request_template.md +++ /dev/null @@ -1,34 +0,0 @@ -# Overview - -_Short summary of what this PR does_ - -- [ ] Added: ... -- [ ] Updated: ... -- [ ] Removed: ... -- [ ] Fixed: ... - -# Changes Made - -_Detailed explanation of changes (if needed)_ - -- ... - -# Related Issues - -_Link any issues this PR addresses_ - -- Closes #ISSUE_NUMBER -- Related to #ISSUE_NUMBER - -# Screenshots / Demos - -_(Optional: add before/after screenshots, GIFs, or console output)_ - -# Checklist - -- [ ] Code follows project style guidelines -- [ ] Supports mobile and desktop UI/app (if applicable) -- [ ] Added the change to `CHANGELOG.json` -- [ ] `npm run test`, `npm run typecheck` and `npm run validate` pass -- [ ] I have read `CONTRIBUTING.md` -- [ ] This is not a translation request. See [docs](https://docs.termix.site/translations) diff --git a/plugins/ai/.gitignore b/plugins/ai/.gitignore deleted file mode 100644 index e78aa8134..000000000 --- a/plugins/ai/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -node_modules -dist -coverage -*.log -.env -*.tmxplug -*.tmxplug.sig -termix-plugin-signing.key -.termix-tables-* diff --git a/plugins/ai/.husky/commit-msg b/plugins/ai/.husky/commit-msg deleted file mode 100644 index da9948310..000000000 --- a/plugins/ai/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -npx --no -- commitlint --edit "$1" diff --git a/plugins/ai/.husky/pre-commit b/plugins/ai/.husky/pre-commit deleted file mode 100644 index 2312dc587..000000000 --- a/plugins/ai/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -npx lint-staged diff --git a/plugins/ai/.prettierignore b/plugins/ai/.prettierignore deleted file mode 100644 index 012fbaa45..000000000 --- a/plugins/ai/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -coverage -node_modules -package-lock.json diff --git a/plugins/ai/.prettierrc b/plugins/ai/.prettierrc deleted file mode 100644 index fd873cbb3..000000000 --- a/plugins/ai/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "singleQuote": false, - "tabWidth": 2, - "trailingComma": "all", - "printWidth": 80, - "arrowParens": "always", - "endOfLine": "lf" -} diff --git a/plugins/ai/AGENTS-FEATURE.md b/plugins/ai/AGENTS-FEATURE.md deleted file mode 100644 index 9b3c4434b..000000000 --- a/plugins/ai/AGENTS-FEATURE.md +++ /dev/null @@ -1,47 +0,0 @@ -# Remote AI agents - -The **AI Agent** host action appears on the host card and in the SSH terminal toolbar. It opens a host-scoped session tab. Enable AI globally, opt in for the user, and grant `ai.agents` (administrators by default). - -Choose Pi, OpenCode, Claude Code, or Codex; an existing Termix AI provider; a model; and an absolute working directory. The target needs POSIX SSH, Node.js 22.19+, the selected CLI on its login-shell PATH (or an explicit executable path), and SSH remote forwarding to `127.0.0.1`. Missing dependencies fail visibly. The explicit Install button installs the selected CLI and an isolated Node.js 24 LTS runtime; starting an agent never installs software automatically. - -Termix owns the SSH connection, session history, streaming UI, cancellation, and permission responses. The remote runner uses the CLI's native protocol: Pi RPC, Claude streaming JSON, Codex app-server, or OpenCode's loopback HTTP API. No Paseo runtime or daemon is required. - -Model requests travel over an SSH reverse tunnel to a session-scoped proxy. The proxy authenticates a random token, checks host access and AI permissions, restricts inference paths and the selected model, and uses the current user's saved Provider key and the existing AI private-endpoint allowlist. Provider keys never go to the browser or target. OpenAI/compatible endpoints support Pi and OpenCode; Claude needs Anthropic; Codex needs **Responses**, not just Chat Completions. Selecting an OpenAI-compatible record does not prove Responses support. - -Agent state lives in the target user's `~/.local/state/termix-agents/` and the AI plugin's own tables (`agent_sessions`, with events and queued prompts as separate rows). Prompts, drafts and stored event text are limited to 65,535 UTF-8 bytes, the MySQL TEXT limit. Closing a tab leaves the session running; stop ends it. A server restart stops active transports; Resume reopens the native session where supported. Sessions expire after eight hours. Up to four sessions may run per user. The UI retains the most recent 2,000 events; native history remains on the target. Deleting a Termix session does not delete its target-side directory. - -The agent runs with the SSH user's authority. Claude/OpenCode/Codex approval requests are shown in the tab; Pi's ordinary tools run without per-tool approval. Pi extension prompts are forwarded. Structured native questions currently accept JSON answers. Root SSH gives the agent root privileges; select the account and directory accordingly. No global agent config is overwritten. - -The initial host action asks for the working directory explicitly; it does not infer one from terminal buffer text. Mobile-native UI and cross-host session migration are outside this release. - -## Validation - -`tests/backend/agents/agents.test.ts` covers target/command validation, inference-path restrictions, protocol compatibility, and four native-protocol fixture processes including an approval round trip. A real provider turn must also be checked separately; fixture success is not model access verification. - -## Official runtime installation - -The host panel provides **Install selected agent and Node.js**, with streamed progress and version verification. It uses the same host access, AI opt-in, and `ai.agents` permission as remote sessions, and records an audit event. Installation needs curl, tar, a SHA-256 utility, and Linux/macOS x64 or arm64. It requires no sudo and does not change system packages, shell profiles, existing CLI installations, or SSH forwarding policy. Stop active agents on the host before installing. - -Node.js 24 LTS comes from https://nodejs.org/dist/ and must match the official SHA-256 manifest. Agent package names are fixed to the official documentation: [Pi](https://pi.dev/docs/latest/quickstart), [OpenCode](https://opencode.ai/docs/), [Claude Code](https://code.claude.com/docs/en/setup), and [Codex](https://developers.openai.com/cookbook/examples/codex/secure_quality_gitlab). Only https://registry.npmjs.org dependencies are accepted; missing SHA-512 lock entries are filled from matching official version metadata. npm ci checks the locked integrity values. User/global npm configuration and lifecycle scripts are disabled. Claude/OpenCode use the verified optional platform binary directly, without running download scripts. No third-party mirror or arbitrary installer URL is accepted. - -Files live under `~/.local/share/termix-agent-runtime`. Termix automatically uses this runtime for launches with the default executable. A verified replacement retains the previous installation in `.previous`. Unsupported systems, missing prerequisites, download failures, checksum mismatches, and unavailable platform binaries fail visibly without activating the replacement. SSH remote forwarding remains a separate administrator setting. - -## Enable Agent forwarding - -The separate **Enable Agent forwarding** button configures standard Linux/systemd OpenSSH servers using root or non-interactive sudo. It derives the SSH account and actual peer address from the SSH connection, rather than accepting them from the browser. Existing working remote forwarding is left unchanged. Otherwise, it inserts a scoped `Match User ... Address ...` block allowing `127.0.0.1:*` remote listeners with `GatewayPorts no`; existing local forwarding is preserved. This uses the same host access and `ai.agents` gates and records an audit event. - -The operation backs up sshd_config under `/etc/ssh/termix-agent-backups`, validates both syntax and effective rules before activation, and reloads the active SSH service. Failed activation/reload restores the previous configuration. Existing `DisableForwarding` policies are not overridden. Start/resume creates a new SSH connection after a successful change. Idempotency, preservation of local forwarding, invalid configuration rejection, and reload-failure rollback are covered by executable shell fixtures. - -After configuration, Termix opens a fresh SSH connection and verifies an actual loopback remote listener before reporting success. Key-level forwarding restrictions or a different active server configuration therefore remain visible failures. - -The conversation composer sends with Enter and inserts a newline with Shift+Enter. Escape interrupts a running agent when focus is inside its panel. IME composition and repeated keydown events do not submit or interrupt; a failed send preserves the draft. - -## Conversation workflow - -- Enter sends an idle turn or queues a message while running. A session stores up to 20 queued messages, each editable/removable until dispatched. Dispatch is FIFO after completion; cancellation, archive, and remote errors pause the queue. Continue explicitly resumes it. A stopped session must be resumed first. Messages already dispatched are not automatically replayed after a transport failure. -- Rename and search sessions by name/model/directory. Archive stops the remote process and pauses its queue; restore preserves its transcript. Composer drafts are saved per session after 500 ms and on tab/session unmount. Unsent attachments remain reusable in the session attachment picker. -- Upload a file, paste an image into the composer, or reference a regular file within the working directory (symlinks outside it are rejected). Limits: 1 MiB per file, four per message, 100 per session. Uploads live in the remote user's private session directory. Image MIME is detected from bytes and sent through native Pi/Claude/Codex/OpenCode image inputs; the selected provider/model must support vision. Other files are referenced by their host path for the agent's tools. No file is sent to the model until a message using it is dispatched. -- Git review lists changed/untracked files and shows unstaged and staged diffs, including deleted files. Worktree creation makes a new branch from HEAD in the remote user's Termix state directory. It preserves uncommitted changes in the original workspace, does not overwrite an existing branch, and offers a new session in the resulting directory. Repository hooks, external diff commands, and textconv are disabled for these operations. Worktrees are retained until the user removes them explicitly. -- Browser event streams reconnect with bounded backoff and refresh a persisted snapshot before continuing from its sequence cursor. Duplicate events are suppressed and events are written in batches at most once a second. Access-denied sessions stop retrying. A server/SSH restart still requires Resume; the UI never replays a potentially executed command. The 2,000-event UI retention limit still applies. - -Tests cover FIFO/cancel behavior through the HTTP routes, cross-user metadata/attachment access, native image payloads for four CLI protocols, real temporary Git repositories, path/size boundaries, archive persistence, keyboard/IME handling, and dropped-stream recovery. diff --git a/plugins/ai/CHANGELOG.json b/plugins/ai/CHANGELOG.json deleted file mode 100644 index 6aa54bef8..000000000 --- a/plugins/ai/CHANGELOG.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/Termix-SSH/Termix/main/packages/plugin-sdk/schema/changelog.schema.json", - "releases": [ - { - "version": "1.0.0", - "changes": [ - { - "type": "added", - "text": "Ask about your hosts, snippets, fleets, alerts, automations and more" - }, - { - "type": "added", - "text": "Proposes changes instead of making them, and nothing runs until you approve it" - }, - { - "type": "added", - "text": "Can run safe read-only commands like `df` and `uptime` if you allow it" - }, - { - "type": "added", - "text": "Ask about a host right from its terminal" - }, - { - "type": "added", - "text": "Works with Ollama, Anthropic, OpenAI, Google Gemini and any OpenAI compatible endpoint" - }, - { - "type": "added", - "text": "AI Agents: run Pi, OpenCode, Claude Code or Codex on an SSH host using one of your Termix providers, with tool approvals, queued messages, attachments, Git diffs and worktrees" - }, - { - "type": "added", - "text": "One-click install of the selected agent and Node.js, and a button to set up SSH loopback forwarding" - } - ] - } - ] -} diff --git a/plugins/ai/CONTRIBUTING.md b/plugins/ai/CONTRIBUTING.md deleted file mode 100644 index ea43ef867..000000000 --- a/plugins/ai/CONTRIBUTING.md +++ /dev/null @@ -1,37 +0,0 @@ -# Contributing to AI Assistant - -## Development - -```bash -npm run build # build into dist/ -npm run test # run this plugin's tests -npm run typecheck # type-check this plugin -npm run validate # check manifest.json -npm run format # format the code with Prettier -``` - -## Settings - -### Admin - -- **AI assistant:** let users turn the assistant on. While this is off it is hidden and blocked for everyone -- **Allowed private AI hosts:** hosts on your private network a provider may point at, such as a self-hosted Ollama - -### User - -- **Enable the AI assistant:** turn the assistant on for yourself -- **Allow read-only diagnostic commands:** let the assistant run safe commands without asking -- **Providers:** your AI providers, models and API keys - -### Host - -- **Enable AI Assistant:** let the assistant help in this host's terminal. Off by default - -## Permissions - -- `ai.use`: use the assistant. Admins and users have it by default. -- `ai.manage_providers`: add, edit and remove AI providers and their API keys. Admins and users have it by default. -- `ai.apply_proposals`: let the assistant carry out the changes it suggests. Admins and users have it by default. -- `ai.services.use`: let other features call the assistant for you. Admins and users have it by default. -- `ai.secrets.share`: let other features use the stored API key without seeing it. Admins and users have it by default. -- `ai.agents`: run coding agents on SSH hosts. Only admins have it by default. diff --git a/plugins/ai/README.md b/plugins/ai/README.md deleted file mode 100644 index 5171eddba..000000000 --- a/plugins/ai/README.md +++ /dev/null @@ -1,48 +0,0 @@ -
- -Termix Logo - -

AI Assistant

- -

An optional AI assistant that knows your servers

- -
- -
- -## Overview - -AI Assistant is a chat that can read your Termix setup and propose changes for you to approve. It can also run coding agents on your SSH hosts. - -
- -## Features - -- Ask about your hosts, snippets, fleets, alerts, automations and more -- Proposes changes instead of making them, and nothing runs until you approve it -- Can run safe read-only commands like `df` and `uptime` if you allow it -- Ask about a host right from its terminal -- Works with Ollama, Anthropic, OpenAI, Google Gemini and any OpenAI compatible endpoint -- Run Pi, OpenCode, Claude Code or Codex as a coding agent on an SSH host, using your own provider - -
- -## Services - -Uses from other plugins, each one optional: - -- `workspaces.saved`, `network-topology.graph`, `snippets.access`, `fleets.access`, `terminal.history`, `automations.access` and `homepage.items` to read your data - -
- -## Support - -To report a bug or request a feature, open a [support ticket](https://github.com/Termix-SSH/Support/issues/new/choose). You need to be logged in to GitHub. Please be as detailed as possible, preferably in English. - -For discussions and questions, join the [Discord](https://discord.gg/jVQGdvHDrf) server. - -
- -## License - -Distributed under the Apache License Version 2.0. See [LICENSE](https://github.com/Termix-SSH/Termix/blob/main/LICENSE) for more information. diff --git a/plugins/ai/locales/en.json b/plugins/ai/locales/en.json deleted file mode 100644 index 9bf378297..000000000 --- a/plugins/ai/locales/en.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Use the assistant", - "description": "Open the assistant and ask it things." - }, - "manage_providers": { - "title": "Manage providers", - "description": "Add, edit and remove the AI providers and their API keys." - }, - "apply_proposals": { - "title": "Apply proposals", - "description": "Let the assistant carry out the changes it suggests." - }, - "services.use": { - "title": "Use AI from other features", - "description": "Other features may call the assistant on your behalf." - }, - "secrets.share": { - "title": "Share the API key", - "description": "Other features may use the stored AI API key without seeing it." - } - }, - "settings": { - "assistantUnavailable": "The AI assistant is turned off on this server.", - "providersNeedOptIn": "Turn on the AI assistant above to add providers.", - "admin": { - "globallyEnabled": { - "label": "AI assistant", - "description": "Let users turn on the AI assistant. While this is off, the assistant is hidden and blocked for everyone." - }, - "privateEndpoints": { - "label": "Allowed private AI hosts", - "description": "Hosts on your private network that users may point a provider at, such as a self-hosted Ollama. Put one host on each line." - } - }, - "host": { - "enableAiAssistant": { - "label": "Enable AI Assistant", - "description": "Allow the AI assistant to help in this host's terminal. Off by default, and still needs AI enabled for your account." - } - } - }, - "ai": { - "title": "Assistant", - "send": "Send", - "stop": "Stop", - "save": "Save", - "cancel": "Cancel", - "running": "running", - "approve": "Approve", - "reject": "Reject", - "inputPlaceholder": "Ask about your servers, or describe a change to propose", - "selectProvider": "Select a provider", - "providerSettings": "Provider settings", - "addProvider": "Add provider", - "editProvider": "Edit provider", - "removeProvider": "Remove provider", - "providerType": "Provider", - "providerLabel": "Name", - "providerLabelPlaceholder": "For example: Local Ollama", - "baseUrl": "Server address", - "apiKey": "API key", - "defaultModel": "Default model", - "defaultModelPlaceholder": "For example: llama3.1 or claude-opus-5", - "labelRequired": "Give this provider a name", - "providerSaveFailed": "Could not save the provider", - "providerUpdated": "Provider updated", - "providerDeleteFailed": "Could not remove the provider", - "privateEndpointHint": "Addresses on your private network need an administrator to allow the host first.", - "providerOllama": "Ollama", - "providerAnthropic": "Anthropic", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI compatible", - "toolArguments": "Input", - "toolResult": "Result", - "statusApplied": "Applied", - "statusRejected": "Rejected", - "proposalApplied": "Change applied", - "proposalApplyFailed": "Could not apply the change", - "proposalRejectFailed": "Could not reject the change", - "enableTitle": "Enable the AI assistant", - "enableDescription": "Let the assistant read your setup and suggest changes for you to approve.", - "readOnlyCommands": "Allow read-only diagnostic commands", - "readOnlyCommandsDescription": "The assistant may run a short list of safe commands like df and uptime without asking each time. Anything that changes a server is always proposed first.", - "modelDetectFailed": "Could not reach the provider, so this is a general list. You can type any model name.", - "modelCustom": "Enter a model name", - "modelRefresh": "Refresh list", - "modelPlaceholder": "Choose or type a model", - "attachHint": "Type @ to reference a host, snippet or automation. Changes are proposed for you to approve.", - "runCommandTitle": "Run command", - "assistant": "AI Assistant", - "terminalPanelEmpty": "Ask the assistant about this host. Anything that changes state needs your approval first.", - "terminalPanelPlaceholder": "Ask about this host...", - "noProvidersConfigured": "No AI provider is configured yet. Add one in the AI assistant tab.", - "runsInOpenTerminal": "Runs in your open terminal session.", - "runInTerminal": "Run", - "terminalContextPrefill": "Here is my terminal output:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Let other features use your AI provider API key for their own requests. The key is never copied, and you can revoke this at any time." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI assistant", - "aiIntro": "Termix can include an optional AI assistant. You can change this later in your settings.", - "aiSkipTitle": "Leave it off", - "aiSkipDesc": "Termix works as it does now. The assistant stays hidden.", - "aiEnableTitle": "Turn it on", - "aiEnableDesc": "Adds an assistant that reads your setup and suggests changes you approve. Needs a provider you supply, such as a local Ollama.", - "aiNote": "Nothing is sent anywhere until you add a provider and send a message." - }, - "hosts": { - "docsLink": "View docs" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/af_ZA.json b/plugins/ai/locales/translated/af_ZA.json deleted file mode 100644 index 3ae53903f..000000000 --- a/plugins/ai/locales/translated/af_ZA.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Gebruik die assistent", - "description": "Maak die assistent oop en vra dit dinge." - }, - "manage_providers": { - "title": "Bestuur verskaffers", - "description": "Voeg die KI-verskaffers en hul API-sleutels by, wysig en verwyder hulle." - }, - "apply_proposals": { - "title": "Pas voorstelle toe", - "description": "Laat die assistent die veranderinge wat dit voorstel, uitvoer." - }, - "services.use": { - "title": "Gebruik KI van ander funksies", - "description": "Ander funksies kan die assistent namens jou skakel." - }, - "secrets.share": { - "title": "Deel die API-sleutel", - "description": "Ander funksies mag die gestoorde KI API-sleutel gebruik sonder om dit te sien." - } - }, - "settings": { - "assistantUnavailable": "Die KI-assistent is afgeskakel op hierdie bediener.", - "providersNeedOptIn": "Skakel die KI-assistent hierbo aan om verskaffers by te voeg.", - "admin": { - "globallyEnabled": { - "label": "KI-assistent", - "description": "Laat gebruikers die KI-assistent aanskakel. Terwyl dit af is, is die assistent vir almal versteek en geblokkeer." - }, - "privateEndpoints": { - "label": "Toegelate private KI-gashere", - "description": "Gashere op jou privaat netwerk waarna gebruikers 'n verskaffer kan verwys, soos 'n self-gehoste Ollama. Plaas een gasheer op elke lyn." - } - }, - "host": { - "enableAiAssistant": { - "label": "Aktiveer KI-assistent", - "description": "Laat die KI-assistent toe om in hierdie gasheer se terminaal te help. Standaard afgeskakel, en KI moet steeds vir jou rekening geaktiveer word." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Stuur", - "stop": "Stop", - "save": "Stoor", - "cancel": "Kanselleer", - "running": "hardloop", - "approve": "Goedkeur", - "reject": "Verwerp", - "inputPlaceholder": "Vra oor jou bedieners, of beskryf 'n verandering om voor te stel", - "selectProvider": "Kies 'n verskaffer", - "providerSettings": "Verskafferinstellings", - "addProvider": "Voeg verskaffer by", - "editProvider": "Wysig verskaffer", - "removeProvider": "Verwyder verskaffer", - "providerType": "Verskaffer", - "providerLabel": "Naam", - "providerLabelPlaceholder": "Byvoorbeeld: Plaaslike Ollama", - "baseUrl": "Bedieneradres", - "apiKey": "API-sleutel", - "defaultModel": "Standaardmodel", - "defaultModelPlaceholder": "Byvoorbeeld: llama3.1 of claude-opus-5", - "labelRequired": "Gee hierdie verskaffer 'n naam", - "providerSaveFailed": "Kon nie die verskaffer stoor nie", - "providerUpdated": "Verskaffer opgedateer", - "providerDeleteFailed": "Kon nie die verskaffer verwyder nie", - "privateEndpointHint": "Adresse op jou private netwerk benodig eers 'n administrateur om die gasheer toe te laat.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropiese", - "providerOpenai": "OpenAI", - "providerGemini": "Google Tweeling", - "providerOpenaiCompatible": "OpenAI-versoenbaar", - "toolArguments": "Invoer", - "toolResult": "Resultaat", - "statusApplied": "Toegepas", - "statusRejected": "Verwerp", - "proposalApplied": "Verandering toegepas", - "proposalApplyFailed": "Kon nie die verandering toepas nie", - "proposalRejectFailed": "Kon nie die verandering verwerp nie", - "enableTitle": "Aktiveer die KI-assistent", - "enableDescription": "Laat die assistent jou opstelling lees en veranderinge voorstel wat jy kan goedkeur.", - "readOnlyCommands": "Laat leesalleen-diagnostiese bevele toe", - "readOnlyCommandsDescription": "Die assistent kan 'n kort lys veilige opdragte soos df en uptime uitvoer sonder om elke keer te vra. Enigiets wat 'n bediener verander, word altyd eerste voorgestel.", - "modelDetectFailed": "Kon nie die verskaffer bereik nie, so dit is 'n algemene lys. Jy kan enige modelnaam tik.", - "modelCustom": "Voer 'n modelnaam in", - "modelRefresh": "Verfris lys", - "modelPlaceholder": "Kies of tik 'n model", - "attachHint": "Tik @ om na 'n gasheer, brokkie of outomatisering te verwys. Veranderinge word voorgestel vir jou om goed te keur.", - "runCommandTitle": "Voer opdrag uit", - "assistant": "KI-assistent", - "terminalPanelEmpty": "Vra die assistent oor hierdie gasheer. Enigiets wat van status verander, benodig eers jou goedkeuring.", - "terminalPanelPlaceholder": "Vra oor hierdie gasheer...", - "noProvidersConfigured": "Geen KI-verskaffer is nog gekonfigureer nie. Voeg een by in die KI-assistent-oortjie.", - "runsInOpenTerminal": "Loop in jou oop terminaalsessie.", - "runInTerminal": "Hardloop", - "terminalContextPrefill": "Hier is my terminaaluitvoer:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Laat ander funksies jou KI-verskaffer API-sleutel vir hul eie versoeke gebruik. Die sleutel word nooit gekopieer nie, en jy kan dit te eniger tyd herroep." - } - }, - "nav": { - "ai": "KI" - }, - "onboarding": { - "aiTitle": "KI-assistent", - "aiIntro": "Termix kan 'n opsionele KI-assistent insluit. Jy kan dit later in jou instellings verander.", - "aiSkipTitle": "Los dit af", - "aiSkipDesc": "Termix werk soos dit nou doen. Die assistent bly versteek.", - "aiEnableTitle": "Skakel dit aan", - "aiEnableDesc": "Voeg 'n assistent by wat jou opstelling lees en veranderinge voorstel wat jy goedkeur. Benodig 'n verskaffer wat jy verskaf, soos 'n plaaslike Ollama.", - "aiNote": "Niks word êrens heen gestuur totdat jy 'n verskaffer byvoeg en 'n boodskap stuur nie." - }, - "hosts": { - "docsLink": "Bekyk dokumente" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ar_SA.json b/plugins/ai/locales/translated/ar_SA.json deleted file mode 100644 index 0469291a2..000000000 --- a/plugins/ai/locales/translated/ar_SA.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "استخدم المساعد", - "description": "افتح المساعد واسأله ما تريد." - }, - "manage_providers": { - "title": "إدارة الموفر", - "description": "أضف، عدّل، واحذف مزودي خدمات الذكاء الاصطناعي ومفاتيح واجهة برمجة التطبيقات الخاصة بهم." - }, - "apply_proposals": { - "title": "تقديم المقترحات", - "description": "دع المساعد يقوم بتنفيذ التغييرات التي يقترحها." - }, - "services.use": { - "title": "استخدم الذكاء الاصطناعي من ميزات أخرى", - "description": "قد تقوم ميزات أخرى بالاتصال بالمساعد نيابةً عنك." - }, - "secrets.share": { - "title": "شارك مفتاح API", - "description": "قد تستخدم ميزات أخرى مفتاح واجهة برمجة تطبيقات الذكاء الاصطناعي المخزن دون رؤيته." - } - }, - "settings": { - "assistantUnavailable": "تم إيقاف تشغيل مساعد الذكاء الاصطناعي على هذا الخادم.", - "providersNeedOptIn": "قم بتشغيل مساعد الذكاء الاصطناعي أعلاه لإضافة مزودي الخدمة.", - "admin": { - "globallyEnabled": { - "label": "مساعد الذكاء الاصطناعي", - "description": "اسمح للمستخدمين بتفعيل مساعد الذكاء الاصطناعي. عند إيقاف تشغيله، يكون المساعد مخفيًا ومحظورًا عن الجميع." - }, - "privateEndpoints": { - "label": "المضيفون الخاصون المسموح بهم للذكاء الاصطناعي", - "description": "المضيفون على شبكتك الخاصة الذين يمكن للمستخدمين توجيه مزود الخدمة إليهم، مثل Ollama المُستضاف ذاتيًا. ضع مضيفًا واحدًا على كل سطر." - } - }, - "host": { - "enableAiAssistant": { - "label": "تفعيل مساعد الذكاء الاصطناعي", - "description": "اسمح للمساعد الذكي بالمساعدة في طرفية هذا المضيف. هذه الميزة معطلة افتراضيًا، ولا يزال يتعين تفعيلها لحسابك." - } - } - }, - "ai": { - "title": "مساعد", - "send": "يرسل", - "stop": "التوقف", - "save": "حفظ", - "cancel": "إلغاء", - "running": "قيد التشغيل", - "approve": "يعتمد", - "reject": "يرفض", - "inputPlaceholder": "استفسر عن خوادمك، أو صف تغييرًا ترغب في اقتراحه.", - "selectProvider": "اختر مزود الخدمة", - "providerSettings": "إعدادات المزوّد", - "addProvider": "إضافة موفر", - "editProvider": "موفر التحرير", - "removeProvider": "إزالة الموفر", - "providerType": "مزود", - "providerLabel": "الاسم", - "providerLabelPlaceholder": "على سبيل المثال: أولاما المحلي", - "baseUrl": "عنوان الخادم", - "apiKey": "مفتاح API", - "defaultModel": "النموذج الافتراضي", - "defaultModelPlaceholder": "على سبيل المثال: llama3.1 أو claude-opus-5", - "labelRequired": "أعطِ هذا المزوّد اسمًا", - "providerSaveFailed": "تعذر حفظ بيانات المزوّد", - "providerUpdated": "تم تحديث المزوّد", - "providerDeleteFailed": "تعذر إزالة الموفر", - "privateEndpointHint": "تحتاج العناوين الموجودة على شبكتك الخاصة إلى موافقة المسؤول أولاً للسماح للمضيف بالوصول إليها.", - "providerOllama": "أولاما", - "providerAnthropic": "الأنثروبيك", - "providerOpenai": "أوبن إيه آي", - "providerGemini": "جوجل جيميني", - "providerOpenaiCompatible": "متوافق مع OpenAI", - "toolArguments": "إدخال", - "toolResult": "نتيجة", - "statusApplied": "مُطبَّق", - "statusRejected": "مرفوض", - "proposalApplied": "تم تطبيق التغيير", - "proposalApplyFailed": "تعذر تطبيق التغيير", - "proposalRejectFailed": "لم يكن بالإمكان رفض التغيير", - "enableTitle": "قم بتفعيل مساعد الذكاء الاصطناعي", - "enableDescription": "دع المساعد يقرأ إعداداتك ويقترح تغييرات لتوافق عليها.", - "readOnlyCommands": "السماح بأوامر التشخيص للقراءة فقط", - "readOnlyCommandsDescription": "قد يقوم المساعد بتشغيل قائمة مختصرة من الأوامر الآمنة مثل df و uptime دون الحاجة إلى طلب ذلك في كل مرة. ويتم اقتراح أي أمر يُغيّر حالة الخادم أولاً.", - "modelDetectFailed": "تعذر الوصول إلى المورّد، لذا فهذه قائمة عامة. يمكنك كتابة أي اسم طراز.", - "modelCustom": "أدخل اسم النموذج", - "modelRefresh": "تحديث القائمة", - "modelPlaceholder": "اختر أو اكتب طرازًا", - "attachHint": "اكتب @ للإشارة إلى مضيف أو مقتطف أو عملية آلية. سيتم اقتراح تغييرات عليك الموافقة عليها.", - "runCommandTitle": "تشغيل الأمر", - "assistant": "مساعد الذكاء الاصطناعي", - "terminalPanelEmpty": "استفسر من المساعد عن هذا المضيف. أي تغيير في حالته يتطلب موافقتك أولاً.", - "terminalPanelPlaceholder": "اسأل عن هذا المضيف...", - "noProvidersConfigured": "لم يتم إعداد أي موفر للذكاء الاصطناعي حتى الآن. أضف واحداً في علامة تبويب مساعد الذكاء الاصطناعي.", - "runsInOpenTerminal": "يتم تشغيله في جلسة الطرفية المفتوحة لديك.", - "runInTerminal": "تشغيل", - "terminalContextPrefill": "هذا هو ناتج الطرفية الخاص بي:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "اسمح للميزات الأخرى باستخدام مفتاح واجهة برمجة التطبيقات (API) الخاص بمزود الذكاء الاصطناعي لطلباتها الخاصة. لا يتم نسخ المفتاح مطلقًا، ويمكنك إلغاء هذا في أي وقت." - } - }, - "nav": { - "ai": "الذكاء الاصطناعي" - }, - "onboarding": { - "aiTitle": "مساعد الذكاء الاصطناعي", - "aiIntro": "يمكن أن يتضمن تطبيق Termix مساعدًا ذكيًا اختياريًا. يمكنك تغيير هذا الخيار لاحقًا من الإعدادات.", - "aiSkipTitle": "اتركه مغلقًا", - "aiSkipDesc": "يعمل برنامج Termix كما هو الآن. ويبقى المساعد مخفياً.", - "aiEnableTitle": "قم بتشغيله", - "aiEnableDesc": "يُضيف هذا البرنامج مساعدًا يقرأ إعداداتك ويقترح تغييرات توافق عليها. يتطلب ذلك مزود خدمة تُحدده بنفسك، مثل مزود خدمة Ollama محلي.", - "aiNote": "لن يتم إرسال أي شيء إلى أي مكان حتى تقوم بإضافة مزود خدمة وإرسال رسالة." - }, - "hosts": { - "docsLink": "عرض المستندات" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/bg_BG.json b/plugins/ai/locales/translated/bg_BG.json deleted file mode 100644 index 00f398754..000000000 --- a/plugins/ai/locales/translated/bg_BG.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Използвайте асистента", - "description": "Отворете асистента и го попитайте нещо." - }, - "manage_providers": { - "title": "Управление на доставчици", - "description": "Добавяне, редактиране и премахване на доставчици на изкуствен интелект и техните API ключове." - }, - "apply_proposals": { - "title": "Прилагане на предложения", - "description": "Нека асистентът извърши предложените от него промени." - }, - "services.use": { - "title": "Използвайте изкуствен интелект от други функции", - "description": "Други функции може да извикат асистента от ваше име." - }, - "secrets.share": { - "title": "Споделете API ключа", - "description": "Други функции могат да използват съхранения AI API ключ, без да го виждат." - } - }, - "settings": { - "assistantUnavailable": "Асистентът с изкуствен интелект е изключен на този сървър.", - "providersNeedOptIn": "Включете AI асистента по-горе, за да добавите доставчици.", - "admin": { - "globallyEnabled": { - "label": "Асистент с изкуствен интелект", - "description": "Позволете на потребителите да включат асистента с изкуствен интелект. Докато това е изключено, асистентът е скрит и блокиран за всички." - }, - "privateEndpoints": { - "label": "Разрешени частни хостове с изкуствен интелект", - "description": "Хостове във вашата частна мрежа, към които потребителите могат да насочат доставчик, като например самостоятелно хостван Ollama. Поставете по един хост на всеки ред." - } - }, - "host": { - "enableAiAssistant": { - "label": "Активиране на AI асистент", - "description": "Разрешете на AI асистента да помага в терминала на този хост. Изключено по подразбиране и все още изисква AI да е активиран за вашия акаунт." - } - } - }, - "ai": { - "title": "Асистент", - "send": "Изпрати", - "stop": "Спри", - "save": "Запазване", - "cancel": "Отказ", - "running": "бягане", - "approve": "Одобряване", - "reject": "Отхвърляне", - "inputPlaceholder": "Попитайте за вашите сървъри или опишете промяна, която да предложите", - "selectProvider": "Изберете доставчик", - "providerSettings": "Настройки на доставчика", - "addProvider": "Добавяне на доставчик", - "editProvider": "Редактиране на доставчика", - "removeProvider": "Премахване на доставчика", - "providerType": "Доставчик", - "providerLabel": "Име", - "providerLabelPlaceholder": "Например: Местна Олама", - "baseUrl": "Адрес на сървъра", - "apiKey": "API ключ", - "defaultModel": "Модел по подразбиране", - "defaultModelPlaceholder": "Например: llama3.1 или claude-opus-5", - "labelRequired": "Дайте име на този доставчик", - "providerSaveFailed": "Доставчикът не можа да бъде запазен", - "providerUpdated": "Доставчикът е актуализиран", - "providerDeleteFailed": "Доставчикът не можа да бъде премахнат", - "privateEndpointHint": "Адресите във вашата частна мрежа се нуждаят първо от администратор, който да разреши хоста.", - "providerOllama": "Олама", - "providerAnthropic": "Антропен", - "providerOpenai": "OpenAI", - "providerGemini": "Google Джемини", - "providerOpenaiCompatible": "Съвместим с OpenAI", - "toolArguments": "Вход", - "toolResult": "Резултат", - "statusApplied": "Приложено", - "statusRejected": "Отхвърлено", - "proposalApplied": "Промяната е приложена", - "proposalApplyFailed": "Промяната не можа да се приложи", - "proposalRejectFailed": "Промяната не можа да бъде отхвърлена", - "enableTitle": "Активирайте AI асистента", - "enableDescription": "Нека асистентът прочете настройката ви и предложи промени, които да одобрите.", - "readOnlyCommands": "Разрешаване на диагностични команди само за четене", - "readOnlyCommandsDescription": "Асистентът може да изпълни кратък списък с безопасни команди като df и uptime, без да пита всеки път. Всичко, което променя сървър, винаги се предлага първо.", - "modelDetectFailed": "Не можахме да се свържем с доставчика, така че това е общ списък. Можете да въведете име на произволен модел.", - "modelCustom": "Въведете име на модел", - "modelRefresh": "Обновяване на списъка", - "modelPlaceholder": "Изберете или въведете модел", - "attachHint": "Въведете @, за да се обърнете към хост, фрагмент или автоматизация. Предлагат се промени за одобрение.", - "runCommandTitle": "Изпълнете командата", - "assistant": "Асистент с изкуствен интелект", - "terminalPanelEmpty": "Попитайте асистента за този хост. Всичко, което променя състоянието, първо изисква вашето одобрение.", - "terminalPanelPlaceholder": "Попитайте за този домакин...", - "noProvidersConfigured": "Все още не е конфигуриран доставчик на ИИ. Добавете такъв в раздела „Асистент за ИИ“.", - "runsInOpenTerminal": "Изпълнява се във вашата отворена терминална сесия.", - "runInTerminal": "Бягане", - "terminalContextPrefill": "Ето изхода на моя терминал:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Позволете на други функции да използват вашия API ключ на доставчика на изкуствен интелект за свои собствени заявки. Ключът никога не се копира и можете да го отмените по всяко време." - } - }, - "nav": { - "ai": "Изкуствен интелект" - }, - "onboarding": { - "aiTitle": "Асистент с изкуствен интелект", - "aiIntro": "Termix може да включва опционален AI асистент. Можете да промените това по-късно в настройките си.", - "aiSkipTitle": "Остави го изключено", - "aiSkipDesc": "Termix работи както сега. Асистентът остава скрит.", - "aiEnableTitle": "Включете го", - "aiEnableDesc": "Добавя асистент, който чете настройките ви и предлага промени, които одобрявате. Необходим е доставчик, когото вие предоставяте, например местен Ollama.", - "aiNote": "Нищо не се изпраща никъде, докато не добавите доставчик и не изпратите съобщение." - }, - "hosts": { - "docsLink": "Преглед на документи" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/bn_BD.json b/plugins/ai/locales/translated/bn_BD.json deleted file mode 100644 index 861b6915b..000000000 --- a/plugins/ai/locales/translated/bn_BD.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "সহকারী ব্যবহার করুন", - "description": "অ্যাসিস্ট্যান্টটি খুলুন এবং এটিকে বিভিন্ন জিনিস জিজ্ঞাসা করুন।" - }, - "manage_providers": { - "title": "সরবরাহকারীদের পরিচালনা করুন", - "description": "এআই প্রোভাইডার এবং তাদের এপিআই কীগুলো যোগ, সম্পাদনা ও অপসারণ করুন।" - }, - "apply_proposals": { - "title": "প্রস্তাবগুলি প্রয়োগ করুন", - "description": "সহকারীকে তার প্রস্তাবিত পরিবর্তনগুলো কার্যকর করতে দিন।" - }, - "services.use": { - "title": "অন্যান্য বৈশিষ্ট্য থেকে এআই ব্যবহার করুন", - "description": "অন্যান্য বৈশিষ্ট্য আপনার পক্ষ থেকে সহকারীকে ডাকতে পারে।" - }, - "secrets.share": { - "title": "এপিআই কী শেয়ার করুন", - "description": "অন্যান্য ফিচারগুলো সংরক্ষিত এআই এপিআই কী-টি না দেখেই ব্যবহার করতে পারে।" - } - }, - "settings": { - "assistantUnavailable": "এই সার্ভারে এআই অ্যাসিস্ট্যান্ট বন্ধ করা আছে।", - "providersNeedOptIn": "প্রোভাইডার যোগ করতে উপরের এআই অ্যাসিস্ট্যান্টটি চালু করুন।", - "admin": { - "globallyEnabled": { - "label": "এআই সহকারী", - "description": "ব্যবহারকারীদের এআই অ্যাসিস্ট্যান্ট চালু করার সুযোগ দিন। এটি বন্ধ থাকা অবস্থায়, অ্যাসিস্ট্যান্টটি সকলের জন্য লুকানো এবং ব্লক করা থাকবে।" - }, - "privateEndpoints": { - "label": "ব্যক্তিগত এআই হোস্ট অনুমোদিত", - "description": "আপনার প্রাইভেট নেটওয়ার্কে এমন হোস্ট রাখুন, যেগুলোকে ব্যবহারকারীরা কোনো প্রোভাইডারের দিকে নির্দেশ করতে পারে, যেমন একটি সেলফ-হোস্টেড ওলামা। প্রতিটি লাইনে একটি করে হোস্ট রাখুন।" - } - }, - "host": { - "enableAiAssistant": { - "label": "এআই অ্যাসিস্ট্যান্ট সক্রিয় করুন", - "description": "এই হোস্টের টার্মিনালে সাহায্য করার জন্য এআই অ্যাসিস্ট্যান্টকে অনুমতি দিন। এটি ডিফল্টরূপে বন্ধ থাকে এবং আপনার অ্যাকাউন্টের জন্য এআই চালু করা প্রয়োজন।" - } - } - }, - "ai": { - "title": "সহকারী", - "send": "পাঠান", - "stop": "থামুন", - "save": "সংরক্ষণ করুন", - "cancel": "বাতিল করুন", - "running": "দৌড়ানো", - "approve": "অনুমোদন করুন", - "reject": "প্রত্যাখ্যান করুন", - "inputPlaceholder": "আপনার সার্ভারগুলো সম্পর্কে জিজ্ঞাসা করুন, অথবা কোনো পরিবর্তনের প্রস্তাব বর্ণনা করুন।", - "selectProvider": "একজন সরবরাহকারী নির্বাচন করুন", - "providerSettings": "প্রদানকারীর সেটিংস", - "addProvider": "প্রদানকারী যোগ করুন", - "editProvider": "সম্পাদনা প্রদানকারী", - "removeProvider": "প্রদানকারীকে অপসারণ করুন", - "providerType": "সরবরাহকারী", - "providerLabel": "নাম", - "providerLabelPlaceholder": "উদাহরণস্বরূপ: স্থানীয় ওলামা", - "baseUrl": "সার্ভারের ঠিকানা", - "apiKey": "এপিআই কী", - "defaultModel": "ডিফল্ট মডেল", - "defaultModelPlaceholder": "উদাহরণস্বরূপ: llama3.1 অথবা claude-opus-5", - "labelRequired": "এই সরবরাহকারীকে একটি নাম দিন", - "providerSaveFailed": "প্রদানকারীকে সংরক্ষণ করা যায়নি", - "providerUpdated": "প্রদানকারী আপডেট করেছেন", - "providerDeleteFailed": "প্রদানকারীকে সরানো যায়নি", - "privateEndpointHint": "আপনার ব্যক্তিগত নেটওয়ার্কের অ্যাড্রেসগুলো ব্যবহার করার জন্য প্রথমে একজন অ্যাডমিনিস্ট্রেটরের কাছ থেকে হোস্টটিকে অনুমতি নিতে হয়।", - "providerOllama": "ওল্লামা", - "providerAnthropic": "অ্যানথ্রোপিক", - "providerOpenai": "ওপেনএআই", - "providerGemini": "গুগল জেমিনি", - "providerOpenaiCompatible": "ওপেনএআই সামঞ্জস্যপূর্ণ", - "toolArguments": "ইনপুট", - "toolResult": "ফলাফল", - "statusApplied": "প্রয়োগ করা হয়েছে", - "statusRejected": "প্রত্যাখ্যাত", - "proposalApplied": "পরিবর্তন প্রয়োগ করা হয়েছে", - "proposalApplyFailed": "পরিবর্তনটি প্রয়োগ করা যায়নি।", - "proposalRejectFailed": "পরিবর্তনটি প্রত্যাখ্যান করা যায়নি", - "enableTitle": "এআই অ্যাসিস্ট্যান্ট সক্রিয় করুন", - "enableDescription": "সহকারীকে আপনার সেটআপটি পড়তে দিন এবং আপনার অনুমোদনের জন্য পরিবর্তনের পরামর্শ দিতে দিন।", - "readOnlyCommands": "শুধুমাত্র পঠনযোগ্য ডায়াগনস্টিক কমান্ডের অনুমতি দিন", - "readOnlyCommandsDescription": "সহকারী প্রতিবার অনুমতি না নিয়েই df এবং uptime-এর মতো কয়েকটি নিরাপদ কমান্ড চালাতে পারে। সার্ভার পরিবর্তন করে এমন যেকোনো কিছু সর্বদা প্রথমে প্রস্তাব করা হয়।", - "modelDetectFailed": "সরবরাহকারীর সাথে যোগাযোগ করা সম্ভব হয়নি, তাই এটি একটি সাধারণ তালিকা। আপনি যেকোনো মডেলের নাম টাইপ করতে পারেন।", - "modelCustom": "একটি মডেলের নাম লিখুন", - "modelRefresh": "তালিকা রিফ্রেশ করুন", - "modelPlaceholder": "একটি মডেল বেছে নিন বা টাইপ করুন", - "attachHint": "কোনো হোস্ট, স্নিপেট বা অটোমেশন উল্লেখ করতে @ টাইপ করুন। আপনার অনুমোদনের জন্য পরিবর্তনগুলো প্রস্তাব করা হবে।", - "runCommandTitle": "রান কমান্ড", - "assistant": "এআই সহকারী", - "terminalPanelEmpty": "এই হোস্ট সম্পর্কে অ্যাসিস্ট্যান্টকে জিজ্ঞাসা করুন। অবস্থা পরিবর্তনের জন্য প্রথমে আপনার অনুমোদন প্রয়োজন।", - "terminalPanelPlaceholder": "এই হোস্ট সম্পর্কে জিজ্ঞাসা করুন...", - "noProvidersConfigured": "এখনো কোনো এআই প্রোভাইডার কনফিগার করা হয়নি। এআই অ্যাসিস্ট্যান্ট ট্যাবে একটি যোগ করুন।", - "runsInOpenTerminal": "আপনার খোলা টার্মিনাল সেশনে চলে।", - "runInTerminal": "দৌড়", - "terminalContextPrefill": "আমার টার্মিনাল আউটপুট নিচে দেওয়া হলো:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "অন্যান্য ফিচারগুলোকে তাদের নিজস্ব অনুরোধের জন্য আপনার এআই প্রোভাইডার এপিআই কী ব্যবহার করতে দিন। কী-টি কখনোই কপি করা হয় না এবং আপনি যেকোনো সময় এটি প্রত্যাহার করতে পারেন।" - } - }, - "nav": { - "ai": "এআই" - }, - "onboarding": { - "aiTitle": "এআই সহকারী", - "aiIntro": "টারমিক্সে একটি ঐচ্ছিক এআই অ্যাসিস্ট্যান্ট অন্তর্ভুক্ত থাকতে পারে। আপনি পরবর্তীতে আপনার সেটিংসে এটি পরিবর্তন করতে পারবেন।", - "aiSkipTitle": "এটা ছেড়ে দাও", - "aiSkipDesc": "টারমিক্স এখন যেমন কাজ করে, তেমনই কাজ করে। অ্যাসিস্ট্যান্টটি লুকানো থাকে।", - "aiEnableTitle": "এটা চালু করুন", - "aiEnableDesc": "একজন সহকারী যোগ করে যে আপনার সেটআপ পড়ে এবং আপনার অনুমোদিত পরিবর্তনের পরামর্শ দেয়। এর জন্য আপনার সরবরাহ করা একজন প্রদানকারীর প্রয়োজন, যেমন একজন স্থানীয় ওলামা।", - "aiNote": "আপনি একজন প্রোভাইডার যোগ করে মেসেজ না পাঠানো পর্যন্ত কোথাও কিছু পাঠানো হয় না।" - }, - "hosts": { - "docsLink": "নথি দেখুন" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ca_ES.json b/plugins/ai/locales/translated/ca_ES.json deleted file mode 100644 index 6e4fb4326..000000000 --- a/plugins/ai/locales/translated/ca_ES.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Utilitza l'assistent", - "description": "Obre l'assistent i fes-li preguntes." - }, - "manage_providers": { - "title": "Gestiona els proveïdors", - "description": "Afegiu, editeu i elimineu els proveïdors d'IA i les seves claus API." - }, - "apply_proposals": { - "title": "Aplicar propostes", - "description": "Deixa que l'assistent dugui a terme els canvis que suggereixi." - }, - "services.use": { - "title": "Utilitza la IA d'altres funcions", - "description": "Altres funcions poden trucar a l'assistent en nom teu." - }, - "secrets.share": { - "title": "Comparteix la clau de l'API", - "description": "Altres funcions poden utilitzar la clau de l'API d'IA emmagatzemada sense veure-la." - } - }, - "settings": { - "assistantUnavailable": "L'assistent d'IA està desactivat en aquest servidor.", - "providersNeedOptIn": "Activa l'assistent d'IA que hi ha a sobre per afegir proveïdors.", - "admin": { - "globallyEnabled": { - "label": "Assistent d'IA", - "description": "Permet que els usuaris activin l'assistent d'IA. Mentre estigui desactivat, l'assistent està ocult i bloquejat per a tothom." - }, - "privateEndpoints": { - "label": "Amfitrions d'IA privats permesos", - "description": "Amfitrions de la vostra xarxa privada als quals els usuaris poden dirigir un proveïdor, com ara un Ollama autoallotjat. Poseu un amfitrió a cada línia." - } - }, - "host": { - "enableAiAssistant": { - "label": "Activa l'Assistent d'IA", - "description": "Permet que l'assistent d'IA ajudi al terminal d'aquest amfitrió. Desactivat per defecte, però encara cal que la IA estigui activada per al teu compte." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Enviar", - "stop": "Atura", - "save": "Desa", - "cancel": "Cancel·la", - "running": "corrent", - "approve": "Aprova", - "reject": "Rebutja", - "inputPlaceholder": "Pregunteu pels vostres cambrers o descriviu un canvi que vulgueu proposar", - "selectProvider": "Seleccioneu un proveïdor", - "providerSettings": "Configuració del proveïdor", - "addProvider": "Afegeix un proveïdor", - "editProvider": "Edita el proveïdor", - "removeProvider": "Elimina el proveïdor", - "providerType": "Proveïdor", - "providerLabel": "Nom", - "providerLabelPlaceholder": "Per exemple: Ollama local", - "baseUrl": "Adreça del servidor", - "apiKey": "Clau API", - "defaultModel": "Model per defecte", - "defaultModelPlaceholder": "Per exemple: llama3.1 o claude-opus-5", - "labelRequired": "Doneu un nom a aquest proveïdor", - "providerSaveFailed": "No s'ha pogut desar el proveïdor", - "providerUpdated": "Proveïdor actualitzat", - "providerDeleteFailed": "No s'ha pogut eliminar el proveïdor", - "privateEndpointHint": "Les adreces de la vostra xarxa privada necessiten primer que un administrador permeti l'accés a l'amfitrió.", - "providerOllama": "Ollama", - "providerAnthropic": "Antròpic", - "providerOpenai": "OpenAI", - "providerGemini": "Google Bessons", - "providerOpenaiCompatible": "Compatible amb OpenAI", - "toolArguments": "Entrada", - "toolResult": "Resultat", - "statusApplied": "Aplicat", - "statusRejected": "Rebutjat", - "proposalApplied": "Canvi aplicat", - "proposalApplyFailed": "No s'ha pogut aplicar el canvi", - "proposalRejectFailed": "No s'ha pogut rebutjar el canvi", - "enableTitle": "Activa l'assistent d'IA", - "enableDescription": "Deixa que l'assistent llegeixi la teva configuració i et suggereixi canvis perquè els aprovis.", - "readOnlyCommands": "Permetre ordres de diagnòstic de només lectura", - "readOnlyCommandsDescription": "L'assistent pot executar una llista curta d'ordres segures com ara df i uptime sense preguntar cada vegada. Qualsevol cosa que canviï un servidor sempre es proposa primer.", - "modelDetectFailed": "No s'ha pogut contactar amb el proveïdor, per tant, aquesta és una llista general. Podeu escriure qualsevol nom de model.", - "modelCustom": "Introduïu un nom de model", - "modelRefresh": "Actualitza la llista", - "modelPlaceholder": "Trieu o escriviu un model", - "attachHint": "Escriviu @ per fer referència a un host, fragment o automatització. Es proposen canvis perquè els aprovi.", - "runCommandTitle": "Executa l'ordre", - "assistant": "Assistent d'IA", - "terminalPanelEmpty": "Pregunta a l'assistent sobre aquest amfitrió. Qualsevol cosa que canviï d'estat necessita primer la teva aprovació.", - "terminalPanelPlaceholder": "Pregunta per aquest amfitrió...", - "noProvidersConfigured": "Encara no hi ha cap proveïdor d'IA configurat. Afegeix-ne un a la pestanya Assistent d'IA.", - "runsInOpenTerminal": "S'executa a la sessió de terminal oberta.", - "runInTerminal": "Corre", - "terminalContextPrefill": "Aquí teniu la sortida del meu terminal:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Permet que altres funcions utilitzin la clau de l'API del proveïdor d'IA per a les seves pròpies sol·licituds. La clau no es copia mai i pots revocar-la en qualsevol moment." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "Assistent d'IA", - "aiIntro": "Termix pot incloure un assistent d'IA opcional. Pots canviar-ho més tard a la configuració.", - "aiSkipTitle": "Deixa-ho fora", - "aiSkipDesc": "Termix funciona com ho fa ara. L'assistent roman ocult.", - "aiEnableTitle": "Engega-ho", - "aiEnableDesc": "Afegeix un assistent que llegeix la configuració i suggereix els canvis que aproveu. Necessita un proveïdor que subministreu, com ara un Ollama local.", - "aiNote": "No s'envia res enlloc fins que no afegiu un proveïdor i envieu un missatge." - }, - "hosts": { - "docsLink": "Veure documents" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/cs_CZ.json b/plugins/ai/locales/translated/cs_CZ.json deleted file mode 100644 index 2986ae373..000000000 --- a/plugins/ai/locales/translated/cs_CZ.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Použijte asistenta", - "description": "Otevřete asistenta a zeptejte se ho na něco." - }, - "manage_providers": { - "title": "Spravovat poskytovatele", - "description": "Přidávejte, upravujte a odebírejte poskytovatele umělé inteligence a jejich klíče API." - }, - "apply_proposals": { - "title": "Použít návrhy", - "description": "Nechte asistenta provést změny, které navrhuje." - }, - "services.use": { - "title": "Používejte umělou inteligenci z jiných funkcí", - "description": "Jiné funkce mohou zavolat asistenta za vás." - }, - "secrets.share": { - "title": "Sdílení klíče API", - "description": "Jiné funkce mohou používat uložený klíč API AI, aniž by ho viděly." - } - }, - "settings": { - "assistantUnavailable": "Asistent s umělou inteligencí je na tomto serveru vypnutý.", - "providersNeedOptIn": "Zapněte výše uvedeného asistenta s umělou inteligencí a přidejte poskytovatele.", - "admin": { - "globallyEnabled": { - "label": "Asistent s umělou inteligencí", - "description": "Umožněte uživatelům zapnout asistenta s umělou inteligencí. Pokud je tato funkce vypnutá, asistent je skrytý a blokovaný pro všechny." - }, - "privateEndpoints": { - "label": "Povolení soukromí hostitelé s umělou inteligencí", - "description": "Hostitelé ve vaší privátní síti, na které mohou uživatelé odkazovat poskytovatele, například Ollama hostovaná na vlastním serveru. Na každý řádek uveďte jednoho hostitele." - } - }, - "host": { - "enableAiAssistant": { - "label": "Povolit asistenta s umělou inteligencí", - "description": "Povolit asistentovi s umělou inteligencí pomáhat v terminálu tohoto hostitele. Ve výchozím nastavení vypnuto, ale stále vyžaduje, aby byla pro váš účet povolena umělá inteligence." - } - } - }, - "ai": { - "title": "Asistent", - "send": "Poslat", - "stop": "Zastávka", - "save": "Uložit", - "cancel": "Zrušit", - "running": "běh", - "approve": "Schvalovat", - "reject": "Odmítnout", - "inputPlaceholder": "Zeptejte se na své servery nebo popište změnu, kterou chcete navrhnout", - "selectProvider": "Vyberte poskytovatele", - "providerSettings": "Nastavení poskytovatele", - "addProvider": "Přidat poskytovatele", - "editProvider": "Upravit poskytovatele", - "removeProvider": "Odebrat poskytovatele", - "providerType": "Poskytovatel", - "providerLabel": "Jméno", - "providerLabelPlaceholder": "Například: Místní Ollama", - "baseUrl": "Adresa serveru", - "apiKey": "Klíč API", - "defaultModel": "Výchozí model", - "defaultModelPlaceholder": "Například: llama3.1 nebo claude-opus-5", - "labelRequired": "Zadejte tomuto poskytovateli název", - "providerSaveFailed": "Nepodařilo se uložit poskytovatele", - "providerUpdated": "Poskytovatel aktualizován", - "providerDeleteFailed": "Nepodařilo se odebrat poskytovatele", - "privateEndpointHint": "Adresy ve vaší privátní síti vyžadují, aby je správce nejprve povolil hostiteli.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropický", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Kompatibilní s OpenAI", - "toolArguments": "Vstup", - "toolResult": "Výsledek", - "statusApplied": "Aplikovaný", - "statusRejected": "Odmítnuto", - "proposalApplied": "Změna použita", - "proposalApplyFailed": "Změnu se nepodařilo použít", - "proposalRejectFailed": "Změnu se nepodařilo odmítnout", - "enableTitle": "Povolte asistenta s umělou inteligencí", - "enableDescription": "Nechte asistenta přečíst si vaše nastavení a navrhnout změny ke schválení.", - "readOnlyCommands": "Povolit diagnostické příkazy pouze pro čtení", - "readOnlyCommandsDescription": "Asistent může spustit krátký seznam bezpečných příkazů, jako například df a uptime, aniž by se pokaždé ptal. Vše, co mění server, je vždy navrženo jako první.", - "modelDetectFailed": "Nepodařilo se mi spojit s poskytovatelem, toto je obecný seznam. Můžete zadat libovolný název modelu.", - "modelCustom": "Zadejte název modelu", - "modelRefresh": "Obnovit seznam", - "modelPlaceholder": "Vyberte nebo zadejte model", - "attachHint": "Zadejte @ pro odkaz na hostitele, úryvek kódu nebo automatizaci. Změny jsou navrženy ke schválení.", - "runCommandTitle": "Spustit příkaz", - "assistant": "Asistent umělé inteligence", - "terminalPanelEmpty": "Zeptejte se asistenta na tohoto hostitele. Jakákoli změna stavu vyžaduje váš první souhlas.", - "terminalPanelPlaceholder": "Zeptejte se na tohoto hostitele...", - "noProvidersConfigured": "Zatím není nakonfigurován žádný poskytovatel AI. Přidejte ho na kartě Asistent AI.", - "runsInOpenTerminal": "Spustí se ve vaší otevřené terminálové relaci.", - "runInTerminal": "Běh", - "terminalContextPrefill": "Zde je můj terminálový výstup:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Umožněte ostatním funkcím používat váš klíč API poskytovatele AI pro jejich vlastní požadavky. Klíč se nikdy nekopíruje a jeho použití můžete kdykoli zrušit." - } - }, - "nav": { - "ai": "Umělá inteligence" - }, - "onboarding": { - "aiTitle": "Asistent s umělou inteligencí", - "aiIntro": "Termix může obsahovat volitelného asistenta s umělou inteligencí. Toto můžete později změnit v nastavení.", - "aiSkipTitle": "Nech to být", - "aiSkipDesc": "Termix funguje stejně jako teď. Asistent zůstává skrytý.", - "aiEnableTitle": "Zapněte to", - "aiEnableDesc": "Přidá asistenta, který si přečte vaše nastavení a navrhne změny, které schválíte. Vyžaduje poskytovatele, kterého dodáte, například místní Ollamu.", - "aiNote": "Nic se nikam neodesílá, dokud nepřidáte poskytovatele a neodešlete zprávu." - }, - "hosts": { - "docsLink": "Zobrazit dokumenty" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/da_DK.json b/plugins/ai/locales/translated/da_DK.json deleted file mode 100644 index f8a25b25b..000000000 --- a/plugins/ai/locales/translated/da_DK.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Brug assistenten", - "description": "Åbn assistenten og stil den spørgsmål." - }, - "manage_providers": { - "title": "Administrer udbydere", - "description": "Tilføj, rediger og fjern AI-udbyderne og deres API-nøgler." - }, - "apply_proposals": { - "title": "Anvend forslag", - "description": "Lad assistenten udføre de foreslåede ændringer." - }, - "services.use": { - "title": "Brug AI fra andre funktioner", - "description": "Andre funktioner kan ringe til assistenten på dine vegne." - }, - "secrets.share": { - "title": "Del API-nøglen", - "description": "Andre funktioner kan bruge den gemte AI API-nøgle uden at se den." - } - }, - "settings": { - "assistantUnavailable": "AI-assistenten er slået fra på denne server.", - "providersNeedOptIn": "Slå AI-assistenten ovenfor til for at tilføje udbydere.", - "admin": { - "globallyEnabled": { - "label": "AI-assistent", - "description": "Lad brugerne aktivere AI-assistenten. Når dette er slået fra, er assistenten skjult og blokeret for alle." - }, - "privateEndpoints": { - "label": "Tilladte private AI-værter", - "description": "Værter på dit private netværk, som brugerne kan henvise en udbyder til, f.eks. en selvhostet Ollama. Sæt én vært på hver linje." - } - }, - "host": { - "enableAiAssistant": { - "label": "Aktivér AI-assistent", - "description": "Tillad AI-assistenten at hjælpe i denne værts terminal. Deaktiveret som standard, og kræver stadig, at AI er aktiveret for din konto." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Sende", - "stop": "Stop", - "save": "Spare", - "cancel": "Ophæve", - "running": "løb", - "approve": "Godkende", - "reject": "Afvise", - "inputPlaceholder": "Spørg om dine servere, eller beskriv en ændring, du vil foreslå", - "selectProvider": "Vælg en udbyder", - "providerSettings": "Udbyderindstillinger", - "addProvider": "Tilføj udbyder", - "editProvider": "Rediger udbyder", - "removeProvider": "Fjern udbyder", - "providerType": "Udbyder", - "providerLabel": "Navn", - "providerLabelPlaceholder": "For eksempel: Lokal Ollama", - "baseUrl": "Serveradresse", - "apiKey": "API-nøgle", - "defaultModel": "Standardmodel", - "defaultModelPlaceholder": "For eksempel: llama3.1 eller claude-opus-5", - "labelRequired": "Giv denne udbyder et navn", - "providerSaveFailed": "Udbyderen kunne ikke gemmes", - "providerUpdated": "Udbyder opdateret", - "providerDeleteFailed": "Udbyderen kunne ikke fjernes", - "privateEndpointHint": "Adresser på dit private netværk kræver først tilladelse fra en administrator til værten.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropisk", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-kompatibel", - "toolArguments": "Input", - "toolResult": "Resultat", - "statusApplied": "Anvendt", - "statusRejected": "Afvist", - "proposalApplied": "Ændring anvendt", - "proposalApplyFailed": "Ændringen kunne ikke anvendes", - "proposalRejectFailed": "Kunne ikke afvise ændringen", - "enableTitle": "Aktivér AI-assistenten", - "enableDescription": "Lad assistenten læse din opsætning og foreslå ændringer, som du kan godkende.", - "readOnlyCommands": "Tillad skrivebeskyttede diagnosticeringskommandoer", - "readOnlyCommandsDescription": "Assistenten kan køre en kort liste med sikre kommandoer som f.eks. df og uptime uden at spørge hver gang. Alt, der ændrer en server, foreslås altid først.", - "modelDetectFailed": "Kunne ikke få kontakt med udbyderen, så dette er en generel liste. Du kan skrive et hvilket som helst modelnavn.", - "modelCustom": "Indtast et modelnavn", - "modelRefresh": "Opdater listen", - "modelPlaceholder": "Vælg eller skriv en model", - "attachHint": "Skriv @ for at referere til en vært, et kodestykke eller en automatisering. Ændringer foreslås, som du skal godkende.", - "runCommandTitle": "Kør kommando", - "assistant": "AI-assistent", - "terminalPanelEmpty": "Spørg assistenten om denne vært. Alt, der ændrer status, kræver din godkendelse først.", - "terminalPanelPlaceholder": "Spørg om denne vært...", - "noProvidersConfigured": "Der er endnu ikke konfigureret nogen AI-udbyder. Tilføj en i fanen AI-assistent.", - "runsInOpenTerminal": "Kører i din åbne terminalsession.", - "runInTerminal": "Løbe", - "terminalContextPrefill": "Her er mit terminaloutput:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Lad andre funktioner bruge din AI-udbyders API-nøgle til deres egne anmodninger. Nøglen kopieres aldrig, og du kan til enhver tid tilbagekalde den." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI-assistent", - "aiIntro": "Termix kan inkludere en valgfri AI-assistent. Du kan ændre dette senere i dine indstillinger.", - "aiSkipTitle": "Lad det være slukket", - "aiSkipDesc": "Termix fungerer som den gør nu. Assistenten forbliver skjult.", - "aiEnableTitle": "Tænd den", - "aiEnableDesc": "Tilføjer en assistent, der læser din opsætning og foreslår ændringer, du godkender. Kræver en udbyder, du angiver, f.eks. en lokal Ollama.", - "aiNote": "Intet sendes nogen steder, før du tilføjer en udbyder og sender en besked." - }, - "hosts": { - "docsLink": "Se dokumenter" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/de_DE.json b/plugins/ai/locales/translated/de_DE.json deleted file mode 100644 index a540e4274..000000000 --- a/plugins/ai/locales/translated/de_DE.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Nutzen Sie den Assistenten", - "description": "Öffne den Assistenten und stelle ihm Fragen." - }, - "manage_providers": { - "title": "Anbieter verwalten", - "description": "KI-Anbieter und ihre API-Schlüssel hinzufügen, bearbeiten und entfernen." - }, - "apply_proposals": { - "title": "Vorschläge einreichen", - "description": "Lassen Sie den Assistenten die von ihm vorgeschlagenen Änderungen durchführen." - }, - "services.use": { - "title": "Nutzen Sie KI aus anderen Funktionen", - "description": "Andere Funktionen rufen möglicherweise in Ihrem Namen den Assistenten auf." - }, - "secrets.share": { - "title": "Teilen Sie den API-Schlüssel", - "description": "Andere Funktionen verwenden möglicherweise den gespeicherten KI-API-Schlüssel, ohne ihn anzuzeigen." - } - }, - "settings": { - "assistantUnavailable": "Der KI-Assistent ist auf diesem Server deaktiviert.", - "providersNeedOptIn": "Aktivieren Sie den obenstehenden KI-Assistenten, um Anbieter hinzuzufügen.", - "admin": { - "globallyEnabled": { - "label": "KI-Assistent", - "description": "Ermöglichen Sie den Nutzern, den KI-Assistenten zu aktivieren. Solange diese Option deaktiviert ist, ist der Assistent für alle ausgeblendet und gesperrt." - }, - "privateEndpoints": { - "label": "Zulässige private KI-Hosts", - "description": "Hosts in Ihrem privaten Netzwerk, auf die Benutzer einen Provider verweisen können, wie z. B. ein selbst gehostetes Ollama. Fügen Sie in jede Zeile einen Host ein." - } - }, - "host": { - "enableAiAssistant": { - "label": "KI-Assistent aktivieren", - "description": "Erlauben Sie dem KI-Assistenten, im Terminal dieses Hosts Hilfe zu leisten. Standardmäßig deaktiviert; die KI muss weiterhin für Ihr Konto aktiviert sein." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Schicken", - "stop": "Stoppen", - "save": "Speichern", - "cancel": "Abbrechen", - "running": "laufend", - "approve": "Genehmigen", - "reject": "Ablehnen", - "inputPlaceholder": "Erkundigen Sie sich nach Ihren Servern oder beschreiben Sie einen Änderungsvorschlag.", - "selectProvider": "Wählen Sie einen Anbieter aus", - "providerSettings": "Anbietereinstellungen", - "addProvider": "Anbieter hinzufügen", - "editProvider": "Anbieter bearbeiten", - "removeProvider": "Anbieter entfernen", - "providerType": "Anbieter", - "providerLabel": "Name", - "providerLabelPlaceholder": "Zum Beispiel: Lokaler Ollama", - "baseUrl": "Serveradresse", - "apiKey": "API-Schlüssel", - "defaultModel": "Standardmodell", - "defaultModelPlaceholder": "Zum Beispiel: llama3.1 oder claude-opus-5", - "labelRequired": "Geben Sie diesem Anbieter einen Namen", - "providerSaveFailed": "Der Anbieter konnte nicht gespeichert werden.", - "providerUpdated": "Anbieter aktualisiert", - "providerDeleteFailed": "Der Anbieter konnte nicht entfernt werden.", - "privateEndpointHint": "Adressen in Ihrem privaten Netzwerk benötigen eine vorherige Genehmigung durch einen Administrator.", - "providerOllama": "Ollama", - "providerAnthropic": "Anthropisch", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-kompatibel", - "toolArguments": "Eingabe", - "toolResult": "Ergebnis", - "statusApplied": "Angewandt", - "statusRejected": "Abgelehnt", - "proposalApplied": "Änderung angewendet", - "proposalApplyFailed": "Die Änderung konnte nicht angewendet werden.", - "proposalRejectFailed": "Die Änderung konnte nicht abgelehnt werden.", - "enableTitle": "Aktivieren Sie den KI-Assistenten", - "enableDescription": "Lassen Sie den Assistenten Ihre Einstellungen lesen und Ihnen Änderungen zur Genehmigung vorschlagen.", - "readOnlyCommands": "Zulassen von schreibgeschützten Diagnosebefehlen", - "readOnlyCommandsDescription": "Der Assistent kann eine kurze Liste sicherer Befehle wie df und uptime ausführen, ohne jedes Mal nachzufragen. Alle Änderungen, die einen Server betreffen, werden immer zuerst vorgeschlagen.", - "modelDetectFailed": "Der Anbieter konnte nicht erreicht werden, daher handelt es sich um eine allgemeine Liste. Sie können einen beliebigen Modellnamen eingeben.", - "modelCustom": "Geben Sie einen Modellnamen ein", - "modelRefresh": "Liste aktualisieren", - "modelPlaceholder": "Wählen Sie ein Modell aus oder geben Sie es ein.", - "attachHint": "Geben Sie @ ein, um auf einen Host, ein Code-Snippet oder eine Automatisierung zu verweisen. Die Änderungen werden Ihnen zur Genehmigung vorgeschlagen.", - "runCommandTitle": "Befehl ausführen", - "assistant": "KI-Assistent", - "terminalPanelEmpty": "Fragen Sie den Assistenten nach diesem Gastgeber. Jede Statusänderung bedarf Ihrer vorherigen Zustimmung.", - "terminalPanelPlaceholder": "Erkundigen Sie sich nach diesem Gastgeber...", - "noProvidersConfigured": "Es ist noch kein KI-Anbieter konfiguriert. Fügen Sie einen im Tab „KI-Assistent“ hinzu.", - "runsInOpenTerminal": "Wird in Ihrer geöffneten Terminal-Sitzung ausgeführt.", - "runInTerminal": "Ausführen", - "terminalContextPrefill": "Hier ist meine Terminalausgabe:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Erlauben Sie anderen Funktionen, Ihren API-Schlüssel Ihres KI-Anbieters für ihre eigenen Anfragen zu verwenden. Der Schlüssel wird niemals kopiert, und Sie können diese Berechtigung jederzeit widerrufen." - } - }, - "nav": { - "ai": "KI" - }, - "onboarding": { - "aiTitle": "KI-Assistent", - "aiIntro": "Termix kann optional einen KI-Assistenten beinhalten. Sie können dies später in Ihren Einstellungen ändern.", - "aiSkipTitle": "Lass es weg", - "aiSkipDesc": "Termix funktioniert wie bisher. Der Assistent bleibt verborgen.", - "aiEnableTitle": "Schalte es ein", - "aiEnableDesc": "Fügt einen Assistenten hinzu, der Ihre Einstellungen analysiert und Ihnen von Ihnen genehmigte Änderungen vorschlägt. Benötigt einen von Ihnen angegebenen Anbieter, z. B. einen lokalen Ollama-Händler.", - "aiNote": "Es wird nichts gesendet, bis Sie einen Anbieter hinzufügen und eine Nachricht senden." - }, - "hosts": { - "docsLink": "Dokumentation anzeigen" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/el_GR.json b/plugins/ai/locales/translated/el_GR.json deleted file mode 100644 index fc1018c41..000000000 --- a/plugins/ai/locales/translated/el_GR.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Χρησιμοποιήστε τον βοηθό", - "description": "Άνοιξε τον βοηθό και ρώτα τον πράγματα." - }, - "manage_providers": { - "title": "Διαχείριση παρόχων", - "description": "Προσθήκη, επεξεργασία και κατάργηση των παρόχων τεχνητής νοημοσύνης και των κλειδιών API τους." - }, - "apply_proposals": { - "title": "Εφαρμογή προτάσεων", - "description": "Αφήστε τον βοηθό να εκτελέσει τις αλλαγές που προτείνει." - }, - "services.use": { - "title": "Χρησιμοποιήστε την Τεχνητή Νοημοσύνη από άλλες λειτουργίες", - "description": "Άλλες λειτουργίες ενδέχεται να καλούν τον βοηθό εκ μέρους σας." - }, - "secrets.share": { - "title": "Κοινή χρήση του κλειδιού API", - "description": "Άλλες λειτουργίες ενδέχεται να χρησιμοποιούν το αποθηκευμένο κλειδί API AI χωρίς να το βλέπουν." - } - }, - "settings": { - "assistantUnavailable": "Ο βοηθός τεχνητής νοημοσύνης είναι απενεργοποιημένος σε αυτόν τον διακομιστή.", - "providersNeedOptIn": "Ενεργοποιήστε τον βοηθό τεχνητής νοημοσύνης παραπάνω για να προσθέσετε παρόχους.", - "admin": { - "globallyEnabled": { - "label": "Βοηθός Τεχνητής Νοημοσύνης", - "description": "Επιτρέψτε στους χρήστες να ενεργοποιήσουν τον βοηθό τεχνητής νοημοσύνης. Όσο είναι απενεργοποιημένος, ο βοηθός είναι κρυφός και αποκλεισμένος για όλους." - }, - "privateEndpoints": { - "label": "Επιτρεπόμενοι ιδιωτικοί κεντρικοί υπολογιστές τεχνητής νοημοσύνης", - "description": "Κεντρικοί υπολογιστές στο ιδιωτικό σας δίκτυο στους οποίους οι χρήστες μπορούν να υποδείξουν έναν πάροχο, όπως ένα αυτο-φιλοξενούμενο Ollama. Τοποθετήστε έναν κεντρικό υπολογιστή σε κάθε γραμμή." - } - }, - "host": { - "enableAiAssistant": { - "label": "Ενεργοποίηση Βοηθού Τεχνητής Νοημοσύνης", - "description": "Επιτρέψτε στον βοηθό τεχνητής νοημοσύνης να βοηθά στο τερματικό αυτού του κεντρικού υπολογιστή. Απενεργοποιημένο από προεπιλογή και εξακολουθεί να χρειάζεται ενεργοποιημένη την τεχνητή νοημοσύνη για τον λογαριασμό σας." - } - } - }, - "ai": { - "title": "Βοηθός", - "send": "Στέλνω", - "stop": "Στάση", - "save": "Εκτός", - "cancel": "Ματαίωση", - "running": "τρέξιμο", - "approve": "Εγκρίνω", - "reject": "Απορρίπτω", - "inputPlaceholder": "Ρωτήστε για τους διακομιστές σας ή περιγράψτε μια αλλαγή που προτείνετε", - "selectProvider": "Επιλέξτε έναν πάροχο", - "providerSettings": "Ρυθμίσεις παρόχου", - "addProvider": "Προσθήκη παρόχου", - "editProvider": "Επεξεργασία παρόχου", - "removeProvider": "Κατάργηση παρόχου", - "providerType": "Προμηθευτής", - "providerLabel": "Ονομα", - "providerLabelPlaceholder": "Για παράδειγμα: Τοπικό Ollama", - "baseUrl": "Διεύθυνση διακομιστή", - "apiKey": "Κλειδί API", - "defaultModel": "Προεπιλεγμένο μοντέλο", - "defaultModelPlaceholder": "Για παράδειγμα: llama3.1 ή claude-opus-5", - "labelRequired": "Δώστε σε αυτόν τον πάροχο ένα όνομα", - "providerSaveFailed": "Δεν ήταν δυνατή η αποθήκευση του παρόχου", - "providerUpdated": "Ο πάροχος ενημερώθηκε", - "providerDeleteFailed": "Δεν ήταν δυνατή η κατάργηση του παρόχου", - "privateEndpointHint": "Οι διευθύνσεις στο ιδιωτικό σας δίκτυο χρειάζονται πρώτα έναν διαχειριστή για να επιτρέψει στον κεντρικό υπολογιστή.", - "providerOllama": "Ολάμα", - "providerAnthropic": "Ανθρώπινο", - "providerOpenai": "Ανοιχτή Τεχνητή Νοημοσύνη", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Συμβατό με OpenAI", - "toolArguments": "Εισαγωγή", - "toolResult": "Αποτέλεσμα", - "statusApplied": "Εφαρμοσμένος", - "statusRejected": "Απορρίφθηκε", - "proposalApplied": "Η αλλαγή εφαρμόστηκε", - "proposalApplyFailed": "Δεν ήταν δυνατή η εφαρμογή της αλλαγής", - "proposalRejectFailed": "Δεν ήταν δυνατή η απόρριψη της αλλαγής", - "enableTitle": "Ενεργοποίηση του βοηθού τεχνητής νοημοσύνης", - "enableDescription": "Αφήστε τον βοηθό να διαβάσει τη ρύθμισή σας και να σας προτείνει αλλαγές για έγκριση.", - "readOnlyCommands": "Να επιτρέπονται διαγνωστικές εντολές μόνο για ανάγνωση", - "readOnlyCommandsDescription": "Ο βοηθός μπορεί να εκτελέσει μια σύντομη λίστα ασφαλών εντολών όπως df και uptime χωρίς να ρωτάει κάθε φορά. Οτιδήποτε αλλάζει έναν διακομιστή προτείνεται πάντα πρώτο.", - "modelDetectFailed": "Δεν ήταν δυνατή η επικοινωνία με τον πάροχο, επομένως αυτή είναι μια γενική λίστα. Μπορείτε να πληκτρολογήσετε οποιοδήποτε όνομα μοντέλου.", - "modelCustom": "Εισαγάγετε ένα όνομα μοντέλου", - "modelRefresh": "Ανανέωση λίστας", - "modelPlaceholder": "Επιλέξτε ή πληκτρολογήστε ένα μοντέλο", - "attachHint": "Πληκτρολογήστε @ για να αναφέρετε έναν κεντρικό υπολογιστή, ένα απόσπασμα κώδικα ή έναν αυτοματισμό. Προτείνονται αλλαγές για έγκριση.", - "runCommandTitle": "Εκτέλεση εντολής", - "assistant": "Βοηθός Τεχνητής Νοημοσύνης", - "terminalPanelEmpty": "Ρωτήστε τον βοηθό σχετικά με αυτόν τον οικοδεσπότη. Οτιδήποτε αλλάζει κατάσταση χρειάζεται πρώτα την έγκρισή σας.", - "terminalPanelPlaceholder": "Ρωτήστε για αυτόν τον οικοδεσπότη...", - "noProvidersConfigured": "Δεν έχει διαμορφωθεί ακόμη πάροχος τεχνητής νοημοσύνης. Προσθέστε έναν στην καρτέλα βοηθού τεχνητής νοημοσύνης.", - "runsInOpenTerminal": "Εκτελείται στην ανοιχτή συνεδρία τερματικού σας.", - "runInTerminal": "Τρέξιμο", - "terminalContextPrefill": "Εδώ είναι η έξοδος του τερματικού μου:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Επιτρέψτε σε άλλες λειτουργίες να χρησιμοποιούν το κλειδί API του παρόχου τεχνητής νοημοσύνης για τα δικά τους αιτήματα. Το κλειδί δεν αντιγράφεται ποτέ και μπορείτε να το ανακαλέσετε ανά πάσα στιγμή." - } - }, - "nav": { - "ai": "Όλα συμπεριλαμβάνονται" - }, - "onboarding": { - "aiTitle": "Βοηθός Τεχνητής Νοημοσύνης", - "aiIntro": "Το Termix μπορεί να περιλαμβάνει έναν προαιρετικό βοηθό τεχνητής νοημοσύνης. Μπορείτε να το αλλάξετε αυτό αργότερα στις ρυθμίσεις σας.", - "aiSkipTitle": "Αφήστε το μακριά", - "aiSkipDesc": "Το Termix λειτουργεί όπως τώρα. Ο βοηθός παραμένει κρυφός.", - "aiEnableTitle": "Ενεργοποίησέ το", - "aiEnableDesc": "Προσθέτει έναν βοηθό που διαβάζει τη ρύθμισή σας και προτείνει αλλαγές που εγκρίνετε. Χρειάζεται έναν πάροχο που παρέχετε, όπως ένα τοπικό Ollama.", - "aiNote": "Τίποτα δεν αποστέλλεται πουθενά μέχρι να προσθέσετε έναν πάροχο και να στείλετε ένα μήνυμα." - }, - "hosts": { - "docsLink": "Προβολή εγγράφων" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/es_ES.json b/plugins/ai/locales/translated/es_ES.json deleted file mode 100644 index a03f2f226..000000000 --- a/plugins/ai/locales/translated/es_ES.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Utilice el asistente", - "description": "Abre el asistente y hazle preguntas." - }, - "manage_providers": { - "title": "Gestionar proveedores", - "description": "Agregue, edite y elimine los proveedores de IA y sus claves API." - }, - "apply_proposals": { - "title": "Presentar propuestas", - "description": "Deja que el asistente realice los cambios que sugiere." - }, - "services.use": { - "title": "Utilizar la IA de otras funciones", - "description": "Otras funciones pueden llamar al asistente en tu nombre." - }, - "secrets.share": { - "title": "Comparta la clave API", - "description": "Es posible que otras funciones utilicen la clave API de IA almacenada sin tenerla en cuenta." - } - }, - "settings": { - "assistantUnavailable": "El asistente de IA está desactivado en este servidor.", - "providersNeedOptIn": "Activa el asistente de IA que aparece arriba para añadir proveedores.", - "admin": { - "globallyEnabled": { - "label": "Asistente de IA", - "description": "Permita que los usuarios activen el asistente de IA. Mientras esté desactivado, el asistente permanecerá oculto y bloqueado para todos." - }, - "privateEndpoints": { - "label": "Hosts de IA privados permitidos", - "description": "Hosts en tu red privada a los que los usuarios pueden dirigir un proveedor, como por ejemplo un servidor Ollama autogestionado. Coloca un host en cada línea." - } - }, - "host": { - "enableAiAssistant": { - "label": "Habilitar el asistente de IA", - "description": "Permite que el asistente de IA te ayude en la terminal de este host. Está desactivado por defecto y aún así necesitas tener la IA habilitada para tu cuenta." - } - } - }, - "ai": { - "title": "Asistente", - "send": "Enviar", - "stop": "Parada", - "save": "Guardar", - "cancel": "Cancelar", - "running": "en ejecución", - "approve": "Aprobar", - "reject": "Rechazar", - "inputPlaceholder": "Pregunte sobre sus servidores o describa un cambio para proponer.", - "selectProvider": "Seleccione un proveedor", - "providerSettings": "Configuración del proveedor", - "addProvider": "Agregar proveedor", - "editProvider": "Editar proveedor", - "removeProvider": "Eliminar proveedor", - "providerType": "Proveedor", - "providerLabel": "Nombre", - "providerLabelPlaceholder": "Por ejemplo: Ollama local", - "baseUrl": "Dirección del servidor", - "apiKey": "clave API", - "defaultModel": "Modelo predeterminado", - "defaultModelPlaceholder": "Por ejemplo: llama3.1 o claude-opus-5", - "labelRequired": "Dale un nombre a este proveedor.", - "providerSaveFailed": "No se pudo guardar el proveedor", - "providerUpdated": "Proveedor actualizado", - "providerDeleteFailed": "No se pudo eliminar al proveedor.", - "privateEndpointHint": "Las direcciones de su red privada necesitan que un administrador autorice el acceso al host previamente.", - "providerOllama": "Ollama", - "providerAnthropic": "Antrópico", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Compatible con OpenAI", - "toolArguments": "Entrada", - "toolResult": "Resultado", - "statusApplied": "Aplicado", - "statusRejected": "Rechazado", - "proposalApplied": "Cambio aplicado", - "proposalApplyFailed": "No se pudo aplicar el cambio.", - "proposalRejectFailed": "No se pudo rechazar el cambio", - "enableTitle": "Habilitar el asistente de IA", - "enableDescription": "Deja que el asistente lea tu configuración y te sugiera cambios para que los apruebes.", - "readOnlyCommands": "Permitir comandos de diagnóstico de solo lectura", - "readOnlyCommandsDescription": "El asistente puede ejecutar una breve lista de comandos seguros, como df y uptime, sin preguntar cada vez. Cualquier cambio que afecte al servidor siempre se propone primero.", - "modelDetectFailed": "No se pudo contactar con el proveedor, por lo que esta es una lista general. Puede escribir cualquier nombre de modelo.", - "modelCustom": "Introduzca un nombre de modelo", - "modelRefresh": "Actualizar lista", - "modelPlaceholder": "Seleccione o escriba un modelo.", - "attachHint": "Escribe @ para hacer referencia a un host, fragmento de código o automatización. Se proponen cambios para su aprobación.", - "runCommandTitle": "Ejecutar comando", - "assistant": "Asistente de IA", - "terminalPanelEmpty": "Consulta con el asistente sobre este anfitrión. Cualquier cambio de estado requiere tu aprobación previa.", - "terminalPanelPlaceholder": "Pregunta por este anfitrión...", - "noProvidersConfigured": "Aún no hay ningún proveedor de IA configurado. Añada uno en la pestaña del asistente de IA.", - "runsInOpenTerminal": "Se ejecuta en tu sesión de terminal abierta.", - "runInTerminal": "Ejecutar", - "terminalContextPrefill": "Aquí está la salida de mi terminal:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Permite que otras funciones utilicen la clave API de tu proveedor de IA para sus propias solicitudes. La clave nunca se copia y puedes revocarla en cualquier momento." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "Asistente de IA", - "aiIntro": "Termix puede incluir un asistente de IA opcional. Puedes cambiar esto más adelante en la configuración.", - "aiSkipTitle": "Déjalo apagado", - "aiSkipDesc": "Termix funciona como hasta ahora. El asistente permanece oculto.", - "aiEnableTitle": "Enciéndelo", - "aiEnableDesc": "Agrega un asistente que lee tu configuración y sugiere cambios que apruebas. Requiere un proveedor que tú proporciones, como un Ollama local.", - "aiNote": "No se envía nada a ningún sitio hasta que añadas un proveedor y envíes un mensaje." - }, - "hosts": { - "docsLink": "Ver documentación" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/fi_FI.json b/plugins/ai/locales/translated/fi_FI.json deleted file mode 100644 index 015a13ac9..000000000 --- a/plugins/ai/locales/translated/fi_FI.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Käytä avustajaa", - "description": "Avaa avustaja ja kysy siltä asioita." - }, - "manage_providers": { - "title": "Hallinnoi palveluntarjoajia", - "description": "Lisää, muokkaa ja poista tekoälypalveluntarjoajia ja niiden API-avaimia." - }, - "apply_proposals": { - "title": "Käytä ehdotuksia", - "description": "Anna avustajan suorittaa ehdottamansa muutokset." - }, - "services.use": { - "title": "Käytä tekoälyä muista ominaisuuksista", - "description": "Muut ominaisuudet saattavat soittaa avustajalle puolestasi." - }, - "secrets.share": { - "title": "Jaa API-avain", - "description": "Muut ominaisuudet saattavat käyttää tallennettua tekoälyn API-avainta näkemättä sitä." - } - }, - "settings": { - "assistantUnavailable": "Tekoälyavustaja on pois päältä tällä palvelimella.", - "providersNeedOptIn": "Ota yllä oleva tekoälyavustaja käyttöön lisätäksesi palveluntarjoajia.", - "admin": { - "globallyEnabled": { - "label": "Tekoälyavustaja", - "description": "Salli käyttäjien ottaa tekoälyavustaja käyttöön. Kun tämä on pois päältä, avustaja on piilotettu ja estetty kaikilta." - }, - "privateEndpoints": { - "label": "Sallitut yksityiset tekoälyisännät", - "description": "Yksityisen verkon isännät, joihin käyttäjät voivat ohjata palveluntarjoajan, kuten itse isännöity Ollama. Aseta yksi isäntä kullekin riville." - } - }, - "host": { - "enableAiAssistant": { - "label": "Ota tekoälyavustaja käyttöön", - "description": "Salli tekoälyavustaja auttaa tämän isännän terminaalissa. Oletusarvoisesti pois päältä, mutta tekoälyn on silti oltava käytössä tililläsi." - } - } - }, - "ai": { - "title": "Assistentti", - "send": "Lähetä", - "stop": "Stop", - "save": "Tallentaa", - "cancel": "Peruuttaa", - "running": "juokseminen", - "approve": "Hyväksyä", - "reject": "Hylätä", - "inputPlaceholder": "Kysy tarjoilijoistasi tai kuvaile ehdottamaasi muutosta", - "selectProvider": "Valitse palveluntarjoaja", - "providerSettings": "Palveluntarjoajan asetukset", - "addProvider": "Lisää palveluntarjoaja", - "editProvider": "Muokkaa palveluntarjoajaa", - "removeProvider": "Poista palveluntarjoaja", - "providerType": "Palveluntarjoaja", - "providerLabel": "Nimi", - "providerLabelPlaceholder": "Esimerkiksi: Paikallinen Ollama", - "baseUrl": "Palvelimen osoite", - "apiKey": "API-avain", - "defaultModel": "Oletusmalli", - "defaultModelPlaceholder": "Esimerkiksi: llama3.1 tai claude-opus-5", - "labelRequired": "Anna tälle palveluntarjoajalle nimi", - "providerSaveFailed": "Palveluntarjoajaa ei voitu tallentaa", - "providerUpdated": "Palveluntarjoaja päivitetty", - "providerDeleteFailed": "Palveluntarjoajaa ei voitu poistaa", - "privateEndpointHint": "Yksityisen verkon osoitteiden ylläpitäjän on ensin sallittava isäntä.", - "providerOllama": "Ollama", - "providerAnthropic": "Antrooppinen", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-yhteensopiva", - "toolArguments": "Syöttö", - "toolResult": "Tulos", - "statusApplied": "Käytetty", - "statusRejected": "Hylätty", - "proposalApplied": "Muutos otettu käyttöön", - "proposalApplyFailed": "Muutosta ei voitu ottaa käyttöön", - "proposalRejectFailed": "Muutosta ei voitu hylätä", - "enableTitle": "Ota tekoälyavustaja käyttöön", - "enableDescription": "Anna avustajan lukea asetuksesi ja ehdottaa muutoksia hyväksyttäväksi.", - "readOnlyCommands": "Salli vain luettavat diagnostiikkakomennot", - "readOnlyCommandsDescription": "Assistantti voi suorittaa lyhyen listan turvallisia komentoja, kuten df ja uptime, kysymättä joka kerta. Kaikki palvelinta muuttavat ehdotukset esitetään aina ensin.", - "modelDetectFailed": "Palveluntarjoajaan ei saatu yhteyttä, joten tämä on yleisluettelo. Voit kirjoittaa minkä tahansa mallin nimen.", - "modelCustom": "Anna mallin nimi", - "modelRefresh": "Päivitä lista", - "modelPlaceholder": "Valitse tai kirjoita malli", - "attachHint": "Kirjoita @ viitataksesi isäntään, koodinpätkään tai automaatioon. Muutoksia ehdotetaan hyväksyttäväksi.", - "runCommandTitle": "Suorita komento", - "assistant": "Tekoälyavustaja", - "terminalPanelEmpty": "Kysy avustajalta tästä majoittajasta. Kaikki osavaltion muutokset vaativat ensin hyväksyntäsi.", - "terminalPanelPlaceholder": "Kysy tästä isännöitsijästä...", - "noProvidersConfigured": "Tekoälypalveluntarjoajaa ei ole vielä määritetty. Lisää sellainen tekoälyavustaja-välilehdellä.", - "runsInOpenTerminal": "Suoritetaan avoimessa pääteistunnossa.", - "runInTerminal": "Juokse", - "terminalContextPrefill": "Tässä on päätteen tuloste:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Anna muiden ominaisuuksien käyttää tekoälypalveluntarjoajasi API-avainta omiin pyyntöihinsä. Avainta ei koskaan kopioida, ja voit peruuttaa tämän milloin tahansa." - } - }, - "nav": { - "ai": "Tekoäly" - }, - "onboarding": { - "aiTitle": "Tekoälyavustaja", - "aiIntro": "Termix voi sisältää valinnaisen tekoälyavustaja. Voit muuttaa tätä myöhemmin asetuksissasi.", - "aiSkipTitle": "Jätä se pois", - "aiSkipDesc": "Termix toimii kuten nytkin. Avustaja pysyy piilossa.", - "aiEnableTitle": "Käynnistä se", - "aiEnableDesc": "Lisää avustajan, joka lukee asetuksesi ja ehdottaa hyväksymiäsi muutoksia. Edellyttää toimittamaasi palveluntarjoajaa, kuten paikallisen Ollaman.", - "aiNote": "Mitään ei lähetetä mihinkään ennen kuin lisäät palveluntarjoajan ja lähetät viestin." - }, - "hosts": { - "docsLink": "Näytä dokumentit" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/fr_FR.json b/plugins/ai/locales/translated/fr_FR.json deleted file mode 100644 index 2ebefed04..000000000 --- a/plugins/ai/locales/translated/fr_FR.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Utilisez l'assistant", - "description": "Ouvrez l'assistant et posez-lui des questions." - }, - "manage_providers": { - "title": "Gérer les fournisseurs", - "description": "Ajoutez, modifiez et supprimez les fournisseurs d'IA et leurs clés API." - }, - "apply_proposals": { - "title": "Soumettre les propositions", - "description": "Laissez l'assistant effectuer les modifications qu'il suggère." - }, - "services.use": { - "title": "Utiliser l'IA d'autres fonctionnalités", - "description": "D'autres fonctionnalités peuvent appeler l'assistant en votre nom." - }, - "secrets.share": { - "title": "Partagez la clé API", - "description": "D'autres fonctionnalités peuvent utiliser la clé API IA stockée sans la voir." - } - }, - "settings": { - "assistantUnavailable": "L'assistant IA est désactivé sur ce serveur.", - "providersNeedOptIn": "Activez l'assistant IA ci-dessus pour ajouter des fournisseurs.", - "admin": { - "globallyEnabled": { - "label": "Assistant IA", - "description": "Autoriser les utilisateurs à activer l'assistant IA. Lorsqu'il est désactivé, l'assistant est masqué et inaccessible à tous." - }, - "privateEndpoints": { - "label": "Hôtes d'IA privés autorisés", - "description": "Sur votre réseau privé, configurez des hôtes vers lesquels les utilisateurs peuvent orienter un fournisseur, comme un serveur Ollama auto-hébergé. Placez un hôte sur chaque ligne." - } - }, - "host": { - "enableAiAssistant": { - "label": "Activer l'assistant IA", - "description": "Autoriser l'assistant IA à vous aider dans le terminal de cet hôte. Désactivé par défaut ; l'IA doit être activée pour votre compte." - } - } - }, - "ai": { - "title": "Assistant", - "send": "Envoyer", - "stop": "Stop", - "save": "Enregistrer", - "cancel": "Annuler", - "running": "en cours", - "approve": "Approuver", - "reject": "Rejeter", - "inputPlaceholder": "Renseignez-vous sur vos serveurs ou décrivez un changement à proposer.", - "selectProvider": "Sélectionnez un fournisseur", - "providerSettings": "Paramètres du fournisseur", - "addProvider": "Ajouter un fournisseur", - "editProvider": "Modifier le fournisseur", - "removeProvider": "Supprimer le fournisseur", - "providerType": "Fournisseur", - "providerLabel": "Nom", - "providerLabelPlaceholder": "Par exemple : Ollama local", - "baseUrl": "Adresse du serveur", - "apiKey": "Clé API", - "defaultModel": "Modèle par défaut", - "defaultModelPlaceholder": "Par exemple : llama3.1 ou claude-opus-5", - "labelRequired": "Donnez un nom à ce fournisseur", - "providerSaveFailed": "Impossible de sauver le fournisseur", - "providerUpdated": "Mise à jour du fournisseur", - "providerDeleteFailed": "Impossible de supprimer le fournisseur", - "privateEndpointHint": "Les adresses de votre réseau privé nécessitent l'autorisation préalable d'un administrateur pour accéder à l'hôte.", - "providerOllama": "Ollama", - "providerAnthropic": "Anthropique", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Compatible avec OpenAI", - "toolArguments": "Entrée", - "toolResult": "Résultat", - "statusApplied": "Appliqué", - "statusRejected": "Rejeté", - "proposalApplied": "Changement appliqué", - "proposalApplyFailed": "Impossible d'appliquer la modification", - "proposalRejectFailed": "Impossible de refuser la modification", - "enableTitle": "Activer l'assistant IA", - "enableDescription": "Laissez l'assistant analyser votre configuration et vous suggérer des modifications à approuver.", - "readOnlyCommands": "Autoriser les commandes de diagnostic en lecture seule", - "readOnlyCommandsDescription": "L'assistant peut exécuter une courte liste de commandes sûres, comme `df` et `uptime`, sans demander l'autorisation à chaque fois. Toute modification apportée au serveur est toujours proposée au préalable.", - "modelDetectFailed": "Impossible de joindre le fournisseur ; voici donc une liste générale. Vous pouvez saisir n’importe quel nom de modèle.", - "modelCustom": "Saisissez le nom du modèle", - "modelRefresh": "Actualiser la liste", - "modelPlaceholder": "Choisissez ou saisissez un modèle", - "attachHint": "Saisissez @ pour faire référence à un hôte, un extrait de code ou une automatisation. Les modifications proposées doivent être approuvées.", - "runCommandTitle": "Exécuter la commande", - "assistant": "Assistant IA", - "terminalPanelEmpty": "Renseignez-vous auprès de l'assistant concernant cet hôte. Tout changement d'état nécessite votre approbation préalable.", - "terminalPanelPlaceholder": "Renseignez-vous sur cet hôte...", - "noProvidersConfigured": "Aucun fournisseur d'IA n'est encore configuré. Ajoutez-en un dans l'onglet Assistant IA.", - "runsInOpenTerminal": "S'exécute dans votre session de terminal ouverte.", - "runInTerminal": "Exécuter", - "terminalContextPrefill": "Voici la sortie de mon terminal :\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Autorisez d'autres fonctionnalités à utiliser votre clé API de fournisseur d'IA pour leurs propres requêtes. Cette clé n'est jamais copiée et vous pouvez la révoquer à tout moment." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "Assistant IA", - "aiIntro": "Termix peut inclure un assistant IA optionnel. Vous pouvez modifier ce paramètre ultérieurement.", - "aiSkipTitle": "Laissez-le de côté", - "aiSkipDesc": "Termix fonctionne comme actuellement. L'assistant reste caché.", - "aiEnableTitle": "Allumez-le", - "aiEnableDesc": "Ajoute un assistant qui analyse votre configuration et suggère des modifications que vous approuvez. Nécessite un fournisseur que vous désignez, comme un Ollama local.", - "aiNote": "Aucun message n'est envoyé tant que vous n'avez pas ajouté de fournisseur et envoyé un message." - }, - "hosts": { - "docsLink": "Voir la documentation" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/he_IL.json b/plugins/ai/locales/translated/he_IL.json deleted file mode 100644 index 492d24759..000000000 --- a/plugins/ai/locales/translated/he_IL.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "השתמש בעוזר", - "description": "פתח את העוזר ושאל אותו דברים." - }, - "manage_providers": { - "title": "ניהול ספקים", - "description": "הוספה, עריכה והסרה של ספקי הבינה המלאכותית ואת מפתחות ה-API שלהם." - }, - "apply_proposals": { - "title": "החלת הצעות", - "description": "תן לעוזר לבצע את השינויים שהוא מציע." - }, - "services.use": { - "title": "השתמש בבינה מלאכותית מתכונות אחרות", - "description": "ייתכן שתכונות אחרות יתקשרו לעוזר בשמך." - }, - "secrets.share": { - "title": "שתף את מפתח ה-API", - "description": "תכונות אחרות עשויות להשתמש במפתח ה-API של בינה מלאכותית המאוחסן מבלי לראות אותו." - } - }, - "settings": { - "assistantUnavailable": "עוזר הבינה המלאכותית כבוי בשרת הזה.", - "providersNeedOptIn": "הפעל את עוזר הבינה המלאכותית למעלה כדי להוסיף ספקים.", - "admin": { - "globallyEnabled": { - "label": "עוזר בינה מלאכותית", - "description": "אפשר למשתמשים להפעיל את עוזר הבינה המלאכותית. כאשר זה כבוי, העוזר מוסתר וחסום עבור כולם." - }, - "privateEndpoints": { - "label": "מארחים פרטיים של בינה מלאכותית מורשים", - "description": "מארחים ברשת הפרטית שלך שמשתמשים יכולים להפנות אליהם ספק, כגון Ollama המאוחסן בעצמו. שים מארח אחד בכל שורה." - } - }, - "host": { - "enableAiAssistant": { - "label": "הפעל את עוזר הבינה המלאכותית", - "description": "אפשר לעוזר הבינה המלאכותית לסייע במסוף של מארח זה. כבוי כברירת מחדל, ועדיין נדרשת הפעלת בינה מלאכותית עבור חשבונך." - } - } - }, - "ai": { - "title": "עוֹזֵר", - "send": "לִשְׁלוֹחַ", - "stop": "לְהַפְסִיק", - "save": "לְהַצִיל", - "cancel": "לְבַטֵל", - "running": "רִיצָה", - "approve": "לְאַשֵׁר", - "reject": "לִדחוֹת", - "inputPlaceholder": "שאל לגבי המלצרים שלך, או תאר שינוי להציע", - "selectProvider": "בחר ספק", - "providerSettings": "הגדרות ספק", - "addProvider": "הוסף ספק", - "editProvider": "עריכת ספק", - "removeProvider": "הסר ספק", - "providerType": "ספק", - "providerLabel": "שֵׁם", - "providerLabelPlaceholder": "לדוגמה: אולמה מקומית", - "baseUrl": "כתובת השרת", - "apiKey": "מפתח API", - "defaultModel": "מודל ברירת מחדל", - "defaultModelPlaceholder": "לדוגמה: llama3.1 או claude-opus-5", - "labelRequired": "תן שם לספק הזה", - "providerSaveFailed": "לא ניתן היה לשמור את הספק", - "providerUpdated": "הספק עודכן", - "providerDeleteFailed": "לא ניתן היה להסיר את הספק", - "privateEndpointHint": "כתובות ברשת הפרטית שלך דורשות תחילה אישור ממנהל מערכת למארח.", - "providerOllama": "אולמה", - "providerAnthropic": "אנתרופי", - "providerOpenai": "בינה מלאכותית פתוחה", - "providerGemini": "גוגל ג'מיני", - "providerOpenaiCompatible": "תואם ל-OpenAI", - "toolArguments": "קֶלֶט", - "toolResult": "תוֹצָאָה", - "statusApplied": "יישומי", - "statusRejected": "נִדחֶה", - "proposalApplied": "השינוי הוחל", - "proposalApplyFailed": "לא ניתן היה להחיל את השינוי", - "proposalRejectFailed": "לא ניתן היה לדחות את השינוי", - "enableTitle": "הפעל את עוזר הבינה המלאכותית", - "enableDescription": "תן לעוזר לקרוא את ההגדרות שלך ולהציע שינויים לאישור.", - "readOnlyCommands": "אפשר פקודות אבחון לקריאה בלבד", - "readOnlyCommandsDescription": "העוזר יכול להריץ רשימה קצרה של פקודות בטוחות כמו df ו-uptime מבלי לשאול בכל פעם. כל דבר שמשנה שרת תמיד מוצע ראשון.", - "modelDetectFailed": "לא ניתן היה להגיע לספק, לכן זוהי רשימה כללית. ניתן להקליד כל שם דגם.", - "modelCustom": "הזן שם דגם", - "modelRefresh": "רענון הרשימה", - "modelPlaceholder": "בחר או הקלד מודל", - "attachHint": "הקלד @ כדי להפנות למארח, קטע טקסט או אוטומציה. שינויים מוצעים לאישורך.", - "runCommandTitle": "הפעלה של הפקודה", - "assistant": "עוזר בינה מלאכותית", - "terminalPanelEmpty": "שאל את העוזר לגבי המארח הזה. כל שינוי במצב דורש תחילה את אישורך.", - "terminalPanelPlaceholder": "שאל על המארח הזה...", - "noProvidersConfigured": "עדיין לא הוגדר ספק בינה מלאכותית. הוסף אחד בכרטיסייה עוזר בינה מלאכותית.", - "runsInOpenTerminal": "פועל בסשן הטרמינל הפתוח שלך.", - "runInTerminal": "לָרוּץ", - "terminalContextPrefill": "הנה פלט הטרמינל שלי:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "אפשרו לתכונות אחרות להשתמש במפתח ה-API של ספק הבינה המלאכותית שלכם עבור בקשות משלהן. המפתח לעולם לא יועתק, ותוכלו לבטל אותו בכל עת." - } - }, - "nav": { - "ai": "בינה מלאכותית" - }, - "onboarding": { - "aiTitle": "עוזר בינה מלאכותית", - "aiIntro": "טרמיקס יכול לכלול עוזר בינה מלאכותית אופציונלי. ניתן לשנות זאת מאוחר יותר בהגדרות.", - "aiSkipTitle": "תעזוב את זה", - "aiSkipDesc": "טרמיקס עובד כמו שהוא עובד עכשיו. העוזר נשאר מוסתר.", - "aiEnableTitle": "הפעל אותו", - "aiEnableDesc": "מוסיף עוזר שקורא את ההגדרות שלך ומציע שינויים שאתה מאשר. נדרש ספק שאתה מספק, כגון אולמה מקומי.", - "aiNote": "שום דבר לא נשלח לשום מקום עד שתוסיף ספק ותשלח הודעה." - }, - "hosts": { - "docsLink": "הצג מסמכים" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/hi_IN.json b/plugins/ai/locales/translated/hi_IN.json deleted file mode 100644 index bd1a1b5f4..000000000 --- a/plugins/ai/locales/translated/hi_IN.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "सहायक का उपयोग करें", - "description": "असिस्टेंट खोलें और उससे सवाल पूछें।" - }, - "manage_providers": { - "title": "प्रदाताओं का प्रबंधन करें", - "description": "एआई प्रदाताओं और उनकी एपीआई कुंजियों को जोड़ें, संपादित करें और हटाएं।" - }, - "apply_proposals": { - "title": "प्रस्ताव लागू करें", - "description": "सहायक को उसके द्वारा सुझाए गए बदलावों को लागू करने दें।" - }, - "services.use": { - "title": "अन्य सुविधाओं से एआई का उपयोग करें", - "description": "अन्य सुविधाएं आपकी ओर से असिस्टेंट को कॉल कर सकती हैं।" - }, - "secrets.share": { - "title": "एपीआई कुंजी साझा करें", - "description": "अन्य सुविधाएं संग्रहीत एआई एपीआई कुंजी का उपयोग उसे देखे बिना भी कर सकती हैं।" - } - }, - "settings": { - "assistantUnavailable": "इस सर्वर पर एआई असिस्टेंट बंद है।", - "providersNeedOptIn": "प्रदाताओं को जोड़ने के लिए ऊपर दिए गए एआई सहायक को चालू करें।", - "admin": { - "globallyEnabled": { - "label": "एआई सहायक", - "description": "उपयोगकर्ताओं को एआई सहायक चालू करने की अनुमति दें। जब यह बंद होगा, तो सहायक सभी के लिए छिपा हुआ और अवरुद्ध रहेगा।" - }, - "privateEndpoints": { - "label": "निजी एआई होस्ट की अनुमति दी गई", - "description": "आपके निजी नेटवर्क पर मौजूद होस्ट, जिन्हें उपयोगकर्ता किसी प्रदाता से जोड़ सकते हैं, जैसे कि स्व-होस्टेड ओलामा। प्रत्येक पंक्ति में एक होस्ट रखें।" - } - }, - "host": { - "enableAiAssistant": { - "label": "एआई असिस्टेंट को सक्षम करें", - "description": "इस होस्ट के टर्मिनल में एआई सहायक को मदद करने की अनुमति दें। यह डिफ़ॉल्ट रूप से बंद है, लेकिन आपके खाते के लिए एआई को सक्षम करना आवश्यक है।" - } - } - }, - "ai": { - "title": "सहायक", - "send": "भेजना", - "stop": "स्टॉप", - "save": "सहेजें", - "cancel": "रद्द करें", - "running": "चल रहे", - "approve": "मंज़ूरी देना", - "reject": "अस्वीकार करना", - "inputPlaceholder": "अपने सर्वरों के बारे में पूछें, या प्रस्तावित परिवर्तन का वर्णन करें।", - "selectProvider": "एक प्रदाता का चयन करें", - "providerSettings": "प्रदाता सेटिंग्स", - "addProvider": "प्रदाता जोड़ें", - "editProvider": "प्रदाता संपादित करें", - "removeProvider": "प्रदाता को हटाएँ", - "providerType": "प्रदाता", - "providerLabel": "नाम", - "providerLabelPlaceholder": "उदाहरण के लिए: स्थानीय ओलामा", - "baseUrl": "सर्वर पता", - "apiKey": "एपीआई कुंजी", - "defaultModel": "डिफ़ॉल्ट मॉडल", - "defaultModelPlaceholder": "उदाहरण के लिए: llama3.1 या claude-opus-5", - "labelRequired": "इस प्रदाता को एक नाम दें", - "providerSaveFailed": "प्रदाता को बचाया नहीं जा सका", - "providerUpdated": "प्रदाता ने अपडेट किया", - "providerDeleteFailed": "प्रदाता को हटाया नहीं जा सका", - "privateEndpointHint": "आपके निजी नेटवर्क पर मौजूद पतों के लिए पहले एक प्रशासक द्वारा होस्ट को अनुमति देना आवश्यक है।", - "providerOllama": "ओलामा", - "providerAnthropic": "anthropic", - "providerOpenai": "ओपनएआई", - "providerGemini": "गूगल जेमिनी", - "providerOpenaiCompatible": "OpenAI संगत", - "toolArguments": "इनपुट", - "toolResult": "परिणाम", - "statusApplied": "लागू किया गया", - "statusRejected": "अस्वीकार कर दिया", - "proposalApplied": "परिवर्तन लागू हो गया", - "proposalApplyFailed": "परिवर्तन लागू नहीं हो सका", - "proposalRejectFailed": "परिवर्तन को अस्वीकार नहीं किया जा सका", - "enableTitle": "एआई सहायक को सक्रिय करें", - "enableDescription": "सहायक को आपकी सेटअप प्रक्रिया पढ़ने दें और वह आपके अनुमोदन के लिए परिवर्तन सुझाएगा।", - "readOnlyCommands": "केवल पढ़ने योग्य डायग्नोस्टिक कमांड की अनुमति दें", - "readOnlyCommandsDescription": "सहायक बिना पूछे ही df और uptime जैसे कुछ सुरक्षित कमांड चला सकता है। सर्वर में कोई भी बदलाव करने का प्रस्ताव हमेशा पहले दिया जाता है।", - "modelDetectFailed": "प्रदाता से संपर्क नहीं हो सका, इसलिए यह एक सामान्य सूची है। आप किसी भी मॉडल का नाम टाइप कर सकते हैं।", - "modelCustom": "मॉडल का नाम दर्ज करें", - "modelRefresh": "सूची रीफ़्रेश करें", - "modelPlaceholder": "कोई मॉडल चुनें या टाइप करें", - "attachHint": "किसी होस्ट, स्निपेट या ऑटोमेशन को संदर्भित करने के लिए @ टाइप करें। आपके अनुमोदन के लिए परिवर्तन प्रस्तावित किए जा रहे हैं।", - "runCommandTitle": "कमांड चलाएँ", - "assistant": "एआई सहायक", - "terminalPanelEmpty": "इस होस्ट के बारे में असिस्टेंट से पूछें। किसी भी स्थिति में बदलाव के लिए पहले आपकी स्वीकृति आवश्यक है।", - "terminalPanelPlaceholder": "इस होस्ट के बारे में पूछें...", - "noProvidersConfigured": "अभी तक कोई एआई प्रदाता कॉन्फ़िगर नहीं किया गया है। एआई सहायक टैब में एक जोड़ें।", - "runsInOpenTerminal": "यह आपके खुले टर्मिनल सत्र में चलता है।", - "runInTerminal": "चलाएं", - "terminalContextPrefill": "यह मेरे टर्मिनल का आउटपुट है:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "अन्य सुविधाओं को अपने अनुरोधों के लिए आपके एआई प्रदाता एपीआई कुंजी का उपयोग करने दें। कुंजी की कभी प्रतिलिपि नहीं बनाई जाती है, और आप इसे किसी भी समय रद्द कर सकते हैं।" - } - }, - "nav": { - "ai": "ऐ" - }, - "onboarding": { - "aiTitle": "एआई सहायक", - "aiIntro": "टर्मिक्स में एक वैकल्पिक एआई सहायक शामिल हो सकता है। आप इसे बाद में अपनी सेटिंग्स में बदल सकते हैं।", - "aiSkipTitle": "इसे बंद कर दो", - "aiSkipDesc": "टर्मिक्स अभी की तरह ही काम करता है। असिस्टेंट छिपा रहता है।", - "aiEnableTitle": "इसे चालू करें", - "aiEnableDesc": "यह एक सहायक सुविधा जोड़ता है जो आपके सेटअप को पढ़ता है और आपके द्वारा स्वीकृत किए जाने वाले परिवर्तनों का सुझाव देता है। इसके लिए आपको एक प्रदाता उपलब्ध कराना होगा, जैसे कि स्थानीय ओलामा।", - "aiNote": "जब तक आप कोई प्रदाता नहीं जोड़ते और संदेश नहीं भेजते, तब तक कुछ भी कहीं नहीं भेजा जाता।" - }, - "hosts": { - "docsLink": "दस्तावेज़ देखें" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/hu_HU.json b/plugins/ai/locales/translated/hu_HU.json deleted file mode 100644 index 68213833f..000000000 --- a/plugins/ai/locales/translated/hu_HU.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Használja az asszisztenst", - "description": "Nyisd meg az asszisztenst, és kérdezz tőle dolgokat." - }, - "manage_providers": { - "title": "Szolgáltatók kezelése", - "description": "MI-szolgáltatók és API-kulcsaik hozzáadása, szerkesztése és eltávolítása." - }, - "apply_proposals": { - "title": "Javaslatok alkalmazása", - "description": "Hagyd, hogy az asszisztens végrehajtsa a javasolt módosításokat." - }, - "services.use": { - "title": "Használja a mesterséges intelligenciát más funkciókból", - "description": "Más funkciók felhívhatják az asszisztenst az Ön nevében." - }, - "secrets.share": { - "title": "API-kulcs megosztása", - "description": "Más funkciók használhatják a tárolt AI API-kulcsot anélkül, hogy látnák azt." - } - }, - "settings": { - "assistantUnavailable": "A mesterséges intelligencia asszisztens ki van kapcsolva ezen a szerveren.", - "providersNeedOptIn": "Kapcsolja be a fenti AI asszisztenst a szolgáltatók hozzáadásához.", - "admin": { - "globallyEnabled": { - "label": "MI asszisztens", - "description": "Engedélyezheti a felhasználóknak a mesterséges intelligencia asszisztens bekapcsolását. Amíg ez ki van kapcsolva, az asszisztens rejtett és blokkolt mindenki számára." - }, - "privateEndpoints": { - "label": "Engedélyezett privát AI-gazdagépek", - "description": "Privát hálózatán található hosztok, amelyekre a felhasználók szolgáltatót irányíthatnak, például egy saját hosztot futtató Ollama. Minden sorba egy hosztot írjon be." - } - }, - "host": { - "enableAiAssistant": { - "label": "AI asszisztens engedélyezése", - "description": "Engedélyezi a mesterséges intelligencia asszisztensének a gazdagép terminálján való segítséget. Alapértelmezés szerint ki van kapcsolva, de továbbra is engedélyeznie kell a mesterséges intelligenciát a fiókjában." - } - } - }, - "ai": { - "title": "Helyettes", - "send": "Elküld", - "stop": "Stop", - "save": "Megtakarítás", - "cancel": "Mégsem", - "running": "futás", - "approve": "Jóváhagyás", - "reject": "Elutasít", - "inputPlaceholder": "Kérdezzen a felszolgálókról, vagy írjon le egy javasolt változtatást", - "selectProvider": "Válasszon szolgáltatót", - "providerSettings": "Szolgáltatói beállítások", - "addProvider": "Szolgáltató hozzáadása", - "editProvider": "Szolgáltató szerkesztése", - "removeProvider": "Szolgáltató eltávolítása", - "providerType": "Szolgáltató", - "providerLabel": "Név", - "providerLabelPlaceholder": "Például: Helyi Ollama", - "baseUrl": "Szerver címe", - "apiKey": "API-kulcs", - "defaultModel": "Alapértelmezett modell", - "defaultModelPlaceholder": "Például: llama3.1 vagy claude-opus-5", - "labelRequired": "Adjon nevet ennek a szolgáltatónak", - "providerSaveFailed": "Nem sikerült menteni a szolgáltatót", - "providerUpdated": "Szolgáltató frissítve", - "providerDeleteFailed": "Nem sikerült eltávolítani a szolgáltatót", - "privateEndpointHint": "A privát hálózaton lévő címekhez először egy rendszergazdának kell engedélyeznie a gazdagépet.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropikus", - "providerOpenai": "Nyílt mesterséges intelligencia", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-kompatibilis", - "toolArguments": "Bemenet", - "toolResult": "Eredmény", - "statusApplied": "Alkalmazott", - "statusRejected": "Elutasítva", - "proposalApplied": "Módosítás alkalmazva", - "proposalApplyFailed": "Nem sikerült alkalmazni a módosítást", - "proposalRejectFailed": "Nem sikerült elutasítani a módosítást", - "enableTitle": "Engedélyezze a mesterséges intelligencia asszisztenst", - "enableDescription": "Hagyd, hogy az asszisztens felolvassa a beállításokat, és javasoljon módosításokat jóváhagyásra.", - "readOnlyCommands": "Csak olvasható diagnosztikai parancsok engedélyezése", - "readOnlyCommandsDescription": "Az asszisztens lefuttathat egy rövid listát a biztonságos parancsokról, mint például a df és az uptime, anélkül, hogy minden alkalommal rákérdezne. Bármi, ami megváltoztat egy szervert, mindig elsőként kerül felkérésre.", - "modelDetectFailed": "Nem sikerült elérni a szolgáltatót, ezért ez egy általános lista. Bármelyik modellnevet beírhatja.", - "modelCustom": "Adjon meg egy modellnevet", - "modelRefresh": "Lista frissítése", - "modelPlaceholder": "Válasszon vagy írjon be egy modellt", - "attachHint": "Írjon be @ jelet egy gazdagépre, kódrészletre vagy automatizálásra való hivatkozáshoz. A rendszer módosításokat javasol jóváhagyásra.", - "runCommandTitle": "Parancs futtatása", - "assistant": "MI asszisztens", - "terminalPanelEmpty": "Kérdezd meg az asszisztenst erről a házigazdáról. Bármi, ami megváltoztatja az állapotát, először a jóváhagyásodra van szükség.", - "terminalPanelPlaceholder": "Kérdezz rá erre a házigazdára...", - "noProvidersConfigured": "Még nincs konfigurálva AI-szolgáltató. Adjon hozzá egyet az AI-asszisztens fülön.", - "runsInOpenTerminal": "A megnyitott terminál munkamenetben fut.", - "runInTerminal": "Fut", - "terminalContextPrefill": "Itt a terminál kimenete:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Engedélyezd más funkcióknak a mesterséges intelligencia szolgáltató API-kulcsának használatát a saját kéréseikhez. A kulcs soha nem kerül másolásra, és ezt bármikor visszavonhatod." - } - }, - "nav": { - "ai": "MI" - }, - "onboarding": { - "aiTitle": "MI asszisztens", - "aiIntro": "A Termix tartalmazhat opcionális mesterséges intelligencia asszisztenst. Ezt később módosíthatja a beállításokban.", - "aiSkipTitle": "Hagyd ki", - "aiSkipDesc": "A Termix ugyanúgy működik, mint most. Az asszisztens rejtve marad.", - "aiEnableTitle": "Kapcsold be", - "aiEnableDesc": "Hozzáad egy asszisztenst, amely beolvassa a beállításait, és javaslatokat tesz az Ön által jóváhagyott módosításokra. Szükség van egy Ön által biztosított szolgáltatóra, például egy helyi Ollama szakemberre.", - "aiNote": "Semmi sem kerül sehova, amíg nem adsz hozzá egy szolgáltatót és nem küldesz üzenetet." - }, - "hosts": { - "docsLink": "Dokumentumok megtekintése" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/id_ID.json b/plugins/ai/locales/translated/id_ID.json deleted file mode 100644 index 867d64006..000000000 --- a/plugins/ai/locales/translated/id_ID.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Gunakan asisten", - "description": "Buka asisten dan ajukan pertanyaan kepadanya." - }, - "manage_providers": { - "title": "Mengelola penyedia", - "description": "Tambahkan, edit, dan hapus penyedia AI dan kunci API mereka." - }, - "apply_proposals": { - "title": "Ajukan proposal", - "description": "Biarkan asisten tersebut menjalankan perubahan yang disarankannya." - }, - "services.use": { - "title": "Gunakan AI dari fitur lain.", - "description": "Fitur lainnya mungkin akan memanggil asisten atas nama Anda." - }, - "secrets.share": { - "title": "Bagikan kunci API", - "description": "Fitur lain mungkin menggunakan kunci API AI yang tersimpan tanpa mengetahuinya." - } - }, - "settings": { - "assistantUnavailable": "Asisten AI dinonaktifkan di server ini.", - "providersNeedOptIn": "Aktifkan asisten AI di atas untuk menambahkan penyedia.", - "admin": { - "globallyEnabled": { - "label": "Asisten AI", - "description": "Izinkan pengguna untuk mengaktifkan asisten AI. Saat dinonaktifkan, asisten akan disembunyikan dan diblokir untuk semua orang." - }, - "privateEndpoints": { - "label": "Host AI pribadi yang diizinkan", - "description": "Host di jaringan pribadi Anda yang dapat diarahkan pengguna ke penyedia layanan, seperti Ollama yang dihosting sendiri. Letakkan satu host di setiap baris." - } - }, - "host": { - "enableAiAssistant": { - "label": "Aktifkan Asisten AI", - "description": "Izinkan asisten AI untuk membantu di terminal host ini. Secara default dinonaktifkan, dan AI tetap perlu diaktifkan untuk akun Anda." - } - } - }, - "ai": { - "title": "Asisten", - "send": "Mengirim", - "stop": "Berhenti", - "save": "Menyimpan", - "cancel": "Membatalkan", - "running": "berlari", - "approve": "Menyetujui", - "reject": "Menolak", - "inputPlaceholder": "Tanyakan tentang server Anda, atau jelaskan perubahan yang ingin Anda usulkan.", - "selectProvider": "Pilih penyedia", - "providerSettings": "Pengaturan penyedia", - "addProvider": "Tambahkan penyedia", - "editProvider": "Penyedia pengeditan", - "removeProvider": "Hapus penyedia", - "providerType": "Penyedia", - "providerLabel": "Nama", - "providerLabelPlaceholder": "Contohnya: Ollama setempat", - "baseUrl": "Alamat server", - "apiKey": "Kunci API", - "defaultModel": "Model default", - "defaultModelPlaceholder": "Misalnya: llama3.1 atau claude-opus-5", - "labelRequired": "Beri nama penyedia ini", - "providerSaveFailed": "Penyedia layanan tidak dapat disimpan.", - "providerUpdated": "Penyedia diperbarui", - "providerDeleteFailed": "Penyedia layanan tidak dapat dihapus.", - "privateEndpointHint": "Alamat-alamat di jaringan pribadi Anda memerlukan izin administrator terlebih dahulu untuk host tersebut.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropis", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Kompatibel dengan OpenAI", - "toolArguments": "Masukan", - "toolResult": "Hasil", - "statusApplied": "Terapan", - "statusRejected": "Ditolak", - "proposalApplied": "Perubahan diterapkan", - "proposalApplyFailed": "Perubahan tidak dapat diterapkan.", - "proposalRejectFailed": "Tidak bisa menolak perubahan tersebut", - "enableTitle": "Aktifkan asisten AI", - "enableDescription": "Biarkan asisten membaca pengaturan Anda dan menyarankan perubahan yang perlu Anda setujui.", - "readOnlyCommands": "Izinkan perintah diagnostik hanya baca.", - "readOnlyCommandsDescription": "Asisten dapat menjalankan daftar singkat perintah aman seperti df dan uptime tanpa bertanya setiap kali. Apa pun yang mengubah server selalu diusulkan terlebih dahulu.", - "modelDetectFailed": "Tidak dapat menghubungi penyedia, jadi ini adalah daftar umum. Anda dapat mengetikkan nama model apa pun.", - "modelCustom": "Masukkan nama model", - "modelRefresh": "Perbarui daftar", - "modelPlaceholder": "Pilih atau ketik model", - "attachHint": "Ketik @ untuk merujuk ke host, cuplikan kode, atau otomatisasi. Perubahan diajukan untuk Anda setujui.", - "runCommandTitle": "Jalankan perintah", - "assistant": "Asisten AI", - "terminalPanelEmpty": "Tanyakan kepada asisten tentang tuan rumah ini. Segala sesuatu yang berubah status memerlukan persetujuan Anda terlebih dahulu.", - "terminalPanelPlaceholder": "Tanyakan tentang tuan rumah ini...", - "noProvidersConfigured": "Belum ada penyedia AI yang dikonfigurasi. Tambahkan satu di tab asisten AI.", - "runsInOpenTerminal": "Berjalan di sesi terminal Anda yang sedang terbuka.", - "runInTerminal": "Berlari", - "terminalContextPrefill": "Berikut adalah output terminal saya:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Izinkan fitur lain menggunakan kunci API penyedia AI Anda untuk permintaan mereka sendiri. Kunci tersebut tidak pernah disalin, dan Anda dapat mencabutnya kapan saja." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "Asisten AI", - "aiIntro": "Termix dapat menyertakan asisten AI opsional. Anda dapat mengubahnya nanti di pengaturan Anda.", - "aiSkipTitle": "Biarkan saja", - "aiSkipDesc": "Termix berfungsi seperti sekarang. Asisten tetap tersembunyi.", - "aiEnableTitle": "Nyalakan", - "aiEnableDesc": "Menambahkan asisten yang membaca pengaturan Anda dan menyarankan perubahan yang Anda setujui. Membutuhkan penyedia yang Anda berikan, seperti Ollama lokal.", - "aiNote": "Tidak ada data yang dikirim ke mana pun sampai Anda menambahkan penyedia dan mengirim pesan." - }, - "hosts": { - "docsLink": "Lihat dokumen" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/it_IT.json b/plugins/ai/locales/translated/it_IT.json deleted file mode 100644 index fbef060cf..000000000 --- a/plugins/ai/locales/translated/it_IT.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Utilizzare l'assistente", - "description": "Apri l'assistente e ponigli delle domande." - }, - "manage_providers": { - "title": "Gestire i fornitori", - "description": "Aggiungi, modifica e rimuovi i provider di intelligenza artificiale e le relative chiavi API." - }, - "apply_proposals": { - "title": "Presentare proposte", - "description": "Lascia che l'assistente esegua le modifiche suggerite." - }, - "services.use": { - "title": "Utilizzare l'intelligenza artificiale da altre funzionalità", - "description": "Altre funzioni potrebbero attivare l'assistente per tuo conto." - }, - "secrets.share": { - "title": "Condividi la chiave API", - "description": "Altre funzionalità potrebbero utilizzare la chiave API AI memorizzata senza visualizzarla." - } - }, - "settings": { - "assistantUnavailable": "L'assistente IA è disattivato su questo server.", - "providersNeedOptIn": "Attiva l'assistente AI qui sopra per aggiungere i fornitori.", - "admin": { - "globallyEnabled": { - "label": "Assistente basato sull'intelligenza artificiale", - "description": "Consenti agli utenti di attivare l'assistente AI. Quando è disattivato, l'assistente è nascosto e bloccato per tutti." - }, - "privateEndpoints": { - "label": "Host di IA privati consentiti", - "description": "Host sulla tua rete privata a cui gli utenti possono indirizzare un provider, come ad esempio un'installazione self-hosted di Ollama. Assegna un host a ciascuna linea." - } - }, - "host": { - "enableAiAssistant": { - "label": "Assistente AI per l'innovazione", - "description": "Consenti all'assistente IA di fornire assistenza nel terminale di questo host. Disattivato per impostazione predefinita, ma è necessario abilitare l'IA per il tuo account." - } - } - }, - "ai": { - "title": "Assistente", - "send": "Inviare", - "stop": "Stop", - "save": "Salva", - "cancel": "Annulla", - "running": "in esecuzione", - "approve": "Approvare", - "reject": "Rifiutare", - "inputPlaceholder": "Chiedi informazioni sui tuoi server o descrivi una modifica da proporre", - "selectProvider": "Seleziona un fornitore", - "providerSettings": "Impostazioni del provider", - "addProvider": "Aggiungi fornitore", - "editProvider": "Fornitore di modifica", - "removeProvider": "Rimuovi il fornitore", - "providerType": "Fornitore", - "providerLabel": "Nome", - "providerLabelPlaceholder": "Ad esempio: Ollama locale", - "baseUrl": "Indirizzo del server", - "apiKey": "Chiave API", - "defaultModel": "Modello predefinito", - "defaultModelPlaceholder": "Ad esempio: llama3.1 o claude-opus-5", - "labelRequired": "Assegna un nome a questo fornitore", - "providerSaveFailed": "Impossibile salvare il provider", - "providerUpdated": "Fornitore aggiornato", - "providerDeleteFailed": "Impossibile rimuovere il provider", - "privateEndpointHint": "Gli indirizzi sulla tua rete privata richiedono prima l'autorizzazione di un amministratore per l'host.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropico", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Compatibile con OpenAI", - "toolArguments": "Input", - "toolResult": "Risultato", - "statusApplied": "Applicato", - "statusRejected": "Respinto", - "proposalApplied": "Modifica applicata", - "proposalApplyFailed": "Impossibile applicare la modifica", - "proposalRejectFailed": "Impossibile rifiutare la modifica", - "enableTitle": "Abilita l'assistente IA", - "enableDescription": "Lascia che l'assistente legga la tua configurazione e ti suggerisca delle modifiche da approvare.", - "readOnlyCommands": "Consenti comandi diagnostici di sola lettura", - "readOnlyCommandsDescription": "L'assistente può eseguire un breve elenco di comandi sicuri come df e uptime senza chiedere conferma ogni volta. Qualsiasi operazione che modifichi un server viene sempre proposta in anticipo.", - "modelDetectFailed": "Non è stato possibile contattare il fornitore, quindi questo è un elenco generico. È possibile digitare qualsiasi nome di modello.", - "modelCustom": "Inserisci il nome del modello", - "modelRefresh": "Aggiorna elenco", - "modelPlaceholder": "Scegli o digita un modello", - "attachHint": "Digita @ per fare riferimento a un host, uno snippet o un'automazione. Le modifiche proposte sono da approvare.", - "runCommandTitle": "Esegui il comando", - "assistant": "Assistente AI", - "terminalPanelEmpty": "Chiedi all'assistente informazioni su questo host. Qualsiasi modifica allo stato richiede la tua approvazione preventiva.", - "terminalPanelPlaceholder": "Chiedi informazioni su questo host...", - "noProvidersConfigured": "Non è ancora configurato alcun provider di IA. Aggiungine uno nella scheda Assistente IA.", - "runsInOpenTerminal": "Viene eseguito nella sessione del terminale aperta.", - "runInTerminal": "Esegui", - "terminalContextPrefill": "Ecco l'output del mio terminale:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Consenti ad altre funzionalità di utilizzare la tua chiave API del provider AI per le proprie richieste. La chiave non viene mai copiata e puoi revocarla in qualsiasi momento." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "Assistente basato sull'intelligenza artificiale", - "aiIntro": "Termix può includere un assistente AI opzionale. Puoi modificare questa impostazione in seguito.", - "aiSkipTitle": "Lascialo spento", - "aiSkipDesc": "Termix funziona come ora. L'assistente rimane nascosto.", - "aiEnableTitle": "Accendilo", - "aiEnableDesc": "Aggiunge un assistente che legge la tua configurazione e suggerisce modifiche che approvi. Richiede un provider che tu fornisca, come ad esempio un Ollama locale.", - "aiNote": "Non viene inviato nulla finché non si aggiunge un provider e si invia un messaggio." - }, - "hosts": { - "docsLink": "Visualizza documentazione" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ja_JP.json b/plugins/ai/locales/translated/ja_JP.json deleted file mode 100644 index cab8c0ae6..000000000 --- a/plugins/ai/locales/translated/ja_JP.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "アシスタントを使用する", - "description": "アシスタントを開いて、いろいろ質問してみてください。" - }, - "manage_providers": { - "title": "プロバイダーを管理する", - "description": "AIプロバイダーとそのAPIキーを追加、編集、削除できます。" - }, - "apply_proposals": { - "title": "提案を適用する", - "description": "アシスタントに提案された変更を実行させましょう。" - }, - "services.use": { - "title": "他の機能からAIを活用する", - "description": "その他の機能によっては、あなたに代わってアシスタントを呼び出す場合があります。" - }, - "secrets.share": { - "title": "APIキーを共有する", - "description": "他の機能は、保存されたAI APIキーを認識せずに使用する場合があります。" - } - }, - "settings": { - "assistantUnavailable": "このサーバーではAIアシスタントは無効になっています。", - "providersNeedOptIn": "上記のAIアシスタントをオンにして、プロバイダーを追加してください。", - "admin": { - "globallyEnabled": { - "label": "AIアシスタント", - "description": "ユーザーがAIアシスタントをオンにできるようにします。オフになっている間は、アシスタントは非表示になり、誰からも利用できなくなります。" - }, - "privateEndpoints": { - "label": "プライベートAIホストを許可", - "description": "ユーザーがプロバイダに指定できる、プライベートネットワーク上のホスト(自己ホスト型のOllamaなど)。各行にホストを1つずつ記述してください。" - } - }, - "host": { - "enableAiAssistant": { - "label": "AIアシスタントを有効にする", - "description": "このホストのターミナルでAIアシスタントによる支援を許可します。デフォルトでは無効になっており、アカウントでAIを有効にする必要があります。" - } - } - }, - "ai": { - "title": "アシスタント", - "send": "送信", - "stop": "ストップ", - "save": "保存", - "cancel": "キャンセル", - "running": "実行中", - "approve": "承認する", - "reject": "拒否する", - "inputPlaceholder": "サーバーについて質問するか、変更案を説明する", - "selectProvider": "プロバイダーを選択してください", - "providerSettings": "プロバイダー設定", - "addProvider": "プロバイダーを追加", - "editProvider": "プロバイダーを編集", - "removeProvider": "プロバイダーを削除", - "providerType": "プロバイダー", - "providerLabel": "名前", - "providerLabelPlaceholder": "例えば:地元のオラマ", - "baseUrl": "サーバーアドレス", - "apiKey": "APIキー", - "defaultModel": "デフォルトモデル", - "defaultModelPlaceholder": "例えば、llama3.1 や claude-opus-5 など。", - "labelRequired": "このプロバイダーに名前を付けてください", - "providerSaveFailed": "プロバイダーを保存できませんでした", - "providerUpdated": "プロバイダーが更新されました", - "providerDeleteFailed": "プロバイダーを削除できませんでした", - "privateEndpointHint": "プライベートネットワーク上のアドレスを使用するには、まず管理者がホストを許可する必要があります。", - "providerOllama": "オラマ", - "providerAnthropic": "人間", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI対応", - "toolArguments": "受信", - "toolResult": "結果", - "statusApplied": "適用済み", - "statusRejected": "拒否されました", - "proposalApplied": "変更が適用されました", - "proposalApplyFailed": "変更を適用できませんでした", - "proposalRejectFailed": "変更を拒否できませんでした", - "enableTitle": "AIアシスタントを有効にする", - "enableDescription": "アシスタントにあなたの設定を読み取らせ、承認が必要な変更案を提案させましょう。", - "readOnlyCommands": "読み取り専用の診断コマンドを許可する", - "readOnlyCommandsDescription": "アシスタントは、dfやuptimeといった安全なコマンドを毎回確認することなく実行する場合があります。サーバーに変更を加える操作は、常に最初に提案されます。", - "modelDetectFailed": "プロバイダーに連絡が取れなかったため、これは一般的なリストです。任意のモデル名を入力してください。", - "modelCustom": "モデル名を入力してください", - "modelRefresh": "リストを更新", - "modelPlaceholder": "モデルを選択または入力してください", - "attachHint": "ホスト、スニペット、またはオートメーションを参照するには、「@」を入力してください。変更案が表示され、承認が必要となります。", - "runCommandTitle": "コマンドを実行する", - "assistant": "AIアシスタント", - "terminalPanelEmpty": "このホストについてアシスタントにお問い合わせください。状態変更を伴う変更はすべて、事前にあなたの承認が必要です。", - "terminalPanelPlaceholder": "このホストについて質問する...", - "noProvidersConfigured": "AIプロバイダーがまだ設定されていません。AIアシスタントタブで追加してください。", - "runsInOpenTerminal": "開いているターミナルセッションで実行されます。", - "runInTerminal": "実行", - "terminalContextPrefill": "これが私のターミナル出力です:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "他の機能が、独自の要求にあなたのAIプロバイダーAPIキーを使用できるようにします。キーは決して複製されず、いつでも取り消すことができます。" - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AIアシスタント", - "aiIntro": "TermixにはオプションでAIアシスタントを搭載できます。これは後で設定画面で変更できます。", - "aiSkipTitle": "オフにしておく", - "aiSkipDesc": "Termixは現状のまま動作します。アシスタントは非表示のままです。", - "aiEnableTitle": "電源を入れる", - "aiEnableDesc": "設定を読み取り、承認した変更を提案するアシスタント機能を追加します。ローカルのOllamaなど、お客様ご自身で手配するプロバイダーが必要です。", - "aiNote": "プロバイダーを追加してメッセージを送信するまで、データはどこにも送信されません。" - }, - "hosts": { - "docsLink": "ドキュメントを表示" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ko_KR.json b/plugins/ai/locales/translated/ko_KR.json deleted file mode 100644 index ff76a33a4..000000000 --- a/plugins/ai/locales/translated/ko_KR.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "도우미를 사용하세요", - "description": "어시스턴트를 열고 질문을 해보세요." - }, - "manage_providers": { - "title": "공급자 관리", - "description": "AI 제공업체와 해당 API 키를 추가, 편집 및 삭제할 수 있습니다." - }, - "apply_proposals": { - "title": "제안서를 제출하세요", - "description": "도우미가 제안하는 변경 사항을 실행하도록 하세요." - }, - "services.use": { - "title": "다른 기능의 AI를 활용하세요", - "description": "다른 기능들이 사용자를 대신하여 어시스턴트에게 전화를 걸 수도 있습니다." - }, - "secrets.share": { - "title": "API 키를 공유하세요", - "description": "다른 기능들이 저장된 AI API 키를 직접 보지 않고도 사용할 수 있습니다." - } - }, - "settings": { - "assistantUnavailable": "이 서버에서는 AI 비서 기능이 꺼져 있습니다.", - "providersNeedOptIn": "위의 AI 도우미를 켜서 제공업체를 추가하세요.", - "admin": { - "globallyEnabled": { - "label": "AI 비서", - "description": "사용자가 AI 비서를 켜고 끌 수 있도록 하세요. AI 비서가 꺼져 있으면 모든 사용자에게 비서 기능이 숨겨지고 차단됩니다." - }, - "privateEndpoints": { - "label": "개인 AI 호스트 허용", - "description": "사용자가 공급자를 지정할 수 있는 개인 네트워크의 호스트(예: 자체 호스팅 Ollama)를 입력합니다. 각 줄에 호스트를 하나씩 입력하십시오." - } - }, - "host": { - "enableAiAssistant": { - "label": "AI 어시스턴트 활성화", - "description": "이 호스트의 터미널에서 AI 도우미의 도움을 받을 수 있도록 허용합니다. 기본적으로 비활성화되어 있으며, 계정에 AI 기능이 활성화되어 있어야 합니다." - } - } - }, - "ai": { - "title": "어시스턴트", - "send": "보내다", - "stop": "스톱", - "save": "저장", - "cancel": "취소", - "running": "실행 중", - "approve": "승인하다", - "reject": "거부하다", - "inputPlaceholder": "서버에 대해 문의하거나, 변경 사항을 제안해 보세요.", - "selectProvider": "제공업체를 선택하세요", - "providerSettings": "공급자 설정", - "addProvider": "공급자 추가", - "editProvider": "제공자 수정", - "removeProvider": "제공업체를 제거하세요", - "providerType": "공급자", - "providerLabel": "이름", - "providerLabelPlaceholder": "예: 지역 올라마", - "baseUrl": "서버 주소", - "apiKey": "API 키", - "defaultModel": "기본 모델", - "defaultModelPlaceholder": "예를 들어: llama3.1 또는 claude-opus-5", - "labelRequired": "이 제공업체에 이름을 지정하세요", - "providerSaveFailed": "공급자를 저장할 수 없습니다.", - "providerUpdated": "제공업체 업데이트됨", - "providerDeleteFailed": "공급자를 제거할 수 없습니다.", - "privateEndpointHint": "개인 네트워크의 주소에 접근하려면 관리자가 먼저 호스트를 허용해야 합니다.", - "providerOllama": "올라마", - "providerAnthropic": "인류", - "providerOpenai": "오픈아이", - "providerGemini": "구글 제미니", - "providerOpenaiCompatible": "OpenAI와 호환 가능", - "toolArguments": "입력", - "toolResult": "결과", - "statusApplied": "적용된", - "statusRejected": "거절됨", - "proposalApplied": "변경 사항이 적용되었습니다", - "proposalApplyFailed": "변경 사항을 적용할 수 없습니다.", - "proposalRejectFailed": "변경을 거부할 수 없었습니다", - "enableTitle": "AI 비서를 활성화하세요", - "enableDescription": "도우미가 사용자의 설정을 읽고 승인할 변경 사항을 제안하도록 하세요.", - "readOnlyCommands": "읽기 전용 진단 명령을 허용합니다.", - "readOnlyCommandsDescription": "도우미는 매번 묻지 않고 df 및 uptime과 같은 안전한 명령어 목록을 실행할 수 있습니다. 서버를 변경하는 작업은 항상 먼저 제안됩니다.", - "modelDetectFailed": "공급업체에 연결할 수 없어서 일반적인 목록을 제공합니다. 모델명을 입력하셔도 됩니다.", - "modelCustom": "모델명을 입력하세요", - "modelRefresh": "목록 새로 고침", - "modelPlaceholder": "모델을 선택하거나 입력하세요.", - "attachHint": "호스트, 코드 조각 또는 자동화를 참조하려면 @를 입력하세요. 승인을 위해 변경 사항이 제안됩니다.", - "runCommandTitle": "명령어를 실행하세요", - "assistant": "AI 어시스턴트", - "terminalPanelEmpty": "이 호스트에 대해 담당자에게 문의하십시오. 상태가 변경되는 모든 사항은 먼저 사용자의 승인이 필요합니다.", - "terminalPanelPlaceholder": "이 호스트에 대해 문의해 보세요...", - "noProvidersConfigured": "아직 AI 제공업체가 구성되지 않았습니다. AI 도우미 탭에서 추가하세요.", - "runsInOpenTerminal": "열려 있는 터미널 세션에서 실행됩니다.", - "runInTerminal": "실행", - "terminalContextPrefill": "다음은 제 터미널 출력 결과입니다:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "다른 기능에서 AI 제공업체 API 키를 사용하여 자체 요청을 처리할 수 있도록 허용합니다. 키는 절대 복사되지 않으며 언제든지 취소할 수 있습니다." - } - }, - "nav": { - "ai": "일체 포함" - }, - "onboarding": { - "aiTitle": "AI 비서", - "aiIntro": "Termix에는 선택적으로 AI 비서 기능이 포함될 수 있습니다. 이 기능은 나중에 설정에서 변경할 수 있습니다.", - "aiSkipTitle": "꺼두세요", - "aiSkipDesc": "Termix는 현재와 같이 작동합니다. 어시스턴트는 숨겨진 상태로 유지됩니다.", - "aiEnableTitle": "켜세요", - "aiEnableDesc": "설정을 읽고 사용자가 승인하는 변경 사항을 제안하는 도우미를 추가합니다. 사용자가 제공하는 공급업체(예: 지역 Ollama)가 필요합니다.", - "aiNote": "공급자를 추가하고 메시지를 보내기 전까지는 아무것도 전송되지 않습니다." - }, - "hosts": { - "docsLink": "문서 보기" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/nl_NL.json b/plugins/ai/locales/translated/nl_NL.json deleted file mode 100644 index 2f88416ec..000000000 --- a/plugins/ai/locales/translated/nl_NL.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Gebruik de assistent", - "description": "Open de assistent en stel er vragen aan." - }, - "manage_providers": { - "title": "Beheer aanbieders", - "description": "AI-providers en hun API-sleutels toevoegen, bewerken en verwijderen." - }, - "apply_proposals": { - "title": "Dien voorstellen in", - "description": "Laat de assistent de voorgestelde wijzigingen doorvoeren." - }, - "services.use": { - "title": "Gebruik AI van andere functies", - "description": "Andere functies kunnen de assistent namens u inschakelen." - }, - "secrets.share": { - "title": "Deel de API-sleutel", - "description": "Andere functies kunnen de opgeslagen AI API-sleutel gebruiken zonder deze te zien." - } - }, - "settings": { - "assistantUnavailable": "De AI-assistent is op deze server uitgeschakeld.", - "providersNeedOptIn": "Schakel de AI-assistent hierboven in om aanbieders toe te voegen.", - "admin": { - "globallyEnabled": { - "label": "AI-assistent", - "description": "Gebruikers kunnen de AI-assistent inschakelen. Wanneer deze is uitgeschakeld, is de assistent verborgen en voor iedereen geblokkeerd." - }, - "privateEndpoints": { - "label": "Toegestane privé-AI-hosts", - "description": "Hosts op uw privénetwerk waarnaar gebruikers een provider kunnen verwijzen, zoals een zelfgehoste Ollama. Plaats één host per lijn." - } - }, - "host": { - "enableAiAssistant": { - "label": "AI-assistent inschakelen", - "description": "Sta de AI-assistent toe om te helpen in de terminal van deze host. Standaard uitgeschakeld, maar AI moet nog steeds voor uw account zijn ingeschakeld." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Versturen", - "stop": "Stop", - "save": "Redden", - "cancel": "Annuleren", - "running": "rennen", - "approve": "Goedkeuren", - "reject": "Afwijzen", - "inputPlaceholder": "Vraag naar uw servers of beschrijf een voorgestelde wijziging.", - "selectProvider": "Selecteer een aanbieder", - "providerSettings": "Aanbiederinstellingen", - "addProvider": "Aanbieder toevoegen", - "editProvider": "Bewerk provider", - "removeProvider": "Verwijder provider", - "providerType": "Aanbieder", - "providerLabel": "Naam", - "providerLabelPlaceholder": "Bijvoorbeeld: Lokale Ollama", - "baseUrl": "Serveradres", - "apiKey": "API-sleutel", - "defaultModel": "Standaardmodel", - "defaultModelPlaceholder": "Bijvoorbeeld: llama3.1 of claude-opus-5", - "labelRequired": "Geef deze aanbieder een naam", - "providerSaveFailed": "De provider kon niet worden opgeslagen.", - "providerUpdated": "Aanbieder bijgewerkt", - "providerDeleteFailed": "De provider kon niet worden verwijderd.", - "privateEndpointHint": "IP-adressen op uw privénetwerk moeten eerst door een beheerder worden toegestaan.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropisch", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-compatibel", - "toolArguments": "Invoer", - "toolResult": "Resultaat", - "statusApplied": "Toegepast", - "statusRejected": "Afgewezen", - "proposalApplied": "Toegepaste wijziging", - "proposalApplyFailed": "De wijziging kon niet worden toegepast.", - "proposalRejectFailed": "Kon de wijziging niet afwijzen", - "enableTitle": "Schakel de AI-assistent in", - "enableDescription": "Laat de assistent je instellingen lezen en wijzigingen voorstellen die je vervolgens kunt goedkeuren.", - "readOnlyCommands": "Sta alleen-lezen diagnostische opdrachten toe.", - "readOnlyCommandsDescription": "De assistent kan een korte lijst met veilige commando's zoals df en uptime uitvoeren zonder telkens opnieuw toestemming te vragen. Alles wat een server wijzigt, wordt altijd eerst voorgesteld.", - "modelDetectFailed": "De aanbieder kon niet worden bereikt, daarom is dit een algemene lijst. U kunt elke modelnaam invoeren.", - "modelCustom": "Voer een modelnaam in", - "modelRefresh": "Vernieuw de lijst", - "modelPlaceholder": "Kies of typ een model", - "attachHint": "Typ @ om te verwijzen naar een host, codefragment of automatisering. Wijzigingen worden voorgesteld ter goedkeuring.", - "runCommandTitle": "Voer het commando uit", - "assistant": "AI-assistent", - "terminalPanelEmpty": "Vraag de assistent naar deze host. Alles wat de status wijzigt, vereist eerst uw goedkeuring.", - "terminalPanelPlaceholder": "Vraag naar deze host...", - "noProvidersConfigured": "Er is nog geen AI-provider geconfigureerd. Voeg er een toe via het tabblad AI-assistent.", - "runsInOpenTerminal": "Wordt uitgevoerd in uw geopende terminalsessie.", - "runInTerminal": "Loop", - "terminalContextPrefill": "Hier is mijn terminaluitvoer:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Laat andere functies uw API-sleutel van de AI-provider gebruiken voor hun eigen verzoeken. De sleutel wordt nooit gekopieerd en u kunt deze op elk moment intrekken." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI-assistent", - "aiIntro": "Termix kan optioneel een AI-assistent bevatten. Je kunt dit later in je instellingen wijzigen.", - "aiSkipTitle": "Laat het weg", - "aiSkipDesc": "Termix werkt zoals het nu werkt. De assistent blijft verborgen.", - "aiEnableTitle": "Schakel het in.", - "aiEnableDesc": "Voegt een assistent toe die je instellingen leest en wijzigingen voorstelt die je kunt goedkeuren. Vereist een leverancier die je zelf aanlevert, zoals een lokale Ollama.", - "aiNote": "Er wordt niets verzonden totdat je een provider toevoegt en een bericht verstuurt." - }, - "hosts": { - "docsLink": "Documenten bekijken" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/no_NO.json b/plugins/ai/locales/translated/no_NO.json deleted file mode 100644 index eeaddb4a8..000000000 --- a/plugins/ai/locales/translated/no_NO.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Bruk assistenten", - "description": "Åpne assistenten og spør den om ting." - }, - "manage_providers": { - "title": "Administrer leverandører", - "description": "Legg til, rediger og fjern AI-leverandørene og API-nøklene deres." - }, - "apply_proposals": { - "title": "Bruk forslag", - "description": "La assistenten utføre endringene den foreslår." - }, - "services.use": { - "title": "Bruk AI fra andre funksjoner", - "description": "Andre funksjoner kan ringe assistenten på dine vegne." - }, - "secrets.share": { - "title": "Del API-nøkkelen", - "description": "Andre funksjoner kan bruke den lagrede AI API-nøkkelen uten å se den." - } - }, - "settings": { - "assistantUnavailable": "AI-assistenten er slått av på denne serveren.", - "providersNeedOptIn": "Slå på AI-assistenten ovenfor for å legge til leverandører.", - "admin": { - "globallyEnabled": { - "label": "AI-assistent", - "description": "La brukere slå på AI-assistenten. Mens dette er av, er assistenten skjult og blokkert for alle." - }, - "privateEndpoints": { - "label": "Tillatte private AI-verter", - "description": "Verter på ditt private nettverk som brukere kan peke en leverandør til, for eksempel en selvhostet Ollama. Plasser én vert på hver linje." - } - }, - "host": { - "enableAiAssistant": { - "label": "Aktiver AI-assistent", - "description": "Tillat at AI-assistenten hjelper til i denne vertens terminal. Av som standard, og krever fortsatt at AI er aktivert for kontoen din." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Sende", - "stop": "Stopp", - "save": "Lagre", - "cancel": "Avbryt", - "running": "kjører", - "approve": "Vedta", - "reject": "Avvis", - "inputPlaceholder": "Spør om servitørene dine, eller beskriv en endring du vil foreslå", - "selectProvider": "Velg en leverandør", - "providerSettings": "Leverandørinnstillinger", - "addProvider": "Legg til leverandør", - "editProvider": "Rediger leverandør", - "removeProvider": "Fjern leverandør", - "providerType": "Leverandør", - "providerLabel": "Navn", - "providerLabelPlaceholder": "For eksempel: Lokal Ollama", - "baseUrl": "Serveradresse", - "apiKey": "API-nøkkel", - "defaultModel": "Standardmodell", - "defaultModelPlaceholder": "For eksempel: llama3.1 eller claude-opus-5", - "labelRequired": "Gi denne leverandøren et navn", - "providerSaveFailed": "Kunne ikke lagre leverandøren", - "providerUpdated": "Leverandøren er oppdatert", - "providerDeleteFailed": "Kunne ikke fjerne leverandøren", - "privateEndpointHint": "Adresser på ditt private nettverk krever at en administrator tillater verten først.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropisk", - "providerOpenai": "Åpen kunstig intelligens", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-kompatibel", - "toolArguments": "Inndata", - "toolResult": "Resultat", - "statusApplied": "Anvendt", - "statusRejected": "Avvist", - "proposalApplied": "Endringen er brukt", - "proposalApplyFailed": "Kunne ikke bruke endringen", - "proposalRejectFailed": "Kunne ikke avvise endringen", - "enableTitle": "Aktiver AI-assistenten", - "enableDescription": "La assistenten lese oppsettet ditt og foreslå endringer du kan godkjenne.", - "readOnlyCommands": "Tillat skrivebeskyttede diagnostiske kommandoer", - "readOnlyCommandsDescription": "Assistenten kan kjøre en kort liste med sikre kommandoer som df og uptime uten å spørre hver gang. Alt som endrer en server blir alltid foreslått først.", - "modelDetectFailed": "Kunne ikke nå leverandøren, så dette er en generell liste. Du kan skrive inn et hvilket som helst modellnavn.", - "modelCustom": "Skriv inn et modellnavn", - "modelRefresh": "Oppdater listen", - "modelPlaceholder": "Velg eller skriv inn en modell", - "attachHint": "Skriv inn @ for å referere til en vert, et kodeutdrag eller en automatisering. Endringer foreslås som du kan godkjenne.", - "runCommandTitle": "Kjør kommandoen", - "assistant": "AI-assistent", - "terminalPanelEmpty": "Spør assistenten om denne verten. Alt som endrer status trenger din godkjenning først.", - "terminalPanelPlaceholder": "Spør om denne verten...", - "noProvidersConfigured": "Ingen AI-leverandør er konfigurert ennå. Legg til en i AI-assistent-fanen.", - "runsInOpenTerminal": "Kjører i din åpne terminaløkt.", - "runInTerminal": "Kjør", - "terminalContextPrefill": "Her er terminalutgangen min:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "La andre funksjoner bruke API-nøkkelen til AI-leverandøren din for sine egne forespørsler. Nøkkelen kopieres aldri, og du kan tilbakekalle den når som helst." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI-assistent", - "aiIntro": "Termix kan inkludere en valgfri AI-assistent. Du kan endre dette senere i innstillingene dine.", - "aiSkipTitle": "La det være av", - "aiSkipDesc": "Termix fungerer som den gjør nå. Assistenten forblir skjult.", - "aiEnableTitle": "Slå den på", - "aiEnableDesc": "Legger til en assistent som leser oppsettet ditt og foreslår endringer du godkjenner. Krever en leverandør du oppgir, for eksempel en lokal Ollama.", - "aiNote": "Ingenting sendes noe sted før du legger til en leverandør og sender en melding." - }, - "hosts": { - "docsLink": "Vis dokumenter" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/pl_PL.json b/plugins/ai/locales/translated/pl_PL.json deleted file mode 100644 index 55bc57021..000000000 --- a/plugins/ai/locales/translated/pl_PL.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Użyj asystenta", - "description": "Otwórz asystenta i zadaj mu pytania." - }, - "manage_providers": { - "title": "Zarządzaj dostawcami", - "description": "Dodawaj, edytuj i usuwaj dostawców sztucznej inteligencji oraz ich klucze API." - }, - "apply_proposals": { - "title": "Zastosuj propozycje", - "description": "Pozwól asystentowi wprowadzić sugerowane zmiany." - }, - "services.use": { - "title": "Użyj sztucznej inteligencji z innych funkcji", - "description": "Inne funkcje mogą umożliwiać wywoływanie asystenta w Twoim imieniu." - }, - "secrets.share": { - "title": "Udostępnij klucz API", - "description": "Inne funkcje mogą wykorzystywać zapisany klucz API AI, nawet go nie widząc." - } - }, - "settings": { - "assistantUnavailable": "Asystent AI jest wyłączony na tym serwerze.", - "providersNeedOptIn": "Włącz asystenta AI powyżej, aby dodać dostawców.", - "admin": { - "globallyEnabled": { - "label": "Asystent AI", - "description": "Pozwól użytkownikom włączyć asystenta AI. Gdy ta opcja jest wyłączona, asystent jest ukryty i zablokowany dla wszystkich." - }, - "privateEndpoints": { - "label": "Dozwolone prywatne hosty AI", - "description": "Hosty w Twojej sieci prywatnej, na które użytkownicy mogą wskazać dostawcę, na przykład samodzielnie hostowany Ollama. Umieść jednego hosta na każdej linii." - } - }, - "host": { - "enableAiAssistant": { - "label": "Włącz asystenta AI", - "description": "Zezwól asystentowi AI na pomoc w terminalu tego hosta. Domyślnie wyłączone, ale nadal wymaga włączenia AI na Twoim koncie." - } - } - }, - "ai": { - "title": "Asystent", - "send": "Wyślij", - "stop": "Zatrzymaj", - "save": "Zapisz", - "cancel": "Anuluj", - "running": "działa", - "approve": "Zatwierdź", - "reject": "Odrzuć", - "inputPlaceholder": "Zapytaj o swoje serwery lub opisz proponowaną zmianę", - "selectProvider": "Wybierz dostawcę", - "providerSettings": "Ustawienia dostawcy", - "addProvider": "Dodaj dostawcę", - "editProvider": "Edytuj dostawcę", - "removeProvider": "Usuń dostawcę", - "providerType": "Dostawca", - "providerLabel": "Nazwa", - "providerLabelPlaceholder": "Na przykład: Ollama lokalnie", - "baseUrl": "Adres serwera", - "apiKey": "Klucz API", - "defaultModel": "Model domyślny", - "defaultModelPlaceholder": "Na przykład: llama3.1 lub claude-opus-5", - "labelRequired": "Podaj nazwę tego dostawcy", - "providerSaveFailed": "Nie udało się zapisać dostawcy", - "providerUpdated": "Zaktualizowano dostawcę", - "providerDeleteFailed": "Nie można usunąć dostawcy", - "privateEndpointHint": "Adresy w sieci prywatnej wymagają, aby administrator najpierw dopuścił dany host.", - "providerOllama": "Ollama", - "providerAnthropic": "Anthropic", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Kompatybilny z OpenAI", - "toolArguments": "Dane wejściowe", - "toolResult": "Wynik", - "statusApplied": "Zastosowano", - "statusRejected": "Odrzucono", - "proposalApplied": "Zastosowano zmianę", - "proposalApplyFailed": "Nie można zastosować zmiany", - "proposalRejectFailed": "Nie można odrzucić zmiany", - "enableTitle": "Włącz asystenta AI", - "enableDescription": "Pozwól asystentowi odczytać konfigurację i proponować zmiany do Twojego zatwierdzenia.", - "readOnlyCommands": "Zezwalaj na polecenia diagnostyczne tylko do odczytu", - "readOnlyCommandsDescription": "Asystent może uruchamiać krótką listę bezpiecznych poleceń, takich jak df czy uptime, bez pytania za każdym razem. Wszystko, co zmienia serwer, jest zawsze najpierw proponowane do zatwierdzenia.", - "modelDetectFailed": "Nie udało się skontaktować z dostawcą, więc jest to lista ogólna. Możesz wpisać dowolną nazwę modelu.", - "modelCustom": "Wprowadź nazwę modelu", - "modelRefresh": "Odśwież listę", - "modelPlaceholder": "Wybierz lub wpisz model", - "attachHint": "Wpisz @, aby odwołać się do hosta, snippetu lub automatyzacji. Zmiany są proponowane do Twojego zatwierdzenia.", - "runCommandTitle": "Uruchom polecenie", - "assistant": "Asystent AI", - "terminalPanelEmpty": "Zapytaj asystenta o tego hosta. Każda zmiana stanu wymaga Twojej zgody.", - "terminalPanelPlaceholder": "Zapytaj o tego gospodarza...", - "noProvidersConfigured": "Nie skonfigurowano jeszcze żadnego dostawcy AI. Dodaj go w zakładce Asystent AI.", - "runsInOpenTerminal": "Działa w otwartej sesji terminalowej.", - "runInTerminal": "Uruchom", - "terminalContextPrefill": "Oto wynik mojego terminala:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Pozwól innym funkcjom używać klucza API dostawcy sztucznej inteligencji do własnych żądań. Klucz nigdy nie jest kopiowany i możesz go w dowolnym momencie cofnąć." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "Asystent AI", - "aiIntro": "Termix może zawierać opcjonalnego asystenta AI. Możesz to później zmienić w ustawieniach.", - "aiSkipTitle": "Zostaw wyłączone", - "aiSkipDesc": "Termix działa tak jak teraz. Asystent pozostaje ukryty.", - "aiEnableTitle": "Włącz", - "aiEnableDesc": "Dodaje asystenta, który odczytuje Twoją konfigurację i sugeruje zmiany, które zatwierdzasz. Wymaga wskazanego przez Ciebie dostawcy, np. lokalnej Ollamy.", - "aiNote": "Dopóki nie dodasz dostawcy i nie wyślesz wiadomości, nic nie zostanie wysłane." - }, - "hosts": { - "docsLink": "Zobacz dokumentację" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/pt_BR.json b/plugins/ai/locales/translated/pt_BR.json deleted file mode 100644 index 8c7085311..000000000 --- a/plugins/ai/locales/translated/pt_BR.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Use o assistente", - "description": "Abra o assistente e faça perguntas." - }, - "manage_providers": { - "title": "Gerenciar fornecedores", - "description": "Adicione, edite e remova os fornecedores de IA e suas chaves de API." - }, - "apply_proposals": { - "title": "Apresentar propostas", - "description": "Deixe que o assistente execute as alterações que sugerir." - }, - "services.use": { - "title": "Utilizar IA de outras funcionalidades", - "description": "Outras funcionalidades podem chamar o assistente em seu nome." - }, - "secrets.share": { - "title": "Compartilhe a chave da API", - "description": "Outras funcionalidades podem usar a chave da API de IA armazenada sem visualizá-la." - } - }, - "settings": { - "assistantUnavailable": "O assistente de IA está desativado neste servidor.", - "providersNeedOptIn": "Ative o assistente de IA acima para adicionar fornecedores.", - "admin": { - "globallyEnabled": { - "label": "assistente de IA", - "description": "Permita que os usuários ativem o assistente de IA. Quando estiver desativado, o assistente ficará oculto e bloqueado para todos." - }, - "privateEndpoints": { - "label": "Hosts de IA privados permitidos", - "description": "Servidores na sua rede privada que os usuários podem usar como provedores, como um Ollama auto-hospedado. Coloque um servidor em cada linha." - } - }, - "host": { - "enableAiAssistant": { - "label": "Ativar o assistente de IA", - "description": "Permita que o assistente de IA ajude no terminal deste host. Desativado por padrão, mas ainda precisa que a IA esteja ativada para sua conta." - } - } - }, - "ai": { - "title": "Assistente", - "send": "Enviar", - "stop": "Parar", - "save": "Salvar", - "cancel": "Cancelar", - "running": "correndo", - "approve": "Aprovar", - "reject": "Rejeitar", - "inputPlaceholder": "Pergunte sobre seus servidores ou descreva uma alteração que você gostaria de propor.", - "selectProvider": "Selecione um fornecedor", - "providerSettings": "Configurações do provedor", - "addProvider": "Adicionar provedor", - "editProvider": "Editar provedor", - "removeProvider": "Remover provedor", - "providerType": "Fornecedor", - "providerLabel": "Nome", - "providerLabelPlaceholder": "Por exemplo: Ollama local", - "baseUrl": "Endereço do servidor", - "apiKey": "Chave de API", - "defaultModel": "Modelo padrão", - "defaultModelPlaceholder": "Por exemplo: llama3.1 ou claude-opus-5", - "labelRequired": "Dê um nome a este fornecedor.", - "providerSaveFailed": "Não foi possível salvar o provedor.", - "providerUpdated": "Fornecedor atualizado", - "providerDeleteFailed": "Não foi possível remover o provedor.", - "privateEndpointHint": "Os endereços na sua rede privada precisam que um administrador autorize o host primeiro.", - "providerOllama": "Ollama", - "providerAnthropic": "Antrópico", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Compatível com OpenAI", - "toolArguments": "Entrada", - "toolResult": "Resultado", - "statusApplied": "Aplicado", - "statusRejected": "Rejeitado", - "proposalApplied": "Alteração aplicada", - "proposalApplyFailed": "Não foi possível aplicar a alteração.", - "proposalRejectFailed": "Não foi possível rejeitar a alteração.", - "enableTitle": "Ative o assistente de IA", - "enableDescription": "Deixe o assistente ler sua configuração e sugerir alterações para você aprovar.", - "readOnlyCommands": "Permitir comandos de diagnóstico somente leitura", - "readOnlyCommandsDescription": "O assistente pode executar uma pequena lista de comandos seguros, como `df` e `uptime`, sem perguntar a cada vez. Qualquer alteração que faça no servidor será sempre sugerida primeiro.", - "modelDetectFailed": "Não foi possível contatar o fornecedor, portanto esta é uma lista genérica. Você pode digitar o nome de qualquer modelo.", - "modelCustom": "Insira o nome do modelo", - "modelRefresh": "Atualizar lista", - "modelPlaceholder": "Selecione ou digite um modelo", - "attachHint": "Digite @ para fazer referência a um host, snippet ou automação. As alterações são propostas para sua aprovação.", - "runCommandTitle": "Executar comando", - "assistant": "Assistente de IA", - "terminalPanelEmpty": "Pergunte ao assistente sobre este anfitrião. Qualquer alteração de estado precisa da sua aprovação prévia.", - "terminalPanelPlaceholder": "Pergunte sobre este anfitrião...", - "noProvidersConfigured": "Nenhum provedor de IA está configurado ainda. Adicione um na aba do assistente de IA.", - "runsInOpenTerminal": "Executa na sua sessão de terminal aberta.", - "runInTerminal": "Correr", - "terminalContextPrefill": "Aqui está a saída do meu terminal:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Permita que outros recursos usem sua chave de API do provedor de IA para suas próprias solicitações. A chave nunca é copiada e você pode revogá-la a qualquer momento." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "assistente de IA", - "aiIntro": "O Termix pode incluir um assistente de IA opcional. Você pode alterar isso posteriormente nas suas configurações.", - "aiSkipTitle": "Deixe desligado", - "aiSkipDesc": "O Termix funciona como está agora. O assistente permanece oculto.", - "aiEnableTitle": "Ligue-o", - "aiEnableDesc": "Adiciona um assistente que lê sua configuração e sugere alterações que você aprova. Requer um provedor fornecido por você, como um Ollama local.", - "aiNote": "Nada será enviado até que você adicione um provedor e envie uma mensagem." - }, - "hosts": { - "docsLink": "Documentação" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/pt_PT.json b/plugins/ai/locales/translated/pt_PT.json deleted file mode 100644 index 84675ebdd..000000000 --- a/plugins/ai/locales/translated/pt_PT.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Utilize o assistente", - "description": "Abra o assistente e coloque questões." - }, - "manage_providers": { - "title": "Gerir fornecedores", - "description": "Adicione, edite e remova os fornecedores de IA e as suas chaves API." - }, - "apply_proposals": { - "title": "Apresentar propostas", - "description": "Deixe que o assistente execute as alterações que sugerir." - }, - "services.use": { - "title": "Utilizar IA de outras funcionalidades", - "description": "Outras funcionalidades podem chamar o assistente em seu nome." - }, - "secrets.share": { - "title": "Partilhar a chave da API", - "description": "Outras funcionalidades podem utilizar a chave da API de IA armazenada sem a visualizar." - } - }, - "settings": { - "assistantUnavailable": "O assistente de IA está desativado neste servidor.", - "providersNeedOptIn": "Ative o assistente de IA acima para adicionar fornecedores.", - "admin": { - "globallyEnabled": { - "label": "assistente de IA", - "description": "Permita que os utilizadores ativem o assistente de IA. Quando estiver desativado, o assistente ficará oculto e bloqueado para todos." - }, - "privateEndpoints": { - "label": "Hosts de IA privados permitidos", - "description": "Servidores na sua rede privada que os utilizadores podem utilizar como fornecedores, como um Ollama auto-hospedado. Coloque um servidor em cada linha." - } - }, - "host": { - "enableAiAssistant": { - "label": "Ativar o assistente de IA", - "description": "Permita que o assistente de IA ajude no terminal deste host. Desativado por defeito, mas ainda precisa que a IA esteja ativada para a sua conta." - } - } - }, - "ai": { - "title": "Assistente", - "send": "Enviar", - "stop": "Stop", - "save": "Guardar", - "cancel": "Cancelar", - "running": "em execução", - "approve": "Aprovar", - "reject": "Rejeitar", - "inputPlaceholder": "Pergunte pelos seus servidores ou descreva uma alteração que gostaria de propor.", - "selectProvider": "Selecione um fornecedor", - "providerSettings": "Configurações do fornecedor", - "addProvider": "Adicionar provedor", - "editProvider": "Editar provedor", - "removeProvider": "Remover fornecedor", - "providerType": "Fornecedor", - "providerLabel": "Nome", - "providerLabelPlaceholder": "Por exemplo: Ollama local", - "baseUrl": "Endereço do servidor", - "apiKey": "Chave API", - "defaultModel": "Modelo padrão", - "defaultModelPlaceholder": "Por exemplo: llama3.1 ou claude-opus-5", - "labelRequired": "Dê um nome a este fornecedor.", - "providerSaveFailed": "Não foi possível guardar o fornecedor.", - "providerUpdated": "Fornecedor atualizado", - "providerDeleteFailed": "Não foi possível remover o fornecedor.", - "privateEndpointHint": "Os endereços na sua rede privada necessitam que um administrador autorize primeiro o host.", - "providerOllama": "Ollama", - "providerAnthropic": "Antrópico", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Compatível com OpenAI", - "toolArguments": "Entrada", - "toolResult": "Resultado", - "statusApplied": "Aplicado", - "statusRejected": "Rejeitado", - "proposalApplied": "Alteração aplicada", - "proposalApplyFailed": "Não foi possível aplicar a alteração.", - "proposalRejectFailed": "Não foi possível rejeitar a alteração.", - "enableTitle": "Ative o assistente de IA", - "enableDescription": "Deixe o assistente ler a sua configuração e sugerir alterações para que a aprove.", - "readOnlyCommands": "Permitir comandos de diagnóstico de leitura apenas", - "readOnlyCommandsDescription": "O assistente pode executar uma pequena lista de comandos seguros, como `df` e `uptime`, sem perguntar de cada vez. Qualquer alteração que faça no servidor será sempre sugerida em primeiro lugar.", - "modelDetectFailed": "Não foi possível contactar o fornecedor, pelo que esta é uma lista genérica. Pode digitar o nome de qualquer modelo.", - "modelCustom": "Introduza o nome do modelo", - "modelRefresh": "Atualizar lista", - "modelPlaceholder": "Selecione ou introduza um modelo", - "attachHint": "Digite @ para fazer referência a um host, snippet ou automação. As alterações são propostas para a sua aprovação.", - "runCommandTitle": "Executar comando", - "assistant": "Assistente de IA", - "terminalPanelEmpty": "Pergunte ao assistente sobre este anfitrião. Qualquer alteração de estado necessita da sua aprovação prévia.", - "terminalPanelPlaceholder": "Pergunte sobre este anfitrião...", - "noProvidersConfigured": "Nenhum fornecedor de IA está configurado ainda. Adicione um no separador do assistente de IA.", - "runsInOpenTerminal": "Executa na sua sessão de terminal aberta.", - "runInTerminal": "Executar", - "terminalContextPrefill": "Eis a saída do meu terminal:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Permita que outros recursos utilizem a sua chave API do fornecedor de IA para os seus próprios pedidos. A chave nunca é copiada e pode revogá-la a qualquer momento." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "assistente de IA", - "aiIntro": "O Termix pode incluir um assistente de IA opcional. Pode alterar isso posteriormente nas suas definições.", - "aiSkipTitle": "Deixe desligado", - "aiSkipDesc": "O Termix funciona como está agora. O assistente permanece oculto.", - "aiEnableTitle": "Ligue-o", - "aiEnableDesc": "Adiciona um assistente que lê a sua configuração e sugere alterações que aprova. Requer um fornecedor fornecido por si, como um Ollama local.", - "aiNote": "Nada será enviado até que adicione um fornecedor e envie uma mensagem." - }, - "hosts": { - "docsLink": "Ver documentação" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ro_RO.json b/plugins/ai/locales/translated/ro_RO.json deleted file mode 100644 index 9f6fa508b..000000000 --- a/plugins/ai/locales/translated/ro_RO.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Folosește asistentul", - "description": "Deschide asistentul și pune-i întrebări." - }, - "manage_providers": { - "title": "Gestionați furnizorii", - "description": "Adăugați, editați și eliminați furnizorii de inteligență artificială și cheile API ale acestora." - }, - "apply_proposals": { - "title": "Aplicați propunerile", - "description": "Lăsați asistentul să efectueze modificările sugerate de acesta." - }, - "services.use": { - "title": "Folosește inteligența artificială din alte funcții", - "description": "Alte funcții pot apela asistentul în numele tău." - }, - "secrets.share": { - "title": "Partajați cheia API", - "description": "Alte funcții pot utiliza cheia API AI stocată fără a o vedea." - } - }, - "settings": { - "assistantUnavailable": "Asistentul AI este dezactivat pe acest server.", - "providersNeedOptIn": "Activează asistentul AI de mai sus pentru a adăuga furnizori.", - "admin": { - "globallyEnabled": { - "label": "Asistent AI", - "description": "Permiteți utilizatorilor să activeze asistentul AI. Cât timp această opțiune este dezactivată, asistentul este ascuns și blocat pentru toată lumea." - }, - "privateEndpoints": { - "label": "Gazde private AI permise", - "description": "Gazde din rețeaua ta privată către care utilizatorii pot direcționa un furnizor, cum ar fi un Ollama auto-găzduit. Pune câte o gazdă pe fiecare linie." - } - }, - "host": { - "enableAiAssistant": { - "label": "Activează Asistentul AI", - "description": "Permiteți asistentului AI să vă ajute în terminalul acestei gazde. Dezactivat în mod implicit, dar este nevoie ca AI să fie activat pentru contul dvs." - } - } - }, - "ai": { - "title": "Asistent", - "send": "Trimite", - "stop": "Stop", - "save": "Salva", - "cancel": "Anula", - "running": "funcţionare", - "approve": "Aproba", - "reject": "Respinge", - "inputPlaceholder": "Întreabă despre chelnerii tăi sau descrie o schimbare pe care o propui.", - "selectProvider": "Selectați un furnizor", - "providerSettings": "Setări furnizor", - "addProvider": "Adăugați furnizor", - "editProvider": "Editați furnizorul", - "removeProvider": "Eliminați furnizorul", - "providerType": "Furnizor", - "providerLabel": "Nume", - "providerLabelPlaceholder": "De exemplu: Ollama locală", - "baseUrl": "Adresa serverului", - "apiKey": "Cheie API", - "defaultModel": "Model implicit", - "defaultModelPlaceholder": "De exemplu: llama3.1 sau claude-opus-5", - "labelRequired": "Dați un nume acestui furnizor", - "providerSaveFailed": "Nu s-a putut salva furnizorul", - "providerUpdated": "Furnizor actualizat", - "providerDeleteFailed": "Nu s-a putut elimina furnizorul", - "privateEndpointHint": "Adresele din rețeaua privată necesită mai întâi permisiunea unui administrator pentru gazdă.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropic", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemeni", - "providerOpenaiCompatible": "Compatibil cu OpenAI", - "toolArguments": "Intrare", - "toolResult": "Rezultat", - "statusApplied": "Aplicat", - "statusRejected": "Respins", - "proposalApplied": "Modificare aplicată", - "proposalApplyFailed": "Nu s-a putut aplica modificarea", - "proposalRejectFailed": "Nu s-a putut respinge modificarea", - "enableTitle": "Activează asistentul AI", - "enableDescription": "Permiteți asistentului să citească configurația și să sugereze modificări pentru aprobare.", - "readOnlyCommands": "Permite comenzi de diagnosticare doar în citire", - "readOnlyCommandsDescription": "Asistentul poate rula o listă scurtă de comenzi sigure, cum ar fi df și uptime, fără a întreba de fiecare dată. Orice modificare a unui server este întotdeauna propusă prima.", - "modelDetectFailed": "Nu s-a putut contacta furnizorul, așadar aceasta este o listă generală. Puteți introduce orice nume de model.", - "modelCustom": "Introduceți un nume de model", - "modelRefresh": "Reîmprospătați lista", - "modelPlaceholder": "Alegeți sau introduceți un model", - "attachHint": "Tastați @ pentru a face referire la o gazdă, un fragment de cod sau o automatizare. Sunt propuse modificări pentru aprobare.", - "runCommandTitle": "Comandă de rulare", - "assistant": "Asistent AI", - "terminalPanelEmpty": "Întreabă asistentul despre această gazdă. Orice modificare a stării necesită mai întâi aprobarea ta.", - "terminalPanelPlaceholder": "Întreabă despre această gazdă...", - "noProvidersConfigured": "Încă nu este configurat niciun furnizor de inteligență artificială. Adăugați unul în fila Asistent de inteligență artificială.", - "runsInOpenTerminal": "Rulează în sesiunea de terminal deschisă.", - "runInTerminal": "Aleargă", - "terminalContextPrefill": "Iată ieșirea terminalului meu:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Permiteți altor funcții să utilizeze cheia API a furnizorului dvs. de inteligență artificială pentru propriile solicitări. Cheia nu este niciodată copiată și puteți revoca această opțiune oricând." - } - }, - "nav": { - "ai": "IA" - }, - "onboarding": { - "aiTitle": "Asistent AI", - "aiIntro": "Termix poate include un asistent AI opțional. Puteți modifica acest lucru ulterior în setări.", - "aiSkipTitle": "Lasă-l deoparte", - "aiSkipDesc": "Termix funcționează ca acum. Asistentul rămâne ascuns.", - "aiEnableTitle": "Pornește-l", - "aiEnableDesc": "Adaugă un asistent care citește configurația și sugerează modificări pe care le aprobi. Necesită un furnizor furnizat de tine, cum ar fi un Ollama local.", - "aiNote": "Nimic nu este trimis nicăieri până nu adăugați un furnizor și nu trimiteți un mesaj." - }, - "hosts": { - "docsLink": "Vizualizați documentele" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/ru_RU.json b/plugins/ai/locales/translated/ru_RU.json deleted file mode 100644 index 05a750032..000000000 --- a/plugins/ai/locales/translated/ru_RU.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Воспользуйтесь помощником", - "description": "Откройте ассистента и задайте ему вопросы." - }, - "manage_providers": { - "title": "Управление поставщиками", - "description": "Добавляйте, редактируйте и удаляйте поставщиков ИИ и их API-ключи." - }, - "apply_proposals": { - "title": "Подайте заявки на участие в тендере.", - "description": "Пусть ассистент внесет предложенные им изменения." - }, - "services.use": { - "title": "Используйте ИИ из других функций.", - "description": "Другие функции могут вызывать голосового помощника от вашего имени." - }, - "secrets.share": { - "title": "Поделитесь ключом API", - "description": "Другие функции могут использовать сохраненный ключ API ИИ, даже не видя его." - } - }, - "settings": { - "assistantUnavailable": "На этом сервере ИИ-помощник отключен.", - "providersNeedOptIn": "Включите ИИ-помощника выше, чтобы добавить поставщиков услуг.", - "admin": { - "globallyEnabled": { - "label": "ИИ-помощник", - "description": "Разрешите пользователям включить голосового помощника. Если он выключен, помощник скрыт и заблокирован для всех." - }, - "privateEndpoints": { - "label": "Разрешены частные хосты ИИ", - "description": "Разместите на каждой линии по одному хосту в вашей частной сети, на которые пользователи могут направлять запросы провайдера, например, самостоятельно размещенный Ollama." - } - }, - "host": { - "enableAiAssistant": { - "label": "Включить ИИ-помощника", - "description": "Разрешите помощнику с искусственным интеллектом помогать на терминале этого хоста. По умолчанию отключено, но для вашей учетной записи ИИ все равно должен быть включен." - } - } - }, - "ai": { - "title": "Помощник", - "send": "Отправлять", - "stop": "Остановить", - "save": "Сохранить", - "cancel": "Отмена", - "running": "выполняется", - "approve": "Утвердить", - "reject": "Отклонять", - "inputPlaceholder": "Поинтересуйтесь состоянием ваших серверов или предложите внести изменения.", - "selectProvider": "Выберите поставщика", - "providerSettings": "Настройки поставщика", - "addProvider": "Добавить поставщика", - "editProvider": "Поставщик редактирования", - "removeProvider": "Удалить поставщика", - "providerType": "Поставщик", - "providerLabel": "Название", - "providerLabelPlaceholder": "Например: Местная лама", - "baseUrl": "Адрес сервера", - "apiKey": "ключ API", - "defaultModel": "Модель по умолчанию", - "defaultModelPlaceholder": "Например: llama3.1 или claude-opus-5", - "labelRequired": "Укажите имя этого поставщика услуг.", - "providerSaveFailed": "Не удалось сохранить поставщика услуг.", - "providerUpdated": "Поставщик обновлен", - "providerDeleteFailed": "Не удалось удалить поставщика услуг.", - "privateEndpointHint": "Для доступа к адресам в вашей частной сети необходимо предварительное разрешение администратора.", - "providerOllama": "оллама", - "providerAnthropic": "Антропический", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Совместимость с OpenAI", - "toolArguments": "Ввод", - "toolResult": "Результат", - "statusApplied": "Применяемый", - "statusRejected": "Отклоненный", - "proposalApplied": "Внесены изменения.", - "proposalApplyFailed": "Не удалось применить изменения.", - "proposalRejectFailed": "Не мог отклонить изменение.", - "enableTitle": "Включите ИИ-помощника", - "enableDescription": "Позвольте ассистенту ознакомиться с вашими настройками и предложить изменения для утверждения.", - "readOnlyCommands": "Разрешить диагностические команды только для чтения", - "readOnlyCommandsDescription": "Ассистент может выполнить короткий список безопасных команд, таких как df и uptime, не запрашивая подтверждения каждый раз. Любая команда, изменяющая состояние сервера, всегда предлагается в первую очередь.", - "modelDetectFailed": "Не удалось связаться с поставщиком, поэтому это общий список. Вы можете ввести любое название модели.", - "modelCustom": "Введите название модели", - "modelRefresh": "Обновить список", - "modelPlaceholder": "Выберите или введите модель", - "attachHint": "Введите @ для ссылки на хост, фрагмент кода или автоматизацию. Предлагаются изменения для вашего утверждения.", - "runCommandTitle": "Выполните команду", - "assistant": "ИИ-помощник", - "terminalPanelEmpty": "Спросите помощника об этом хосте. Любое изменение состояния требует вашего предварительного одобрения.", - "terminalPanelPlaceholder": "Спросите об этом хостинге...", - "noProvidersConfigured": "Поставщик ИИ пока не настроен. Добавьте его на вкладке «Помощник ИИ».", - "runsInOpenTerminal": "Запускается в открытой терминальной сессии.", - "runInTerminal": "Запустить", - "terminalContextPrefill": "Вот вывод моего терминала:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Разрешите другим функциям использовать ваш API-ключ поставщика ИИ для своих запросов. Ключ никогда не копируется, и вы можете отозвать его в любое время." - } - }, - "nav": { - "ai": "ИИ" - }, - "onboarding": { - "aiTitle": "ИИ-помощник", - "aiIntro": "В Termix можно добавить опционального ИИ-помощника. Это можно изменить позже в настройках.", - "aiSkipTitle": "Оставьте это выключенным.", - "aiSkipDesc": "Termix работает так, как сейчас. Ассистент остается скрытым.", - "aiEnableTitle": "Включите его", - "aiEnableDesc": "Добавляет помощника, который анализирует ваши настройки и предлагает изменения, которые вы одобряете. Требуется поставщик, которого вы предоставляете, например, местный Ollama.", - "aiNote": "Ничего никуда не отправляется, пока вы не добавите поставщика услуг и не отправите сообщение." - }, - "hosts": { - "docsLink": "Документация" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/sr_SP.json b/plugins/ai/locales/translated/sr_SP.json deleted file mode 100644 index ae986d0b3..000000000 --- a/plugins/ai/locales/translated/sr_SP.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Користите помоћника", - "description": "Отворите асистента и питајте га." - }, - "manage_providers": { - "title": "Управљајте добављачима", - "description": "Додајте, уредите и уклоните добављаче вештачке интелигенције и њихове АПИ кључеве." - }, - "apply_proposals": { - "title": "Примени предлоге", - "description": "Нека асистент изврши измене које предложи." - }, - "services.use": { - "title": "Користите вештачку интелигенцију из других функција", - "description": "Друге функције могу позвати асистента у ваше име." - }, - "secrets.share": { - "title": "Поделите API кључ", - "description": "Друге функције могу користити сачувани AI API кључ, а да га не виде." - } - }, - "settings": { - "assistantUnavailable": "АИ асистент је искључен на овом серверу.", - "providersNeedOptIn": "Укључите AI асистента изнад да бисте додали добављаче.", - "admin": { - "globallyEnabled": { - "label": "Асистент за вештачку интелигенцију", - "description": "Дозволите корисницима да укључе вештачку интелигенцију асистента. Док је ово искључено, асистент је скривен и блокиран за све." - }, - "privateEndpoints": { - "label": "Дозвољени приватни AI хостови", - "description": "Домаћини на вашој приватној мрежи на које корисници могу усмерити провајдера, као што је самостално хостовани Ollama. Ставите по један хост у сваки ред." - } - }, - "host": { - "enableAiAssistant": { - "label": "Омогући AI асистент", - "description": "Дозволите вештачкој интелигенцији да помогне у терминалу овог хоста. Искључено је подразумевано, а и даље је потребно да вештачка интелигенција буде омогућена за ваш налог." - } - } - }, - "ai": { - "title": "Асистент", - "send": "Пошаљи", - "stop": "Заустави", - "save": "Сачувај", - "cancel": "Откажи", - "running": "трчање", - "approve": "Одобри", - "reject": "Одбиј", - "inputPlaceholder": "Питајте о својим серверима или опишите промену коју желите да предложите", - "selectProvider": "Изаберите добављача", - "providerSettings": "Подешавања добављача", - "addProvider": "Додај добављача", - "editProvider": "Измени добављача", - "removeProvider": "Уклони добављача", - "providerType": "Провајдер", - "providerLabel": "Име", - "providerLabelPlaceholder": "На пример: Локална Олама", - "baseUrl": "Адреса сервера", - "apiKey": "API кључ", - "defaultModel": "Подразумевани модел", - "defaultModelPlaceholder": "На пример: llama3.1 или claude-opus-5", - "labelRequired": "Дајте име овом добављачу", - "providerSaveFailed": "Није могуће сачувати добављача", - "providerUpdated": "Провајдер је ажуриран", - "providerDeleteFailed": "Није могуће уклонити добављача", - "privateEndpointHint": "Адресе на вашој приватној мрежи прво треба да дозволе администратору хоста.", - "providerOllama": "Олама", - "providerAnthropic": "Антропски", - "providerOpenai": "OpenAI", - "providerGemini": "Гугл Џемини", - "providerOpenaiCompatible": "Компатибилно са OpenAI-ом", - "toolArguments": "Унос", - "toolResult": "Резултат", - "statusApplied": "Примењено", - "statusRejected": "Одбијено", - "proposalApplied": "Промена је примењена", - "proposalApplyFailed": "Није могуће применити промену", - "proposalRejectFailed": "Није могуће одбити промену", - "enableTitle": "Омогућите вештачку интелигенцију (AI) асистента", - "enableDescription": "Нека асистент прочита ваша подешавања и предложи измене које ћете одобрити.", - "readOnlyCommands": "Дозволи дијагностичке команде само за читање", - "readOnlyCommandsDescription": "Асистент може да покрене кратку листу безбедних команди као што су df и uptime без сваког питања. Све што мења сервер се увек прво предлаже.", - "modelDetectFailed": "Нисмо могли да контактирамо добављача, тако да је ово општа листа. Можете да унесете било који назив модела.", - "modelCustom": "Унесите назив модела", - "modelRefresh": "Освежи листу", - "modelPlaceholder": "Изаберите или унесите модел", - "attachHint": "Унесите @ да бисте референцирали хост, исечак кода или аутоматизацију. Промене се предлажу да бисте их одобрили.", - "runCommandTitle": "Покрени команду", - "assistant": "АИ асистент", - "terminalPanelEmpty": "Питајте асистента о овом домаћину. Све што мења стање прво захтева ваше одобрење.", - "terminalPanelPlaceholder": "Питајте о овом домаћину...", - "noProvidersConfigured": "Још увек није конфигурисан ниједан добављач вештачке интелигенције. Додајте га на картици помоћника за вештачку интелигенцију.", - "runsInOpenTerminal": "Покреће се у вашој отвореној терминалној сесији.", - "runInTerminal": "Трчи", - "terminalContextPrefill": "Ево мог терминалног излаза:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Дозволите другим функцијама да користе ваш API кључ добављача вештачке интелигенције за сопствене захтеве. Кључ се никада не копира и можете га опозвати у било ком тренутку." - } - }, - "nav": { - "ai": "Вештачка интелигенција" - }, - "onboarding": { - "aiTitle": "Асистент за вештачку интелигенцију", - "aiIntro": "Termix може да укључи опционог AI асистента. Ово можете касније променити у подешавањима.", - "aiSkipTitle": "Остави то искључено", - "aiSkipDesc": "Термикс ради као и сада. Асистент остаје скривен.", - "aiEnableTitle": "Укључи га", - "aiEnableDesc": "Додаје асистента који чита ваша подешавања и предлаже измене које одобравате. Потребан је добављач кога ви обезбедите, као што је локални Олама.", - "aiNote": "Ништа се не шаље нигде док не додате провајдера и не пошаљете поруку." - }, - "hosts": { - "docsLink": "Прикажи документе" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/sv_SE.json b/plugins/ai/locales/translated/sv_SE.json deleted file mode 100644 index 929000df7..000000000 --- a/plugins/ai/locales/translated/sv_SE.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Använd assistenten", - "description": "Öppna assistenten och fråga den saker." - }, - "manage_providers": { - "title": "Hantera leverantörer", - "description": "Lägg till, redigera och ta bort AI-leverantörerna och deras API-nycklar." - }, - "apply_proposals": { - "title": "Tillämpa förslag", - "description": "Låt assistenten utföra de ändringar den föreslår." - }, - "services.use": { - "title": "Använd AI från andra funktioner", - "description": "Andra funktioner kan ringa assistenten åt dig." - }, - "secrets.share": { - "title": "Dela API-nyckeln", - "description": "Andra funktioner kan använda den lagrade AI API-nyckeln utan att se den." - } - }, - "settings": { - "assistantUnavailable": "AI-assistenten är avstängd på den här servern.", - "providersNeedOptIn": "Aktivera AI-assistenten ovan för att lägga till leverantörer.", - "admin": { - "globallyEnabled": { - "label": "AI-assistent", - "description": "Låt användare aktivera AI-assistenten. Medan detta är avstängt är assistenten dold och blockerad för alla." - }, - "privateEndpoints": { - "label": "Tillåtna privata AI-värdar", - "description": "Värdar på ditt privata nätverk som användare kan hänvisa en leverantör till, till exempel en självhostad Ollama. Lägg en värd på varje rad." - } - }, - "host": { - "enableAiAssistant": { - "label": "Aktivera AI-assistenten", - "description": "Tillåt AI-assistenten att hjälpa till i den här värdens terminal. Av som standard och kräver fortfarande att AI är aktiverat för ditt konto." - } - } - }, - "ai": { - "title": "Assistent", - "send": "Skicka", - "stop": "Stopp", - "save": "Spara", - "cancel": "Avboka", - "running": "spring", - "approve": "Godkänna", - "reject": "Avvisa", - "inputPlaceholder": "Fråga om era servrar, eller beskriv en förändring att föreslå", - "selectProvider": "Välj en leverantör", - "providerSettings": "Leverantörsinställningar", - "addProvider": "Lägg till leverantör", - "editProvider": "Redigera leverantör", - "removeProvider": "Ta bort leverantör", - "providerType": "Leverantör", - "providerLabel": "Namn", - "providerLabelPlaceholder": "Till exempel: Lokal Ollama", - "baseUrl": "Serveradress", - "apiKey": "API-nyckel", - "defaultModel": "Standardmodell", - "defaultModelPlaceholder": "Till exempel: llama3.1 eller claude-opus-5", - "labelRequired": "Ge den här leverantören ett namn", - "providerSaveFailed": "Kunde inte spara leverantören", - "providerUpdated": "Leverantören uppdaterad", - "providerDeleteFailed": "Kunde inte ta bort leverantören", - "privateEndpointHint": "Adresser i ditt privata nätverk behöver en administratör som först tillåter värden.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropisk", - "providerOpenai": "Öppen AI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI-kompatibel", - "toolArguments": "Input", - "toolResult": "Resultat", - "statusApplied": "Tillämpad", - "statusRejected": "Avvisad", - "proposalApplied": "Ändring tillämpad", - "proposalApplyFailed": "Kunde inte tillämpa ändringen", - "proposalRejectFailed": "Kunde inte avvisa ändringen", - "enableTitle": "Aktivera AI-assistenten", - "enableDescription": "Låt assistenten läsa dina inställningar och föreslå ändringar som du kan godkänna.", - "readOnlyCommands": "Tillåt skrivskyddade diagnostikkommandon", - "readOnlyCommandsDescription": "Assistenten kan köra en kort lista med säkra kommandon som df och uptime utan att fråga varje gång. Allt som ändrar en server föreslås alltid först.", - "modelDetectFailed": "Kunde inte nå leverantören, så detta är en allmän lista. Du kan skriva in vilket modellnamn som helst.", - "modelCustom": "Ange ett modellnamn", - "modelRefresh": "Uppdatera listan", - "modelPlaceholder": "Välj eller skriv en modell", - "attachHint": "Skriv @ för att referera till en värd, ett kodavsnitt eller en automatisering. Ändringar föreslås för dig att godkänna.", - "runCommandTitle": "Kör kommando", - "assistant": "AI-assistent", - "terminalPanelEmpty": "Fråga assistenten om den här värden. Allt som ändras kräver ditt godkännande först.", - "terminalPanelPlaceholder": "Fråga om den här värden...", - "noProvidersConfigured": "Ingen AI-leverantör är konfigurerad ännu. Lägg till en på fliken AI-assistent.", - "runsInOpenTerminal": "Körs i din öppna terminalsession.", - "runInTerminal": "Sikt", - "terminalContextPrefill": "Här är min terminalutgång:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Låt andra funktioner använda din AI-leverantörs API-nyckel för sina egna förfrågningar. Nyckeln kopieras aldrig och du kan återkalla den när som helst." - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI-assistent", - "aiIntro": "Termix kan inkludera en valfri AI-assistent. Du kan ändra detta senare i dina inställningar.", - "aiSkipTitle": "Lämna den av", - "aiSkipDesc": "Termix fungerar som den gör nu. Assistenten förblir dold.", - "aiEnableTitle": "Slå på den", - "aiEnableDesc": "Lägger till en assistent som läser din installation och föreslår ändringar som du godkänner. Kräver en leverantör som du tillhandahåller, till exempel en lokal Ollama.", - "aiNote": "Ingenting skickas någonstans förrän du lägger till en leverantör och skickar ett meddelande." - }, - "hosts": { - "docsLink": "Visa dokument" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/th_TH.json b/plugins/ai/locales/translated/th_TH.json deleted file mode 100644 index 2aa4d8bd5..000000000 --- a/plugins/ai/locales/translated/th_TH.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "ใช้ตัวช่วย", - "description": "เปิดผู้ช่วยเสมือนจริงแล้วถามคำถามได้เลย" - }, - "manage_providers": { - "title": "จัดการผู้ให้บริการ", - "description": "เพิ่ม แก้ไข และลบผู้ให้บริการ AI และคีย์ API ของพวกเขา" - }, - "apply_proposals": { - "title": "ยื่นข้อเสนอ", - "description": "ให้ผู้ช่วยดำเนินการเปลี่ยนแปลงตามที่ระบบแนะนำ" - }, - "services.use": { - "title": "ใช้ AI จากฟีเจอร์อื่นๆ", - "description": "ฟังก์ชันอื่นๆ อาจโทรติดต่อผู้ช่วยในนามของคุณ" - }, - "secrets.share": { - "title": "แชร์คีย์ API", - "description": "ฟังก์ชันอื่นๆ อาจใช้คีย์ API ของ AI ที่บันทึกไว้โดยไม่เห็นคีย์นั้น" - } - }, - "settings": { - "assistantUnavailable": "ระบบผู้ช่วย AI ถูกปิดใช้งานบนเซิร์ฟเวอร์นี้", - "providersNeedOptIn": "เปิดใช้งานผู้ช่วย AI ด้านบนเพื่อเพิ่มผู้ให้บริการ", - "admin": { - "globallyEnabled": { - "label": "ผู้ช่วย AI", - "description": "อนุญาตให้ผู้ใช้เปิดใช้งานผู้ช่วย AI ได้ เมื่อปิดใช้งานอยู่ ผู้ช่วยจะถูกซ่อนและบล็อกไม่ให้ใครเห็น" - }, - "privateEndpoints": { - "label": "อนุญาตให้โฮสต์ AI ส่วนตัว", - "description": "โฮสต์บนเครือข่ายส่วนตัวของคุณที่ผู้ใช้สามารถชี้ไปยังผู้ให้บริการได้ เช่น Ollama ที่ติดตั้งเอง ให้ใส่โฮสต์หนึ่งตัวต่อหนึ่งบรรทัด" - } - }, - "host": { - "enableAiAssistant": { - "label": "เปิดใช้งานผู้ช่วย AI", - "description": "อนุญาตให้ผู้ช่วย AI ช่วยเหลือในเทอร์มินัลของโฮสต์นี้ โดยค่าเริ่มต้นจะปิดอยู่ และยังคงต้องเปิดใช้งาน AI สำหรับบัญชีของคุณก่อน" - } - } - }, - "ai": { - "title": "ผู้ช่วย", - "send": "ส่ง", - "stop": "หยุด", - "save": "บันทึก", - "cancel": "ยกเลิก", - "running": "วิ่ง", - "approve": "อนุมัติ", - "reject": "ปฏิเสธ", - "inputPlaceholder": "สอบถามเกี่ยวกับเซิร์ฟเวอร์ของคุณ หรืออธิบายการเปลี่ยนแปลงที่คุณต้องการเสนอแนะ", - "selectProvider": "เลือกผู้ให้บริการ", - "providerSettings": "การตั้งค่าผู้ให้บริการ", - "addProvider": "เพิ่มผู้ให้บริการ", - "editProvider": "แก้ไขผู้ให้บริการ", - "removeProvider": "ลบผู้ให้บริการ", - "providerType": "ผู้ให้บริการ", - "providerLabel": "ชื่อ", - "providerLabelPlaceholder": "ตัวอย่างเช่น: Ollama ท้องถิ่น", - "baseUrl": "ที่อยู่เซิร์ฟเวอร์", - "apiKey": "คีย์ API", - "defaultModel": "โมเดลเริ่มต้น", - "defaultModelPlaceholder": "ตัวอย่างเช่น: llama3.1 หรือ claude-opus-5", - "labelRequired": "ตั้งชื่อให้ผู้ให้บริการรายนี้", - "providerSaveFailed": "ไม่สามารถบันทึกผู้ให้บริการได้", - "providerUpdated": "ผู้ให้บริการได้รับการอัปเดตแล้ว", - "providerDeleteFailed": "ไม่สามารถลบผู้ให้บริการได้", - "privateEndpointHint": "การเข้าถึงที่อยู่บนเครือข่ายส่วนตัวของคุณนั้น จำเป็นต้องให้ผู้ดูแลระบบอนุญาตก่อน", - "providerOllama": "โอลาม่า", - "providerAnthropic": "แอนโทรปิก", - "providerOpenai": "โอเพ่นไอ", - "providerGemini": "กูเกิล เจมินี", - "providerOpenaiCompatible": "เข้ากันได้กับ OpenAI", - "toolArguments": "ป้อนข้อมูล", - "toolResult": "ผลลัพธ์", - "statusApplied": "สมัครแล้ว", - "statusRejected": "ถูกปฏิเสธ", - "proposalApplied": "มีการเปลี่ยนแปลงเกิดขึ้น", - "proposalApplyFailed": "ไม่สามารถทำการเปลี่ยนแปลงได้", - "proposalRejectFailed": "ไม่สามารถปฏิเสธการเปลี่ยนแปลงได้", - "enableTitle": "เปิดใช้งานผู้ช่วย AI", - "enableDescription": "ให้ผู้ช่วยอ่านการตั้งค่าของคุณและแนะนำการเปลี่ยนแปลงเพื่อให้คุณอนุมัติ", - "readOnlyCommands": "อนุญาตคำสั่งวินิจฉัยแบบอ่านอย่างเดียว", - "readOnlyCommandsDescription": "ผู้ช่วยอาจเรียกใช้คำสั่งที่ปลอดภัยเพียงไม่กี่คำสั่ง เช่น df และ uptime โดยไม่ต้องถามทุกครั้ง คำสั่งใดๆ ที่เปลี่ยนแปลงสถานะของเซิร์ฟเวอร์จะถูกเสนอให้ดำเนินการก่อนเสมอ", - "modelDetectFailed": "ไม่สามารถติดต่อผู้ให้บริการได้ ดังนั้นนี่จึงเป็นรายการทั่วไป คุณสามารถพิมพ์ชื่อรุ่นใดก็ได้", - "modelCustom": "ป้อนชื่อรุ่น", - "modelRefresh": "รีเฟรชรายการ", - "modelPlaceholder": "เลือกหรือพิมพ์แบบจำลอง", - "attachHint": "พิมพ์ @ เพื่ออ้างอิงถึงโฮสต์ สนิปเป็ต หรือระบบอัตโนมัติ การเปลี่ยนแปลงที่เสนอจะถูกนำมาให้คุณอนุมัติ", - "runCommandTitle": "เรียกใช้คำสั่ง", - "assistant": "ผู้ช่วย AI", - "terminalPanelEmpty": "สอบถามผู้ช่วยเกี่ยวกับโฮสต์นี้ การเปลี่ยนแปลงสถานะใดๆ ต้องได้รับการอนุมัติจากคุณก่อน", - "terminalPanelPlaceholder": "สอบถามเกี่ยวกับโฮสต์รายนี้...", - "noProvidersConfigured": "ยังไม่ได้ตั้งค่าผู้ให้บริการ AI เพิ่มผู้ให้บริการในแท็บผู้ช่วย AI", - "runsInOpenTerminal": "ทำงานในเซสชันเทอร์มินัลที่เปิดอยู่ของคุณ", - "runInTerminal": "วิ่ง", - "terminalContextPrefill": "นี่คือผลลัพธ์จากเทอร์มินัลของฉัน:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "อนุญาตให้ฟีเจอร์อื่นๆ ใช้คีย์ API ของผู้ให้บริการ AI ของคุณสำหรับการร้องขอของตนเอง คีย์นี้จะไม่ถูกคัดลอก และคุณสามารถยกเลิกได้ทุกเมื่อ" - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "ผู้ช่วย AI", - "aiIntro": "Termix อาจมีผู้ช่วย AI เสริม คุณสามารถเปลี่ยนการตั้งค่าได้ในภายหลัง", - "aiSkipTitle": "ปิดมันไว้", - "aiSkipDesc": "Termix ทำงานเหมือนเดิมทุกประการ ตัวช่วยยังคงซ่อนอยู่", - "aiEnableTitle": "เปิดใช้งาน", - "aiEnableDesc": "เพิ่มผู้ช่วยที่จะอ่านการตั้งค่าของคุณและแนะนำการเปลี่ยนแปลงที่คุณต้องอนุมัติ จำเป็นต้องมีผู้ให้บริการที่คุณจัดหาให้ เช่น Ollama ในพื้นที่ของคุณ", - "aiNote": "จะไม่มีการส่งข้อมูลใดๆ จนกว่าคุณจะเพิ่มผู้ให้บริการและส่งข้อความ" - }, - "hosts": { - "docsLink": "ดูเอกสาร" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/tr_TR.json b/plugins/ai/locales/translated/tr_TR.json deleted file mode 100644 index 394681dd6..000000000 --- a/plugins/ai/locales/translated/tr_TR.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Yardımcıyı kullanın", - "description": "Asistanı açın ve ona sorular sorun." - }, - "manage_providers": { - "title": "Sağlayıcıları yönetin", - "description": "Yapay zeka sağlayıcılarını ve API anahtarlarını ekleyin, düzenleyin ve kaldırın." - }, - "apply_proposals": { - "title": "Tekliflerinizi sunun.", - "description": "Yardımcının önerdiği değişiklikleri uygulamasına izin verin." - }, - "services.use": { - "title": "Diğer özelliklerden yapay zeka kullanın.", - "description": "Diğer özellikler sizin adınıza asistanı arayabilir." - }, - "secrets.share": { - "title": "API anahtarını paylaşın", - "description": "Diğer özellikler, kaydedilmiş yapay zeka API anahtarını görmeden kullanabilir." - } - }, - "settings": { - "assistantUnavailable": "Bu sunucuda yapay zeka asistanı devre dışı bırakılmıştır.", - "providersNeedOptIn": "Sağlayıcı eklemek için yukarıdaki yapay zeka asistanını açın.", - "admin": { - "globallyEnabled": { - "label": "Yapay zeka asistanı", - "description": "Kullanıcıların yapay zeka asistanını açmasına izin verin. Bu özellik kapalıyken, asistan gizlenir ve herkes için engellenir." - }, - "privateEndpoints": { - "label": "Özel yapay zeka sunucularına izin verildi.", - "description": "Kullanıcıların bir sağlayıcıya yönlendirebileceği özel ağınızdaki sunucular (örneğin, kendi sunucunuzda barındırdığınız Ollama). Her satıra bir sunucu yazın." - } - }, - "host": { - "enableAiAssistant": { - "label": "Yapay Zeka Asistanını Etkinleştir", - "description": "Bu sunucunun terminalinde yapay zeka asistanının yardımcı olmasına izin verin. Varsayılan olarak kapalıdır ve hesabınız için yapay zekanın etkinleştirilmiş olması gerekir." - } - } - }, - "ai": { - "title": "Asistan", - "send": "Göndermek", - "stop": "Durdurma", - "save": "Kaydet", - "cancel": "İptal", - "running": "çalışan", - "approve": "Onaylamak", - "reject": "Reddetmek", - "inputPlaceholder": "Sunucularınız hakkında bilgi isteyin veya önereceğiniz bir değişikliği açıklayın.", - "selectProvider": "Bir sağlayıcı seçin", - "providerSettings": "Sağlayıcı ayarları", - "addProvider": "Sağlayıcı ekle", - "editProvider": "Sağlayıcıyı düzenle", - "removeProvider": "Sağlayıcıyı kaldır", - "providerType": "Sağlayıcı", - "providerLabel": "Ad", - "providerLabelPlaceholder": "Örneğin: Yerel Ollama", - "baseUrl": "Sunucu adresi", - "apiKey": "API anahtarı", - "defaultModel": "Varsayılan model", - "defaultModelPlaceholder": "Örneğin: llama3.1 veya claude-opus-5", - "labelRequired": "Bu sağlayıcıya bir isim verin.", - "providerSaveFailed": "Sağlayıcı kaydedilemedi.", - "providerUpdated": "Sağlayıcı güncellendi.", - "providerDeleteFailed": "Sağlayıcı kaldırılamadı.", - "privateEndpointHint": "Özel ağınızdaki adreslerin kullanılabilmesi için öncelikle yöneticinin sunucuya izin vermesi gerekmektedir.", - "providerOllama": "Ollama", - "providerAnthropic": "Antropik", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "OpenAI uyumlu", - "toolArguments": "Giriş", - "toolResult": "Sonuç", - "statusApplied": "Uygulandı", - "statusRejected": "Reddedilmiş", - "proposalApplied": "Değişiklik uygulandı.", - "proposalApplyFailed": "Değişiklik uygulanamadı.", - "proposalRejectFailed": "Değişikliği reddedemedim.", - "enableTitle": "Yapay zekâ asistanını etkinleştirin", - "enableDescription": "Asistanın ayarlarınızı okumasını ve onaylamanız için değişiklik önerileri sunmasını sağlayın.", - "readOnlyCommands": "Yalnızca okuma amaçlı tanı komutlarına izin ver", - "readOnlyCommandsDescription": "Yardımcı program, her seferinde sormadan df ve uptime gibi kısa bir güvenli komut listesini çalıştırabilir. Sunucuda değişiklik yapan her şey her zaman önce önerilir.", - "modelDetectFailed": "Sağlayıcıya ulaşılamadı, bu nedenle bu genel bir listedir. İstediğiniz model adını yazabilirsiniz.", - "modelCustom": "Bir model adı girin", - "modelRefresh": "Listeyi yenile", - "modelPlaceholder": "Bir model seçin veya yazın.", - "attachHint": "Bir sunucuya, kod parçasına veya otomasyona referans vermek için @ işaretini kullanın. Onaylamanız için değişiklikler öneriliyor.", - "runCommandTitle": "Çalıştır komutu", - "assistant": "Yapay Zeka Asistanı", - "terminalPanelEmpty": "Bu ev sahibi hakkında asistana danışın. Durum değişikliği gerektiren her şey önce sizin onayınızı gerektirir.", - "terminalPanelPlaceholder": "Bu ev sahibi hakkında bilgi isteyin...", - "noProvidersConfigured": "Henüz hiçbir yapay zeka sağlayıcısı yapılandırılmadı. Yapay zeka asistanı sekmesinden bir tane ekleyin.", - "runsInOpenTerminal": "Açık terminal oturumunuzda çalışır.", - "runInTerminal": "Çalıştır", - "terminalContextPrefill": "İşte terminal çıktım:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Diğer özelliklerin kendi istekleri için yapay zeka sağlayıcınızın API anahtarını kullanmasına izin verin. Anahtar asla kopyalanmaz ve bunu istediğiniz zaman iptal edebilirsiniz." - } - }, - "nav": { - "ai": "Yapay zeka" - }, - "onboarding": { - "aiTitle": "Yapay zeka asistanı", - "aiIntro": "Termix isteğe bağlı bir yapay zeka asistanı içerebilir. Bunu daha sonra ayarlarınızdan değiştirebilirsiniz.", - "aiSkipTitle": "Kapalı bırakın", - "aiSkipDesc": "Termix şu anki haliyle çalışıyor. Asistan gizli kalıyor.", - "aiEnableTitle": "Açın", - "aiEnableDesc": "Ayarlarınızı okuyan ve onaylamanız halinde değişiklikler öneren bir asistan ekler. Yerel bir Ollama gibi, sizin sağladığınız bir sağlayıcıya ihtiyaç duyar.", - "aiNote": "Bir sağlayıcı ekleyip mesaj gönderene kadar hiçbir şey hiçbir yere gönderilmez." - }, - "hosts": { - "docsLink": "Dokümanları görüntüle" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/uk_UA.json b/plugins/ai/locales/translated/uk_UA.json deleted file mode 100644 index 53752f19d..000000000 --- a/plugins/ai/locales/translated/uk_UA.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Використовуйте помічника", - "description": "Відкрийте помічника та запитайте його." - }, - "manage_providers": { - "title": "Керування постачальниками", - "description": "Додавайте, редагуйте та видаляйте постачальників штучного інтелекту та їхні ключі API." - }, - "apply_proposals": { - "title": "Застосувати пропозиції", - "description": "Нехай помічник внесе запропоновані ним зміни." - }, - "services.use": { - "title": "Використання ШІ з інших функцій", - "description": "Інші функції можуть викликати помічника від вашого імені." - }, - "secrets.share": { - "title": "Поділитися ключем API", - "description": "Інші функції можуть використовувати збережений ключ API штучного інтелекту, не бачачи його." - } - }, - "settings": { - "assistantUnavailable": "Помічник зі штучним інтелектом вимкнено на цьому сервері.", - "providersNeedOptIn": "Увімкніть помічника зі штучним інтелектом вище, щоб додати постачальників.", - "admin": { - "globallyEnabled": { - "label": "Асистент зі штучним інтелектом", - "description": "Дозвольте користувачам увімкнути помічника зі штучним інтелектом. Коли це вимкнено, помічник прихований і заблокований для всіх." - }, - "privateEndpoints": { - "label": "Дозволені приватні хости ШІ", - "description": "Хости у вашій приватній мережі, на які користувачі можуть вказувати провайдера, наприклад, Ollama з власним хостингом. Розмістіть по одному хосту в кожному рядку." - } - }, - "host": { - "enableAiAssistant": { - "label": "Увімкнути помічника зі штучним інтелектом", - "description": "Дозволити помічнику зі штучним інтелектом допомагати в терміналі цього хоста. Вимкнено за замовчуванням, але все одно потребує ввімкнення штучного інтелекту для вашого облікового запису." - } - } - }, - "ai": { - "title": "Помічник", - "send": "Надіслати", - "stop": "СТІЙ", - "save": "Зберегти", - "cancel": "Скасувати", - "running": "біг", - "approve": "Схвалити", - "reject": "Відхилити", - "inputPlaceholder": "Запитайте про ваші сервери або опишіть зміну, яку ви хочете запропонувати", - "selectProvider": "Виберіть постачальника", - "providerSettings": "Налаштування постачальника", - "addProvider": "Додати постачальника", - "editProvider": "Редагувати постачальника", - "removeProvider": "Видалити постачальника", - "providerType": "Постачальник", - "providerLabel": "Ім'я", - "providerLabelPlaceholder": "Наприклад: Місцевий Оллама", - "baseUrl": "Адреса сервера", - "apiKey": "Ключ API", - "defaultModel": "Модель за замовчуванням", - "defaultModelPlaceholder": "Наприклад: llama3.1 або claude-opus-5", - "labelRequired": "Дайте назву цьому постачальнику", - "providerSaveFailed": "Не вдалося зберегти постачальника", - "providerUpdated": "Постачальника оновлено", - "providerDeleteFailed": "Не вдалося видалити постачальника", - "privateEndpointHint": "Адреси у вашій приватній мережі спочатку потребують дозволу адміністратора хоста.", - "providerOllama": "Оллама", - "providerAnthropic": "Антропний", - "providerOpenai": "OpenAI", - "providerGemini": "Google Близнюки", - "providerOpenaiCompatible": "Сумісний з OpenAI", - "toolArguments": "Вхід", - "toolResult": "Результат", - "statusApplied": "Застосовано", - "statusRejected": "Відхилено", - "proposalApplied": "Зміну застосовано", - "proposalApplyFailed": "Не вдалося застосувати зміну", - "proposalRejectFailed": "Не вдалося відхилити зміну", - "enableTitle": "Увімкнути помічника зі штучним інтелектом", - "enableDescription": "Дозвольте асистенту прочитати ваші налаштування та запропонувати зміни для вашого схвалення.", - "readOnlyCommands": "Дозволити діагностичні команди лише для читання", - "readOnlyCommandsDescription": "Помічник може виконувати короткий список безпечних команд, таких як df та uptime, не запитуючи щоразу. Будь-які зміни на сервері завжди пропонуються першими.", - "modelDetectFailed": "Не вдалося зв’язатися з постачальником, тому це загальний список. Ви можете ввести будь-яку назву моделі.", - "modelCustom": "Введіть назву моделі", - "modelRefresh": "Оновити список", - "modelPlaceholder": "Виберіть або введіть модель", - "attachHint": "Введіть @, щоб посилатися на хост, фрагмент або автоматизацію. Зміни пропонуються для вашого схвалення.", - "runCommandTitle": "Виконати команду", - "assistant": "Помічник зі штучним інтелектом", - "terminalPanelEmpty": "Запитайте асистента про цього хоста. Будь-яка зміна стану потребує вашого попереднього схвалення.", - "terminalPanelPlaceholder": "Запитайте про цього хоста...", - "noProvidersConfigured": "Постачальника штучного інтелекту ще не налаштовано. Додайте його на вкладці помічника штучного інтелекту.", - "runsInOpenTerminal": "Працює у вашому відкритому термінальному сеансі.", - "runInTerminal": "Бігти", - "terminalContextPrefill": "Ось мій вивід з терміналу:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Дозвольте іншим функціям використовувати ваш ключ API постачальника штучного інтелекту для власних запитів. Ключ ніколи не копіюється, і ви можете скасувати його будь-коли." - } - }, - "nav": { - "ai": "Штучний інтелект" - }, - "onboarding": { - "aiTitle": "Асистент зі штучним інтелектом", - "aiIntro": "Termix може включати додатковий помічник зі штучним інтелектом. Ви можете змінити це пізніше в налаштуваннях.", - "aiSkipTitle": "Залиште це вимкненим", - "aiSkipDesc": "Termix працює як і зараз. Помічник залишається прихованим.", - "aiEnableTitle": "Увімкніть це", - "aiEnableDesc": "Додає помічника, який читає ваші налаштування та пропонує зміни, які ви схвалюєте. Потрібен постачальник, якого ви надаєте, наприклад, місцевий Ollama.", - "aiNote": "Нічого нікуди не надсилається, доки ви не додасте постачальника та не надішлете повідомлення." - }, - "hosts": { - "docsLink": "Переглянути документи" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/vi_VN.json b/plugins/ai/locales/translated/vi_VN.json deleted file mode 100644 index a1651020e..000000000 --- a/plugins/ai/locales/translated/vi_VN.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "Sử dụng trợ lý", - "description": "Mở trợ lý ảo và hỏi nó những câu hỏi." - }, - "manage_providers": { - "title": "Quản lý nhà cung cấp", - "description": "Thêm, chỉnh sửa và xóa các nhà cung cấp AI và khóa API của họ." - }, - "apply_proposals": { - "title": "Áp dụng các đề xuất", - "description": "Hãy để trợ lý thực hiện những thay đổi mà nó đề xuất." - }, - "services.use": { - "title": "Sử dụng trí tuệ nhân tạo từ các tính năng khác", - "description": "Các tính năng khác có thể gọi trợ lý ảo thay mặt bạn." - }, - "secrets.share": { - "title": "Chia sẻ khóa API", - "description": "Các tính năng khác có thể sử dụng khóa API AI đã lưu trữ mà không cần nhìn thấy khóa đó." - } - }, - "settings": { - "assistantUnavailable": "Trợ lý AI đã bị tắt trên máy chủ này.", - "providersNeedOptIn": "Hãy bật trợ lý AI ở trên để thêm nhà cung cấp.", - "admin": { - "globallyEnabled": { - "label": "Trợ lý AI", - "description": "Cho phép người dùng bật trợ lý AI. Khi chức năng này tắt, trợ lý sẽ bị ẩn và chặn đối với tất cả mọi người." - }, - "privateEndpoints": { - "label": "Cho phép máy chủ AI riêng", - "description": "Các máy chủ trên mạng riêng của bạn mà người dùng có thể trỏ đến nhà cung cấp, chẳng hạn như Ollama tự lưu trữ. Đặt một máy chủ trên mỗi dòng." - } - }, - "host": { - "enableAiAssistant": { - "label": "Kích hoạt Trợ lý AI", - "description": "Cho phép trợ lý AI hỗ trợ trong thiết bị đầu cuối của máy chủ này. Mặc định là tắt, nhưng bạn vẫn cần bật AI cho tài khoản của mình." - } - } - }, - "ai": { - "title": "Trợ lý", - "send": "Gửi", - "stop": "Bit dừng", - "save": "Lưu", - "cancel": "Hủy", - "running": "đang chạy", - "approve": "Chấp thuận", - "reject": "Từ chối", - "inputPlaceholder": "Hãy hỏi về máy chủ của bạn, hoặc mô tả một thay đổi mà bạn muốn đề xuất.", - "selectProvider": "Chọn nhà cung cấp", - "providerSettings": "Cài đặt nhà cung cấp", - "addProvider": "Thêm nhà cung cấp", - "editProvider": "Chỉnh sửa nhà cung cấp", - "removeProvider": "Xóa nhà cung cấp", - "providerType": "Nhà cung cấp", - "providerLabel": "Tên", - "providerLabelPlaceholder": "Ví dụ: Ollama địa phương", - "baseUrl": "Địa chỉ máy chủ", - "apiKey": "Khóa API", - "defaultModel": "Mô hình mặc định", - "defaultModelPlaceholder": "Ví dụ: llama3.1 hoặc claude-opus-5", - "labelRequired": "Đặt tên cho nhà cung cấp này", - "providerSaveFailed": "Không thể lưu nhà cung cấp", - "providerUpdated": "Nhà cung cấp đã cập nhật", - "providerDeleteFailed": "Không thể xóa nhà cung cấp", - "privateEndpointHint": "Các địa chỉ trên mạng riêng của bạn cần được quản trị viên cho phép truy cập trước.", - "providerOllama": "Ollama", - "providerAnthropic": "Nhân loại", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "Tương thích với OpenAI", - "toolArguments": "Nhập", - "toolResult": "Kết quả", - "statusApplied": "Đã áp dụng", - "statusRejected": "Vật bị loại bỏ", - "proposalApplied": "Thay đổi đã được áp dụng", - "proposalApplyFailed": "Không thể áp dụng thay đổi", - "proposalRejectFailed": "Không thể từ chối thay đổi", - "enableTitle": "Kích hoạt trợ lý AI", - "enableDescription": "Hãy để trợ lý đọc cấu hình của bạn và đề xuất các thay đổi để bạn phê duyệt.", - "readOnlyCommands": "Cho phép các lệnh chẩn đoán chỉ đọc", - "readOnlyCommandsDescription": "Trợ lý có thể chạy một danh sách ngắn các lệnh an toàn như df và uptime mà không cần hỏi mỗi lần. Bất kỳ lệnh nào thay đổi máy chủ đều được đề xuất trước tiên.", - "modelDetectFailed": "Không thể liên lạc được với nhà cung cấp, vì vậy đây là danh sách chung. Bạn có thể nhập bất kỳ tên mẫu nào.", - "modelCustom": "Nhập tên mẫu", - "modelRefresh": "Làm mới danh sách", - "modelPlaceholder": "Chọn hoặc nhập mô hình", - "attachHint": "Nhập @ để tham chiếu đến máy chủ, đoạn mã hoặc quy trình tự động hóa. Các thay đổi được đề xuất để bạn phê duyệt.", - "runCommandTitle": "Chạy lệnh", - "assistant": "Trợ lý AI", - "terminalPanelEmpty": "Hãy hỏi trợ lý về máy chủ này. Bất cứ thay đổi nào về trạng thái đều cần sự chấp thuận của bạn trước.", - "terminalPanelPlaceholder": "Hãy hỏi về chủ nhà này...", - "noProvidersConfigured": "Chưa có nhà cung cấp AI nào được cấu hình. Hãy thêm một nhà cung cấp trong tab trợ lý AI.", - "runsInOpenTerminal": "Chương trình này chạy trong cửa sổ dòng lệnh đang mở của bạn.", - "runInTerminal": "Chạy", - "terminalContextPrefill": "Đây là kết quả hiển thị trên terminal của tôi:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "Cho phép các tính năng khác sử dụng khóa API của nhà cung cấp AI của bạn cho các yêu cầu riêng của chúng. Khóa này không bao giờ được sao chép và bạn có thể thu hồi nó bất cứ lúc nào." - } - }, - "nav": { - "ai": "Trí tuệ nhân tạo" - }, - "onboarding": { - "aiTitle": "Trợ lý AI", - "aiIntro": "Termix có thể bao gồm trợ lý AI tùy chọn. Bạn có thể thay đổi điều này sau trong phần cài đặt.", - "aiSkipTitle": "Đừng làm vậy", - "aiSkipDesc": "Termix vẫn hoạt động như hiện tại. Trợ lý ảo vẫn ẩn mình.", - "aiEnableTitle": "Bật nó lên", - "aiEnableDesc": "Thêm một trợ lý đọc cấu hình của bạn và đề xuất các thay đổi mà bạn chấp thuận. Cần có nhà cung cấp do bạn chỉ định, chẳng hạn như Ollama địa phương.", - "aiNote": "Không có dữ liệu nào được gửi đi cho đến khi bạn thêm nhà cung cấp và gửi tin nhắn." - }, - "hosts": { - "docsLink": "Xem tài liệu" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/zh_CN.json b/plugins/ai/locales/translated/zh_CN.json deleted file mode 100644 index d639fea78..000000000 --- a/plugins/ai/locales/translated/zh_CN.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "使用助手", - "description": "打开助手并向它提问。" - }, - "manage_providers": { - "title": "管理供应商", - "description": "添加、编辑和删除人工智能提供商及其 API 密钥。" - }, - "apply_proposals": { - "title": "提交提案", - "description": "让助手执行它建议的更改。" - }, - "services.use": { - "title": "利用其他功能中的人工智能", - "description": "其他功能可能会代表您呼叫语音助手。" - }, - "secrets.share": { - "title": "分享 API 密钥", - "description": "其他功能可能在看不到密钥的情况下使用已存储的 AI API 密钥。" - } - }, - "settings": { - "assistantUnavailable": "此服务器上的AI助手已关闭。", - "providersNeedOptIn": "开启上方的AI助手,即可添加服务提供商。", - "admin": { - "globallyEnabled": { - "label": "AI 助手", - "description": "允许用户开启 AI 助手。关闭此功能后,助手将被隐藏,所有用户均无法使用。" - }, - "privateEndpoints": { - "label": "允许的私有 AI 主机", - "description": "用户可向服务提供商指向的私有网络上的主机,例如自托管的 Ollama。每行填写一个主机。" - } - }, - "host": { - "enableAiAssistant": { - "label": "启用人工智能助手", - "description": "允许 AI 助手在此主机的终端上提供帮助。默认情况下处于关闭状态,但仍需在您的帐户中启用 AI 功能。" - } - } - }, - "ai": { - "title": "助手", - "send": "发送", - "stop": "停止", - "save": "保存", - "cancel": "取消", - "running": "运行中", - "approve": "批准", - "reject": "拒绝", - "inputPlaceholder": "询问服务器相关情况,或描述要提出的变更建议", - "selectProvider": "选择服务提供商", - "providerSettings": "提供商设置", - "addProvider": "添加提供商", - "editProvider": "编辑提供商", - "removeProvider": "移除提供者", - "providerType": "提供商", - "providerLabel": "名称", - "providerLabelPlaceholder": "例如:本地 Ollama", - "baseUrl": "服务器地址", - "apiKey": "API 密钥", - "defaultModel": "默认模型", - "defaultModelPlaceholder": "例如:llama3.1 或 claude-opus-5", - "labelRequired": "给此提供商起个名字", - "providerSaveFailed": "无法保存提供商", - "providerUpdated": "提供商已更新", - "providerDeleteFailed": "无法移除提供商", - "privateEndpointHint": "您私有网络上的地址需要管理员先允许主机访问。", - "providerOllama": "Ollama", - "providerAnthropic": "Anthropic", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "兼容 OpenAI", - "toolArguments": "输入", - "toolResult": "结果", - "statusApplied": "已应用", - "statusRejected": "已拒绝", - "proposalApplied": "变更已生效", - "proposalApplyFailed": "无法应用更改", - "proposalRejectFailed": "无法拒绝更改", - "enableTitle": "启用 AI 助手", - "enableDescription": "让助手读取您的设置并提出更改建议,供您批准。", - "readOnlyCommands": "允许只读诊断命令", - "readOnlyCommandsDescription": "助手可能会在不每次都询问的情况下运行一些安全命令,例如 `df` 和 `uptime`。任何会更改服务器的操作都会优先被提出。", - "modelDetectFailed": "无法联系到提供商,所以这里提供的是通用列表。您可以输入任何模型名称。", - "modelCustom": "请输入模型名称", - "modelRefresh": "刷新列表", - "modelPlaceholder": "选择或输入模型", - "attachHint": "输入 @ 符号可引用主机、代码片段或自动化流程。系统会提出更改建议,供您批准。", - "runCommandTitle": "运行命令", - "assistant": "人工智能助手", - "terminalPanelEmpty": "向助理询问有关此房东的信息。任何状态变更都需要您先批准。", - "terminalPanelPlaceholder": "询问一下这位房东的情况……", - "noProvidersConfigured": "尚未配置任何 AI 提供商。请在 AI 助手选项卡中添加。", - "runsInOpenTerminal": "在打开的终端会话中运行。", - "runInTerminal": "运行", - "terminalContextPrefill": "以下是我的终端输出:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "允许其他功能使用您的 AI 提供商 API 密钥发出各自的请求。该密钥绝不会被复制,您可以随时撤销此授权。" - } - }, - "nav": { - "ai": "AI" - }, - "onboarding": { - "aiTitle": "AI 助手", - "aiIntro": "Termix 可以包含一个可选的 AI 助手。您可以稍后在设置中更改此选项。", - "aiSkipTitle": "禁用", - "aiSkipDesc": "Termix 正常工作,AI 助手将保持隐藏状态。", - "aiEnableTitle": "启用", - "aiEnableDesc": "启用 AI 助手,它将分析您的配置并提供优化建议。需自行配置服务商,例如本地运行的 Ollama。", - "aiNote": "在您配置服务商并主动发送消息之前,绝不会向外发送任何数据。" - }, - "hosts": { - "docsLink": "查看文档" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "运行远程 AI Agent", - "permissionDescription": "使用已配置的 AI Provider 在 SSH 主机上启动 Agent。Agent 可使用 SSH 账号权限执行命令和修改文件。", - "new": "新建会话", - "description": "在此主机的目录中运行 Agent,使用 Termix 配置的模型服务。", - "runtime": "Agent", - "provider": "Termix Provider", - "chooseProvider": "选择兼容的 Provider", - "model": "模型", - "directory": "工作目录", - "executable": "可执行文件(可选绝对路径)", - "requirements": "主机需要 Node.js 22.19+ 及所选 Agent,并允许 SSH 回环地址远程转发。不覆盖已有 Agent 配置。", - "responsesRequired": "Codex 需要 Responses API;仅支持 Chat Completions 的接口无法用于 Codex。", - "piPermissions": "Pi 工具使用当前 SSH 账号权限执行。请只在允许 Agent 修改的目录中启动。", - "start": "启动 Agent", - "resume": "恢复会话", - "stop": "停止会话", - "interrupt": "中断任务", - "send": "发送", - "prompt": "让 Agent 在此目录中完成任务…", - "noSessions": "此主机还没有 Agent 会话。", - "approval": "Agent 需要你的决定", - "answer": "填写答复(结构化问题使用 JSON)", - "allow": "允许一次", - "deny": "拒绝", - "tool": "工具执行", - "streamFailed": "Agent 消息连接断开,请重新打开会话连接。", - "status": { - "starting": "正在启动", - "ready": "就绪", - "running": "执行中", - "stopped": "已停止", - "error": "出错" - }, - "installRuntime": "安装所选 Agent 和 Node.js", - "installSource": "仅从官方来源安装:nodejs.org(SHA-256 校验)、registry.npmjs.org(锁定依赖与完整性校验)。安装到当前 SSH 用户的 ~/.local/share/termix-agent-runtime;需要 curl、tar 和 Linux/macOS x64 或 arm64。不修改 SSH 转发策略。", - "installSucceeded": "安装及版本校验成功。现在可以启动 Agent;SSH 还需允许回环地址远程转发。", - "installFailed": "安装失败,请查看日志后重试。", - "installLog": "安装日志", - "enableForwarding": "一键启用 Agent 转发", - "forwardingScope": "需要 Linux、systemd/OpenSSH,以及 root 或免密 sudo。自动备份、校验并重载 SSH,仅为当前 SSH 账号与实际连接来源开放回环远程转发,保留已有本地转发权限。失败自动回滚。", - "forwardingSucceeded": "SSH 回环远程转发已就绪。请启动或恢复 Agent,建立新的 SSH 连接。", - "forwardingFailed": "SSH 转发配置失败,请查看日志;未通过校验的配置不会保留。", - "search": "搜索会话", - "showArchived": "显示已归档", - "sessionName": "会话名称", - "rename": "重命名", - "archive": "归档并停止", - "restore": "恢复归档", - "queue": "待发消息", - "enqueue": "加入队列", - "continueQueue": "继续队列", - "pauseQueue": "暂停队列", - "editQueued": "编辑待发消息", - "save": "保存", - "remove": "移除", - "upload": "上传附件", - "remoteFile": "工作目录内的文件路径", - "attach": "引用文件", - "attachments": "复用会话附件", - "pasteImage": "可在输入框粘贴图片。每条最多 4 个附件,每个不超过 1 MiB。", - "attachmentLimit": "每条最多 4 个附件,每个不超过 1 MiB", - "review": "Git 改动与 worktree", - "gitStatus": "刷新 Git 状态", - "diffFile": "要审阅的文件路径", - "diff": "查看 diff", - "branch": "新分支名称", - "createWorktree": "创建 worktree", - "useWorktree": "在此 worktree 新建会话", - "worktreeHint": "从 HEAD 创建新分支。未提交改动保留在当前目录,不会自动删除已有 worktree。", - "clean": "没有改动", - "reconnecting": "正在重连并补齐消息…", - "unavailable": "无法访问会话,请检查权限或重新登录。", - "shortcuts": "Enter 发送或排队 · Shift+Enter 换行 · Esc 中断并暂停队列" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/locales/translated/zh_TW.json b/plugins/ai/locales/translated/zh_TW.json deleted file mode 100644 index 5361d88da..000000000 --- a/plugins/ai/locales/translated/zh_TW.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "permissions": { - "use": { - "title": "使用助手", - "description": "打開助手並向它提問。" - }, - "manage_providers": { - "title": "管理供應商", - "description": "新增、編輯和刪除人工智慧提供者及其 API 金鑰。" - }, - "apply_proposals": { - "title": "提交提案", - "description": "讓助手執行它建議的更改。" - }, - "services.use": { - "title": "利用其他功能中的人工智慧", - "description": "其他功能可能會代表您呼叫語音助理。" - }, - "secrets.share": { - "title": "分享 API 金鑰", - "description": "其他功能可能會在不查看金鑰的情況下使用已儲存的 AI API 金鑰。" - } - }, - "settings": { - "assistantUnavailable": "此伺服器上的AI助理已關閉。", - "providersNeedOptIn": "開啟上方的AI助手,即可新增服務提供者。", - "admin": { - "globallyEnabled": { - "label": "人工智慧助手", - "description": "允許用戶開啟AI助理。關閉此功能後,助手將被隱藏,所有用戶均無法存取。" - }, - "privateEndpoints": { - "label": "允許的私有人工智慧主機", - "description": "使用者可將服務提供者指向的私有網路上的主機,例如自架的 Ollama。每行填寫一個主機。" - } - }, - "host": { - "enableAiAssistant": { - "label": "啟用人工智慧助手", - "description": "允許 AI 助理在此主機的終端機上提供協助。預設為關閉狀態,但仍需在您的帳戶中啟用 AI 功能。" - } - } - }, - "ai": { - "title": "助理", - "send": "傳送", - "stop": "停止位元", - "save": "儲存", - "cancel": "取消", - "running": "執行中", - "approve": "核准", - "reject": "拒絕", - "inputPlaceholder": "詢問伺服器相關情況,或描述要提出的變更建議", - "selectProvider": "選擇服務提供者", - "providerSettings": "提供者設定", - "addProvider": "新增提供者", - "editProvider": "編輯提供者", - "removeProvider": "移除提供者", - "providerType": "提供者", - "providerLabel": "名稱", - "providerLabelPlaceholder": "例如:本地奧拉馬", - "baseUrl": "伺服器位址", - "apiKey": "API金鑰", - "defaultModel": "預設模型", - "defaultModelPlaceholder": "例如:llama3.1 或 claude-opus-5", - "labelRequired": "給這家服務提供者取個名字", - "providerSaveFailed": "無法儲存提供者", - "providerUpdated": "提供者已更新", - "providerDeleteFailed": "無法移除提供者", - "privateEndpointHint": "您私有網路上的位址需要管理員先允許主機存取。", - "providerOllama": "奧拉瑪", - "providerAnthropic": "人類學", - "providerOpenai": "OpenAI", - "providerGemini": "Google Gemini", - "providerOpenaiCompatible": "相容 OpenAI", - "toolArguments": "輸入", - "toolResult": "結果", - "statusApplied": "應用", - "statusRejected": "已拒絕", - "proposalApplied": "變更已生效", - "proposalApplyFailed": "無法套用變更", - "proposalRejectFailed": "無法拒絕更改", - "enableTitle": "啟用人工智慧助手", - "enableDescription": "請助手讀取您的設定並提出更改建議,供您批准。", - "readOnlyCommands": "允許只讀診斷命令", - "readOnlyCommandsDescription": "助手可能會在不每次都詢問的情況下執行一些安全的命令,例如 `df` 和 `uptime`。任何會更改伺服器的操作都會優先被提出。", - "modelDetectFailed": "無法聯繫到供應商,所以這裡提供的是通用清單。您可以輸入任何型號名稱。", - "modelCustom": "請輸入型號名稱", - "modelRefresh": "重新整理清單", - "modelPlaceholder": "選擇或輸入型號", - "attachHint": "輸入 @ 符號可引用主機、程式碼片段或自動化流程。系統會提出更改建議,供您批准。", - "runCommandTitle": "運行命令", - "assistant": "人工智慧助手", - "terminalPanelEmpty": "向助理詢問有關此房東的資訊。任何狀態變更都需要您先批准。", - "terminalPanelPlaceholder": "詢問一下這位房東的狀況…", - "noProvidersConfigured": "尚未配置任何 AI 提供者。請在 AI 助理標籤中新增。", - "runsInOpenTerminal": "在開啟的終端機會話中運行。", - "runInTerminal": "執行", - "terminalContextPrefill": "以下是我的終端輸出:\n\n{{output}}\n\n", - "secrets": { - "shareApiKey": "允許其他功能使用您的 AI 提供者 API 金鑰發出各自的請求。該密鑰絕不會被複製,您可以隨時撤銷此授權。" - } - }, - "nav": { - "ai": "人工智慧" - }, - "onboarding": { - "aiTitle": "人工智慧助手", - "aiIntro": "Termix 可以包含一個可選的 AI 助手。您可以稍後在設定中變更此選項。", - "aiSkipTitle": "丟開", - "aiSkipDesc": "Termix 的運作方式與現在相同。助手保持隱藏狀態。", - "aiEnableTitle": "打開它", - "aiEnableDesc": "添加一個助手,它可以讀取您的設定並提出您批准的更改建議。需要您提供服務提供者,例如當地的 Ollama 服務提供者。", - "aiNote": "在您新增服務提供者並發送訊息之前,任何內容都不會發送到任何地方。" - }, - "hosts": { - "docsLink": "檢視文件" - }, - "agents": { - "title": "AI Agent", - "permissionTitle": "Run remote AI agents", - "permissionDescription": "Start coding agents on SSH hosts using configured AI providers. Agents can execute commands and change files as the SSH user.", - "new": "New session", - "description": "Run an agent in a directory on this host using a Termix AI provider.", - "runtime": "Agent", - "provider": "Termix provider", - "chooseProvider": "Choose a compatible provider", - "model": "Model", - "directory": "Working directory", - "executable": "Executable (optional absolute path)", - "requirements": "The host needs Node.js 22.19+ and the selected agent installed. SSH remote loopback forwarding must be allowed. Existing agent configurations are not overwritten.", - "responsesRequired": "Codex requires a Responses API endpoint. A Chat Completions-only endpoint cannot run Codex.", - "piPermissions": "Pi tools run with this SSH account’s permissions. Only launch in a directory you authorize the agent to modify.", - "start": "Start agent", - "resume": "Resume", - "stop": "Stop session", - "interrupt": "Interrupt turn", - "send": "Send", - "prompt": "Ask the agent to work in this directory…", - "noSessions": "No agent sessions for this host.", - "approval": "Agent needs your decision", - "answer": "Answer (JSON for structured questions)", - "allow": "Allow once", - "deny": "Deny", - "tool": "Tool activity", - "streamFailed": "Agent stream disconnected. Reopen the session to reconnect.", - "status": { - "starting": "Starting", - "ready": "Ready", - "running": "Working", - "stopped": "Stopped", - "error": "Error" - }, - "installRuntime": "Install selected agent and Node.js", - "installSource": "Official sources only: nodejs.org (SHA-256 verified) and registry.npmjs.org (locked package integrity). Installs for the SSH user in ~/.local/share/termix-agent-runtime; requires curl, tar, and Linux/macOS x64 or arm64. Does not change SSH forwarding policy.", - "installSucceeded": "Installation verified. You can now start the agent; SSH loopback remote forwarding must also be allowed.", - "installFailed": "Installation failed. Review the log and retry.", - "installLog": "Installation log", - "enableForwarding": "Enable Agent forwarding", - "forwardingScope": "Linux with systemd/OpenSSH; requires root or passwordless sudo. Backs up and validates SSH configuration, then reloads it. Only the current SSH account and observed connection source receive loopback remote forwarding; existing local forwarding is preserved. Failed changes are rolled back.", - "forwardingSucceeded": "SSH loopback remote forwarding is ready. Start or resume the agent to open a new SSH connection.", - "forwardingFailed": "SSH forwarding configuration failed. Review the log; no unvalidated configuration is kept.", - "search": "Search sessions", - "showArchived": "Show archived", - "sessionName": "Session name", - "rename": "Rename", - "archive": "Archive and stop", - "restore": "Restore session", - "queue": "Queued messages", - "enqueue": "Queue message", - "continueQueue": "Continue queue", - "pauseQueue": "Pause queue", - "editQueued": "Edit queued message", - "save": "Save", - "remove": "Remove", - "upload": "Upload attachment", - "remoteFile": "File path inside working directory", - "attach": "Attach file", - "attachments": "Reuse a session attachment", - "pasteImage": "Paste an image in the composer. Up to 4 attachments, 1 MiB each.", - "attachmentLimit": "Up to 4 attachments, 1 MiB each", - "review": "Git changes and worktrees", - "gitStatus": "Refresh Git status", - "diffFile": "File path to review", - "diff": "View diff", - "branch": "New branch name", - "createWorktree": "Create worktree", - "useWorktree": "New session in this worktree", - "worktreeHint": "Creates a new branch from HEAD. Uncommitted changes stay in the current directory. Existing worktrees are never deleted automatically.", - "clean": "No changes", - "reconnecting": "Reconnecting and restoring messages…", - "unavailable": "Session is no longer accessible. Check your permissions or sign in again.", - "shortcuts": "Enter sends or queues · Shift+Enter adds a line · Esc interrupts and pauses the queue" - }, - "plugin": { - "name": "AI Assistant" - } -} diff --git a/plugins/ai/manifest.json b/plugins/ai/manifest.json deleted file mode 100644 index c74c31b8a..000000000 --- a/plugins/ai/manifest.json +++ /dev/null @@ -1,207 +0,0 @@ -{ - "id": "ai", - "name": "AI Assistant", - "version": "1.0.0", - "description": "An optional AI assistant that reads your setup and proposes changes for you to approve.", - "author": { - "name": "Termix" - }, - "license": "Apache-2.0", - "repository": "https://github.com/Termix-SSH/Termix", - "category": "Productivity", - "icon": "Sparkles", - "engine": { - "termix": ">=26.10.0", - "api": "1" - }, - "capabilities": [ - "hosts:read", - "hosts:write", - "credentials:use", - "ssh:connect", - "db:own", - "secrets:own", - "network:outbound", - "notify:send", - "network:serve", - "ui:surface", - "users:impersonate", - "events:core" - ], - "optionalDependencies": { - "workspaces": "^1.0.0", - "network-topology": "^1.0.0", - "snippets": "^1.0.0", - "fleets": "^1.0.0", - "ssh-terminal": "^1.0.0", - "automations": "^1.0.0", - "homepage": "^1.0.0" - }, - "requires": [ - { - "service": "workspaces.saved", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "network-topology.graph", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "snippets.access", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "fleets.access", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "terminal.history", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "automations.access", - "versionRange": "^1.0.0", - "optional": true - }, - { - "service": "homepage.items", - "versionRange": "^1.0.0", - "optional": true - } - ], - "providesSecret": [ - { - "key": "api-key", - "permission": "ai.secrets.share", - "descriptionKey": "ai.secrets.shareApiKey" - } - ], - "contributes": { - "tabs": [ - { - "id": "ai", - "titleKey": "nav.ai", - "icon": "Sparkles", - "openFrom": ["rail", "palette"] - }, - { - "id": "ai-agents", - "titleKey": "agents.title", - "icon": "Bot", - "openFrom": ["host-context-menu", "palette"] - } - ], - "actions": [ - { - "id": "ai.openWithContext", - "titleKey": "ai.assistant", - "handler": "openWithContext", - "icon": "Bot", - "permission": "ai.services.use", - "slot": "terminal.toolbar", - "kind": "button" - } - ], - "permissions": [ - { - "name": "use", - "titleKey": "permissions.use.title", - "descriptionKey": "permissions.use.description", - "defaultRoles": ["user"] - }, - { - "name": "manage_providers", - "titleKey": "permissions.manage_providers.title", - "descriptionKey": "permissions.manage_providers.description", - "defaultRoles": ["user"] - }, - { - "name": "apply_proposals", - "titleKey": "permissions.apply_proposals.title", - "descriptionKey": "permissions.apply_proposals.description", - "defaultRoles": ["user"] - }, - { - "name": "services.use", - "titleKey": "permissions.services.use.title", - "descriptionKey": "permissions.services.use.description", - "defaultRoles": ["user"] - }, - { - "name": "secrets.share", - "titleKey": "permissions.secrets.share.title", - "descriptionKey": "permissions.secrets.share.description", - "defaultRoles": ["user"] - }, - { - "name": "agents", - "titleKey": "agents.permissionTitle", - "descriptionKey": "agents.permissionDescription", - "defaultRoles": ["admin"] - } - ], - "settings": { - "admin": [ - { - "key": "globallyEnabled", - "type": "boolean", - "labelKey": "settings.admin.globallyEnabled.label", - "descriptionKey": "settings.admin.globallyEnabled.description", - "default": false - }, - { - "key": "privateEndpoints", - "type": "textarea", - "labelKey": "settings.admin.privateEndpoints.label", - "descriptionKey": "settings.admin.privateEndpoints.description", - "requires": "globallyEnabled", - "default": "localhost\n127.0.0.1\n::1\nhost.docker.internal" - } - ], - "user": [ - { - "key": "enabled", - "type": "boolean", - "labelKey": "ai.enableTitle", - "descriptionKey": "ai.enableDescription", - "default": false - }, - { - "key": "allowReadOnlyCommands", - "type": "boolean", - "labelKey": "ai.readOnlyCommands", - "descriptionKey": "ai.readOnlyCommandsDescription", - "requires": "enabled", - "default": false - }, - { - "key": "providers", - "type": "custom", - "component": "providers", - "permission": "manage_providers" - } - ], - "host": { - "editorGroup": "ssh", - "editorOrder": 90, - "fields": [ - { - "key": "enableAiAssistant", - "type": "boolean", - "labelKey": "settings.host.enableAiAssistant.label", - "descriptionKey": "settings.host.enableAiAssistant.description", - "default": false - } - ] - } - }, - "uses": ["terminal.sidePanel", "terminal.toolbar"] - }, - "locales": "locales", - "platforms": ["linux", "win32", "darwin"] -} diff --git a/plugins/ai/migrations/mysql/0001_adopt_ai_tables.sql b/plugins/ai/migrations/mysql/0001_adopt_ai_tables.sql deleted file mode 100644 index 6abe55137..000000000 --- a/plugins/ai/migrations/mysql/0001_adopt_ai_tables.sql +++ /dev/null @@ -1,55 +0,0 @@ --- ai 0001: adopt_ai_tables --- Generated by termix-plugin migrations. Review before committing. --- Hand-edited: keeps the legacy foreign key from messages and proposals to --- their conversation, which the SDK cannot declare. - -CREATE TABLE IF NOT EXISTS `ai_providers` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `user_id` varchar(255) NOT NULL, - `provider_type` text NOT NULL, - `label` varchar(255) NOT NULL, - `base_url` text, - `api_key_prefix` text, - `default_model` text, - `enabled` boolean NOT NULL DEFAULT true, - `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP), - `updated_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP), - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE -); -RENAME TABLE `ai_providers` TO `p_ai_providers`; -CREATE TABLE IF NOT EXISTS `ai_conversations` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `user_id` varchar(255) NOT NULL, - `title` text, - `provider_id` int, - `model` text, - `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP), - `updated_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP), - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE -); -RENAME TABLE `ai_conversations` TO `p_ai_conversations`; -CREATE TABLE IF NOT EXISTS `ai_messages` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `conversation_id` int NOT NULL, - `role` text NOT NULL, - `content` text NOT NULL DEFAULT (''), - `tool_calls` text, - `created_at` varchar(255) NOT NULL DEFAULT (CURRENT_TIMESTAMP), - FOREIGN KEY (`conversation_id`) REFERENCES `p_ai_conversations` (`id`) ON DELETE CASCADE -); -RENAME TABLE `ai_messages` TO `p_ai_messages`; -CREATE TABLE IF NOT EXISTS `ai_proposals` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `conversation_id` int NOT NULL, - `user_id` varchar(255) NOT NULL, - `kind` varchar(255) NOT NULL, - `summary` text, - `payload` text NOT NULL DEFAULT ('{}'), - `status` varchar(32) NOT NULL DEFAULT 'pending', - `applied_at` text, - `result_summary` text, - `created_at` text NOT NULL DEFAULT (CURRENT_TIMESTAMP), - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, - FOREIGN KEY (`conversation_id`) REFERENCES `p_ai_conversations` (`id`) ON DELETE CASCADE -); -RENAME TABLE `ai_proposals` TO `p_ai_proposals`; diff --git a/plugins/ai/migrations/mysql/0002_provider_sync.sql b/plugins/ai/migrations/mysql/0002_provider_sync.sql deleted file mode 100644 index 3652ae1c6..000000000 --- a/plugins/ai/migrations/mysql/0002_provider_sync.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE `p_ai_providers` ADD COLUMN `sync_id` varchar(255) UNIQUE; diff --git a/plugins/ai/migrations/mysql/0003_agent_sessions.sql b/plugins/ai/migrations/mysql/0003_agent_sessions.sql deleted file mode 100644 index 76c6e8ce2..000000000 --- a/plugins/ai/migrations/mysql/0003_agent_sessions.sql +++ /dev/null @@ -1,46 +0,0 @@ --- ai 0003: agent_sessions --- Generated by termix-plugin migrations. Review before committing. - -CREATE TABLE IF NOT EXISTS `p_ai_agent_sessions` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `session_id` varchar(36) NOT NULL UNIQUE, - `user_id` varchar(255) NOT NULL, - `host_id` int NOT NULL, - `provider_id` int NOT NULL, - `agent` varchar(32) NOT NULL, - `model` text NOT NULL, - `cwd` text NOT NULL, - `executable` text NOT NULL, - `native_id` text, - `title` text, - `archived` boolean NOT NULL DEFAULT false, - `queue_paused` boolean NOT NULL DEFAULT false, - `draft` text, - `attachments` text NOT NULL DEFAULT ('[]'), - `updated_at` text NOT NULL, - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE -); -CREATE INDEX `idx_ai_agent_sessions_user` ON `p_ai_agent_sessions` (`user_id`); -CREATE TABLE IF NOT EXISTS `p_ai_agent_events` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `session_id` varchar(36) NOT NULL, - `user_id` varchar(255) NOT NULL, - `seq` int NOT NULL, - `kind` varchar(16) NOT NULL, - `text` text NOT NULL, - `request_id` text, - `choices` text, - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE -); -CREATE UNIQUE INDEX `idx_ai_agent_events_session_seq` ON `p_ai_agent_events` (`session_id`, `seq`); -CREATE TABLE IF NOT EXISTS `p_ai_agent_queue` ( - `id` int AUTO_INCREMENT PRIMARY KEY, - `session_id` varchar(36) NOT NULL, - `user_id` varchar(255) NOT NULL, - `prompt_id` varchar(36) NOT NULL, - `position` int NOT NULL, - `text` text NOT NULL, - `attachment_ids` text NOT NULL DEFAULT ('[]'), - FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE -); -CREATE INDEX `idx_ai_agent_queue_session` ON `p_ai_agent_queue` (`session_id`, `position`); diff --git a/plugins/ai/migrations/postgres/0001_adopt_ai_tables.sql b/plugins/ai/migrations/postgres/0001_adopt_ai_tables.sql deleted file mode 100644 index b872c9a41..000000000 --- a/plugins/ai/migrations/postgres/0001_adopt_ai_tables.sql +++ /dev/null @@ -1,60 +0,0 @@ --- ai 0001: adopt_ai_tables --- Generated by termix-plugin migrations. Review before committing. --- Hand-edited: keeps the legacy foreign key from messages and proposals to --- their conversation, which the SDK cannot declare. - -CREATE TABLE IF NOT EXISTS "ai_providers" ( - "id" serial PRIMARY KEY, - "user_id" varchar(255) NOT NULL, - "provider_type" text NOT NULL, - "label" varchar(255) NOT NULL, - "base_url" text, - "api_key_prefix" text, - "default_model" text, - "enabled" boolean NOT NULL DEFAULT true, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE UNIQUE INDEX IF NOT EXISTS "idx_ai_providers_user_label" ON "ai_providers" ("user_id", "label"); -ALTER TABLE "ai_providers" RENAME TO "p_ai_providers"; -CREATE TABLE IF NOT EXISTS "ai_conversations" ( - "id" serial PRIMARY KEY, - "user_id" varchar(255) NOT NULL, - "title" text, - "provider_id" integer, - "model" text, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" varchar(255) NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_conversations_user" ON "ai_conversations" ("user_id", "updated_at"); -ALTER TABLE "ai_conversations" RENAME TO "p_ai_conversations"; -CREATE TABLE IF NOT EXISTS "ai_messages" ( - "id" serial PRIMARY KEY, - "conversation_id" integer NOT NULL, - "role" text NOT NULL, - "content" text NOT NULL DEFAULT '', - "tool_calls" text, - "created_at" varchar(255) NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("conversation_id") REFERENCES "p_ai_conversations" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_messages_conversation" ON "ai_messages" ("conversation_id", "created_at"); -ALTER TABLE "ai_messages" RENAME TO "p_ai_messages"; -CREATE TABLE IF NOT EXISTS "ai_proposals" ( - "id" serial PRIMARY KEY, - "conversation_id" integer NOT NULL, - "user_id" varchar(255) NOT NULL, - "kind" varchar(255) NOT NULL, - "summary" text, - "payload" text NOT NULL DEFAULT '{}', - "status" varchar(32) NOT NULL DEFAULT 'pending', - "applied_at" text, - "result_summary" text, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE, - FOREIGN KEY ("conversation_id") REFERENCES "p_ai_conversations" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_proposals_user" ON "ai_proposals" ("user_id", "status"); -CREATE INDEX IF NOT EXISTS "idx_ai_proposals_conversation" ON "ai_proposals" ("conversation_id"); -ALTER TABLE "ai_proposals" RENAME TO "p_ai_proposals"; diff --git a/plugins/ai/migrations/postgres/0002_provider_sync.sql b/plugins/ai/migrations/postgres/0002_provider_sync.sql deleted file mode 100644 index f5c46713b..000000000 --- a/plugins/ai/migrations/postgres/0002_provider_sync.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE "p_ai_providers" ADD COLUMN "sync_id" varchar(255) UNIQUE; diff --git a/plugins/ai/migrations/postgres/0003_agent_sessions.sql b/plugins/ai/migrations/postgres/0003_agent_sessions.sql deleted file mode 100644 index b79c1e8ee..000000000 --- a/plugins/ai/migrations/postgres/0003_agent_sessions.sql +++ /dev/null @@ -1,46 +0,0 @@ --- ai 0003: agent_sessions --- Generated by termix-plugin migrations. Review before committing. - -CREATE TABLE IF NOT EXISTS "p_ai_agent_sessions" ( - "id" serial PRIMARY KEY, - "session_id" varchar(36) NOT NULL UNIQUE, - "user_id" varchar(255) NOT NULL, - "host_id" integer NOT NULL, - "provider_id" integer NOT NULL, - "agent" varchar(32) NOT NULL, - "model" text NOT NULL, - "cwd" text NOT NULL, - "executable" text NOT NULL, - "native_id" text, - "title" text, - "archived" boolean NOT NULL DEFAULT false, - "queue_paused" boolean NOT NULL DEFAULT false, - "draft" text, - "attachments" text NOT NULL DEFAULT '[]', - "updated_at" text NOT NULL, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_agent_sessions_user" ON "p_ai_agent_sessions" ("user_id"); -CREATE TABLE IF NOT EXISTS "p_ai_agent_events" ( - "id" serial PRIMARY KEY, - "session_id" varchar(36) NOT NULL, - "user_id" varchar(255) NOT NULL, - "seq" integer NOT NULL, - "kind" varchar(16) NOT NULL, - "text" text NOT NULL, - "request_id" text, - "choices" text, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE UNIQUE INDEX IF NOT EXISTS "idx_ai_agent_events_session_seq" ON "p_ai_agent_events" ("session_id", "seq"); -CREATE TABLE IF NOT EXISTS "p_ai_agent_queue" ( - "id" serial PRIMARY KEY, - "session_id" varchar(36) NOT NULL, - "user_id" varchar(255) NOT NULL, - "prompt_id" varchar(36) NOT NULL, - "position" integer NOT NULL, - "text" text NOT NULL, - "attachment_ids" text NOT NULL DEFAULT '[]', - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_agent_queue_session" ON "p_ai_agent_queue" ("session_id", "position"); diff --git a/plugins/ai/migrations/snapshot.json b/plugins/ai/migrations/snapshot.json deleted file mode 100644 index 872577b88..000000000 --- a/plugins/ai/migrations/snapshot.json +++ /dev/null @@ -1,368 +0,0 @@ -{ - "version": 1, - "tables": { - "providers": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "syncId": { - "type": "varchar", - "length": 255, - "unique": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "providerType": { - "type": "text", - "notNull": true - }, - "label": { - "type": "varchar", - "length": 255, - "notNull": true - }, - "baseUrl": { - "type": "text" - }, - "apiKeyPrefix": { - "type": "text" - }, - "defaultModel": { - "type": "text" - }, - "enabled": { - "type": "boolean", - "notNull": true, - "defaultValue": true - }, - "createdAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - }, - "updatedAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - } - }, - "indexes": [ - { - "name": "idx_ai_providers_user_label", - "columns": ["userId", "label"], - "unique": true - } - ], - "adopts": "ai_providers" - }, - "conversations": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "title": { - "type": "text" - }, - "providerId": { - "type": "integer" - }, - "model": { - "type": "text" - }, - "createdAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - }, - "updatedAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - } - }, - "indexes": [ - { - "name": "idx_ai_conversations_user", - "columns": ["userId", "updatedAt"] - } - ], - "adopts": "ai_conversations" - }, - "messages": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "conversationId": { - "type": "integer", - "notNull": true - }, - "role": { - "type": "text", - "notNull": true - }, - "content": { - "type": "text", - "notNull": true, - "defaultValue": "" - }, - "toolCalls": { - "type": "text" - }, - "createdAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - } - }, - "indexes": [ - { - "name": "idx_ai_messages_conversation", - "columns": ["conversationId", "createdAt"] - } - ], - "adopts": "ai_messages" - }, - "proposals": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "conversationId": { - "type": "integer", - "notNull": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "kind": { - "type": "varchar", - "length": 255, - "notNull": true - }, - "summary": { - "type": "text" - }, - "payload": { - "type": "text", - "notNull": true, - "defaultValue": "{}" - }, - "status": { - "type": "varchar", - "length": 32, - "notNull": true, - "defaultValue": "pending" - }, - "appliedAt": { - "type": "text" - }, - "resultSummary": { - "type": "text" - }, - "createdAt": { - "type": "timestamp", - "notNull": true, - "defaultNow": true - } - }, - "indexes": [ - { - "name": "idx_ai_proposals_user", - "columns": ["userId", "status"] - }, - { - "name": "idx_ai_proposals_conversation", - "columns": ["conversationId"] - } - ], - "adopts": "ai_proposals" - }, - "agent_sessions": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "sessionId": { - "type": "varchar", - "length": 36, - "notNull": true, - "unique": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "hostId": { - "type": "integer", - "notNull": true - }, - "providerId": { - "type": "integer", - "notNull": true - }, - "agent": { - "type": "varchar", - "length": 32, - "notNull": true - }, - "model": { - "type": "text", - "notNull": true - }, - "cwd": { - "type": "text", - "notNull": true - }, - "executable": { - "type": "text", - "notNull": true - }, - "nativeId": { - "type": "text" - }, - "title": { - "type": "text" - }, - "archived": { - "type": "boolean", - "notNull": true, - "defaultValue": false - }, - "queuePaused": { - "type": "boolean", - "notNull": true, - "defaultValue": false - }, - "draft": { - "type": "text" - }, - "attachments": { - "type": "text", - "notNull": true, - "defaultValue": "[]" - }, - "updatedAt": { - "type": "text", - "notNull": true - } - }, - "indexes": [ - { - "name": "idx_ai_agent_sessions_user", - "columns": ["userId"] - } - ] - }, - "agent_events": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "sessionId": { - "type": "varchar", - "length": 36, - "notNull": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "seq": { - "type": "integer", - "notNull": true - }, - "kind": { - "type": "varchar", - "length": 16, - "notNull": true - }, - "text": { - "type": "text", - "notNull": true - }, - "requestId": { - "type": "text" - }, - "choices": { - "type": "text" - } - }, - "indexes": [ - { - "name": "idx_ai_agent_events_session_seq", - "columns": ["sessionId", "seq"], - "unique": true - } - ] - }, - "agent_queue": { - "columns": { - "id": { - "type": "id", - "primaryKey": true, - "notNull": true - }, - "sessionId": { - "type": "varchar", - "length": 36, - "notNull": true - }, - "userId": { - "type": "refUser", - "length": 255, - "notNull": true - }, - "promptId": { - "type": "varchar", - "length": 36, - "notNull": true - }, - "position": { - "type": "integer", - "notNull": true - }, - "text": { - "type": "text", - "notNull": true - }, - "attachmentIds": { - "type": "text", - "notNull": true, - "defaultValue": "[]" - } - }, - "indexes": [ - { - "name": "idx_ai_agent_queue_session", - "columns": ["sessionId", "position"] - } - ] - } - } -} diff --git a/plugins/ai/migrations/sqlite/0001_adopt_ai_tables.sql b/plugins/ai/migrations/sqlite/0001_adopt_ai_tables.sql deleted file mode 100644 index cbcffe2b7..000000000 --- a/plugins/ai/migrations/sqlite/0001_adopt_ai_tables.sql +++ /dev/null @@ -1,60 +0,0 @@ --- ai 0001: adopt_ai_tables --- Generated by termix-plugin migrations. Review before committing. --- Hand-edited: keeps the legacy foreign key from messages and proposals to --- their conversation, which the SDK cannot declare. - -CREATE TABLE IF NOT EXISTS "ai_providers" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "user_id" text NOT NULL, - "provider_type" text NOT NULL, - "label" text NOT NULL, - "base_url" text, - "api_key_prefix" text, - "default_model" text, - "enabled" integer NOT NULL DEFAULT 1, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE UNIQUE INDEX IF NOT EXISTS "idx_ai_providers_user_label" ON "ai_providers" ("user_id", "label"); -ALTER TABLE "ai_providers" RENAME TO "p_ai_providers"; -CREATE TABLE IF NOT EXISTS "ai_conversations" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "user_id" text NOT NULL, - "title" text, - "provider_id" integer, - "model" text, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_conversations_user" ON "ai_conversations" ("user_id", "updated_at"); -ALTER TABLE "ai_conversations" RENAME TO "p_ai_conversations"; -CREATE TABLE IF NOT EXISTS "ai_messages" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "conversation_id" integer NOT NULL, - "role" text NOT NULL, - "content" text NOT NULL DEFAULT '', - "tool_calls" text, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("conversation_id") REFERENCES "p_ai_conversations" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_messages_conversation" ON "ai_messages" ("conversation_id", "created_at"); -ALTER TABLE "ai_messages" RENAME TO "p_ai_messages"; -CREATE TABLE IF NOT EXISTS "ai_proposals" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "conversation_id" integer NOT NULL, - "user_id" text NOT NULL, - "kind" text NOT NULL, - "summary" text, - "payload" text NOT NULL DEFAULT '{}', - "status" text NOT NULL DEFAULT 'pending', - "applied_at" text, - "result_summary" text, - "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE, - FOREIGN KEY ("conversation_id") REFERENCES "p_ai_conversations" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_proposals_user" ON "ai_proposals" ("user_id", "status"); -CREATE INDEX IF NOT EXISTS "idx_ai_proposals_conversation" ON "ai_proposals" ("conversation_id"); -ALTER TABLE "ai_proposals" RENAME TO "p_ai_proposals"; diff --git a/plugins/ai/migrations/sqlite/0002_provider_sync.sql b/plugins/ai/migrations/sqlite/0002_provider_sync.sql deleted file mode 100644 index 2bd7bfcc2..000000000 --- a/plugins/ai/migrations/sqlite/0002_provider_sync.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE "p_ai_providers" ADD COLUMN "sync_id" text; -CREATE UNIQUE INDEX "p_ai_providers_sync_id_unique" ON "p_ai_providers" ("sync_id"); diff --git a/plugins/ai/migrations/sqlite/0003_agent_sessions.sql b/plugins/ai/migrations/sqlite/0003_agent_sessions.sql deleted file mode 100644 index dd245d2d3..000000000 --- a/plugins/ai/migrations/sqlite/0003_agent_sessions.sql +++ /dev/null @@ -1,46 +0,0 @@ --- ai 0003: agent_sessions --- Generated by termix-plugin migrations. Review before committing. - -CREATE TABLE IF NOT EXISTS "p_ai_agent_sessions" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "session_id" text NOT NULL UNIQUE, - "user_id" text NOT NULL, - "host_id" integer NOT NULL, - "provider_id" integer NOT NULL, - "agent" text NOT NULL, - "model" text NOT NULL, - "cwd" text NOT NULL, - "executable" text NOT NULL, - "native_id" text, - "title" text, - "archived" integer NOT NULL DEFAULT 0, - "queue_paused" integer NOT NULL DEFAULT 0, - "draft" text, - "attachments" text NOT NULL DEFAULT '[]', - "updated_at" text NOT NULL, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_agent_sessions_user" ON "p_ai_agent_sessions" ("user_id"); -CREATE TABLE IF NOT EXISTS "p_ai_agent_events" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "session_id" text NOT NULL, - "user_id" text NOT NULL, - "seq" integer NOT NULL, - "kind" text NOT NULL, - "text" text NOT NULL, - "request_id" text, - "choices" text, - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE UNIQUE INDEX IF NOT EXISTS "idx_ai_agent_events_session_seq" ON "p_ai_agent_events" ("session_id", "seq"); -CREATE TABLE IF NOT EXISTS "p_ai_agent_queue" ( - "id" integer PRIMARY KEY AUTOINCREMENT, - "session_id" text NOT NULL, - "user_id" text NOT NULL, - "prompt_id" text NOT NULL, - "position" integer NOT NULL, - "text" text NOT NULL, - "attachment_ids" text NOT NULL DEFAULT '[]', - FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE CASCADE -); -CREATE INDEX IF NOT EXISTS "idx_ai_agent_queue_session" ON "p_ai_agent_queue" ("session_id", "position"); diff --git a/plugins/ai/package.json b/plugins/ai/package.json deleted file mode 100644 index 0936b5db5..000000000 --- a/plugins/ai/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@termix-plugin/ai", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "build": "termix-plugin build", - "test": "termix-plugin test", - "typecheck": "tsc -p .", - "validate": "termix-plugin validate", - "format": "prettier --write .", - "format:check": "prettier --check .", - "prepare": "husky || true" - }, - "dependencies": { - "@anthropic-ai/sdk": "^0.120.0", - "lucide-react": "^1.33.0", - "react-markdown": "^10.1.0", - "remark-gfm": "^4.0.1" - }, - "devDependencies": { - "@commitlint/cli": "^21.2.2", - "@commitlint/config-conventional": "^21.2.0", - "@termix/plugin-sdk": "^1.0.0", - "@testing-library/dom": "^10.4.2", - "@testing-library/react": "^16.3.3", - "@types/node": "^26.2.0", - "@types/react": "^19.2.18", - "@types/react-dom": "^19.2.4", - "better-sqlite3": "^13.0.3", - "husky": "^9.1.7", - "jsdom": "^30.0.1", - "lint-staged": "^17.2.0", - "prettier": "3.9.9", - "vitest": "^4.1.11" - }, - "peerDependencies": { - "@termix/plugin-sdk": "^1.0.0", - "drizzle-orm": "^0.45.2", - "express": "^5.2.1", - "react": "^19.2.8", - "sonner": "^2.0.8", - "ssh2": "^1.17.0" - }, - "lint-staged": { - "*.{ts,tsx,js,jsx,mjs,cjs,json,css,md}": [ - "prettier --write" - ] - } -} diff --git a/plugins/ai/src/backend/agents/forwarding.ts b/plugins/ai/src/backend/agents/forwarding.ts deleted file mode 100644 index 1b7f8c178..000000000 --- a/plugins/ai/src/backend/agents/forwarding.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { shellQuote } from "./types.js"; - -export const FORWARDING_ADMIN_SCRIPT = String.raw`set -eu -account=$1 -peer=$2 -[ "$(uname -s)" = Linux ] || { echo 'Automatic SSH configuration currently requires Linux with systemd and OpenSSH'; exit 1; } -sshd=/usr/sbin/sshd -config=/etc/ssh/sshd_config -[ -x "$sshd" ] && [ -f "$config" ] || { echo 'OpenSSH server configuration not found'; exit 1; } -"$sshd" -t -if systemctl is-active --quiet ssh; then service=ssh; elif systemctl is-active --quiet sshd; then service=sshd; else echo 'No active systemd SSH service found'; exit 1; fi -lock=/etc/ssh/.termix-agent-forward.lock -mkdir "$lock" 2>/dev/null || { echo 'Another SSH configuration operation is running'; exit 1; } -candidate= -backup= -changed=0 -committed=0 -cleanup() { - code=$? - trap - EXIT - if [ "$changed" = 1 ] && [ "$committed" = 0 ]; then - echo 'Restoring previous SSH configuration' - cp -p "$backup" "$config" - "$sshd" -t && systemctl reload "$service" || echo "SSH rollback reload failed; restore $backup manually" - fi - if [ -n "$candidate" ] && [ -f "$candidate" ]; then rm "$candidate"; fi - rmdir "$lock" - exit "$code" -} -trap cleanup EXIT -trap 'exit 130' HUP INT TERM -context="user=$account,addr=$peer,host=termix" -effective=$("$sshd" -T -C "$context") -echo "SSH account: $account; observed connection source: $peer" -if printf '%s\n' "$effective" | grep -qx 'disableforwarding yes'; then - echo 'DisableForwarding is enabled; an administrator must review this policy first' - exit 1 -fi -tcp=$(printf '%s\n' "$effective" | awk '$1 == "allowtcpforwarding" {print $2}') -listen=$(printf '%s\n' "$effective" | awk '$1 == "permitlisten" {$1=""; print}') -case "$tcp" in - yes|all|remote) - for rule in $listen; do - case "$rule" in any|'127.0.0.1:*'|'*:*') echo 'Loopback remote forwarding is already allowed'; exit 0;; esac - done;; -esac -# Preserve an already allowed local forwarding capability. -case "$tcp" in yes|all|local) tcp=yes;; *) tcp=remote;; esac -begin="# BEGIN Termix Agent $account $peer" -end="# END Termix Agent $account $peer" -block=$(printf '%s\nMatch User %s Address %s\n AllowTcpForwarding %s\n PermitListen 127.0.0.1:*\n GatewayPorts no\n%s\n' "$begin" "$account" "$peer" "$tcp" "$end") -candidate=$(mktemp /etc/ssh/.termix-sshd.XXXXXX) -cp -p "$config" "$candidate" -awk -v begin="$begin" -v end="$end" -v block="$block" ' - $0 == begin { skip=1; next } - $0 == end { skip=0; next } - !skip { - if (!inserted && ($0 ~ /^[ \t]*Match[ \t]/ || $0 ~ /^# BEGIN Termix Agent /)) { print block; inserted=1 } - print - } - END { if (!inserted) print block } -' "$config" > "$candidate" -"$sshd" -t -f "$candidate" -check=$("$sshd" -T -f "$candidate" -C "$context") -printf '%s\n' "$check" | grep -qx "allowtcpforwarding $tcp" -printf '%s\n' "$check" | grep -Fqx 'permitlisten 127.0.0.1:*' -printf '%s\n' "$check" | grep -qx 'gatewayports no' -printf '%s\n' "$check" | grep -qx 'disableforwarding no' -mkdir -p /etc/ssh/termix-agent-backups -chmod 700 /etc/ssh/termix-agent-backups -backup=$(mktemp /etc/ssh/termix-agent-backups/sshd_config.XXXXXX) -cp -p "$config" "$backup" -echo "Backup: $backup" -changed=1 -mv "$candidate" "$config" -systemctl reload "$service" -"$sshd" -t -committed=1 -echo 'Enabled loopback remote forwarding for this SSH account and source only' -`; - -export function forwardingScript(): string { - return `set -eu -account=$(id -un) -peer=$(printf '%s' "$SSH_CONNECTION" | cut -d ' ' -f 1) -printf '%s' "$account" | grep -Eq '^[A-Za-z0-9_.-]+$' || { echo 'Unsupported SSH account name'; exit 1; } -printf '%s' "$peer" | grep -Eq '^[0-9a-fA-F:.]+$' || { echo 'Unable to determine the SSH connection source'; exit 1; } -script=${shellQuote(FORWARDING_ADMIN_SCRIPT)} -if [ "$(id -u)" = 0 ]; then - exec sh -c "$script" termix "$account" "$peer" -else - exec sudo -n sh -c "$script" termix "$account" "$peer" -fi -`; -} diff --git a/plugins/ai/src/backend/agents/install.ts b/plugins/ai/src/backend/agents/install.ts deleted file mode 100644 index 177321fe9..000000000 --- a/plugins/ai/src/backend/agents/install.ts +++ /dev/null @@ -1,250 +0,0 @@ -import type { Router } from "express"; -import type { Client, ClientChannel } from "ssh2"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { forwardingScript } from "./forwarding.js"; -import { AGENTS, shellQuote, type AgentKind } from "./types.js"; - -export const PACKAGES: Record = { - pi: "@earendil-works/pi-coding-agent", - opencode: "opencode-ai", - claude: "@anthropic-ai/claude-code", - codex: "@openai/codex", -}; - -// No caller-supplied commands, versions, registries, or download URLs. -export function installScript(agent: AgentKind): string { - if (!AGENTS.includes(agent)) throw Error("Unsupported agent"); - return `set -eu -umask 077 -agent=${shellQuote(agent)} -package=${shellQuote(PACKAGES[agent])} -root="$HOME/.local/share/termix-agent-runtime" -mkdir -p "$root" -lock="$root/install.lock" -mkdir "$lock" 2>/dev/null || { echo 'Another installation is running'; exit 1; } -stage=$(mktemp -d "$root/staging.XXXXXX") -trap 'rm -rf "$stage"; rmdir "$lock"' EXIT -trap 'exit 130' HUP INT TERM -case "$(uname -s)" in Linux) platform=linux;; Darwin) platform=darwin;; *) echo 'Only Linux and macOS are supported'; exit 1;; esac -case "$(uname -m)" in x86_64|amd64) arch=x64;; aarch64|arm64) arch=arm64;; *) echo 'Only x64 and arm64 are supported'; exit 1;; esac -for tool in curl tar; do command -v "$tool" >/dev/null || { echo "Missing prerequisite: $tool"; exit 1; }; done -fetch() { curl --fail --silent --show-error --proto '=https' --tlsv1.2 --max-redirs 0 --connect-timeout 20 --max-time 300 "$1" -o "$2"; } -echo "Detected $platform / $arch; installing for $(id -un)" -if [ ! -x "$root/node/bin/node" ]; then - echo 'Downloading Node.js 24 LTS from https://nodejs.org' - fetch https://nodejs.org/dist/latest-v24.x/SHASUMS256.txt "$stage/SHASUMS256.txt" - artifact=$(awk -v suffix="-$platform-$arch.tar.gz" '$2 ~ (suffix "$") {print $2}' "$stage/SHASUMS256.txt") - printf '%s' "$artifact" | grep -Eq '^node-v24\\.[0-9]+\\.[0-9]+-(linux|darwin)-(x64|arm64)\\.tar\\.gz$' || { echo 'Invalid official Node.js manifest'; exit 1; } - fetch "https://nodejs.org/dist/latest-v24.x/$artifact" "$stage/$artifact" - expected=$(awk -v file="$artifact" '$2 == file {print $1}' "$stage/SHASUMS256.txt") - if command -v sha256sum >/dev/null; then actual=$(sha256sum "$stage/$artifact" | awk '{print $1}'); else actual=$(shasum -a 256 "$stage/$artifact" | awk '{print $1}'); fi - [ "$actual" = "$expected" ] || { echo 'Node.js SHA-256 verification failed'; exit 1; } - mkdir "$stage/node" - tar -xzf "$stage/$artifact" -C "$stage/node" --strip-components=1 - "$stage/node/bin/node" --version - mv "$stage/node" "$root/node" - echo 'Node.js SHA-256 verified' -fi -PATH="$root/node/bin:$PATH" -export PATH -touch "$stage/user.npmrc" "$stage/global.npmrc" -mkdir "$stage/package" -printf '{"name":"termix-agent-runtime","version":"1.0.0","private":true}' > "$stage/package/package.json" -# Ignore host npmrc, scoped registry overrides and lifecycle download scripts. -npm_official() { env -i HOME="$HOME" PATH="$PATH" npm_config_userconfig="$stage/user.npmrc" npm_config_globalconfig="$stage/global.npmrc" npm_config_registry=https://registry.npmjs.org npm_config_strict_ssl=true npm_config_update_notifier=false npm_config_cache="$root/npm-cache" npm "$@"; } -cd "$stage/package" -echo "Resolving $package from https://registry.npmjs.org" -npm_official install --package-lock-only --ignore-scripts --no-audit --no-fund --save-exact "$package@latest" -node - <<'VERIFY' -const fs=require('fs'); -const lock=JSON.parse(fs.readFileSync('package-lock.json','utf8')); -(async () => { - for(const [name,p] of Object.entries(lock.packages)) { - if(!name) continue; - const u=new URL(p.resolved); - if(u.origin!=='https://registry.npmjs.org' || u.username || u.password) throw Error('Non-official dependency: '+name); - if(!/^sha512-/.test(p.integrity||'')) { - const packageName=p.name || name.split('node_modules/').pop(); - const response=await fetch('https://registry.npmjs.org/'+encodeURIComponent(packageName)+'/'+encodeURIComponent(p.version),{redirect:'error',signal:AbortSignal.timeout(30000)}); - if(!response.ok) throw Error('Unable to verify dependency: '+name); - const metadata=await response.json(); - if(metadata.dist?.tarball!==p.resolved || !/^sha512-/.test(metadata.dist?.integrity||'')) throw Error('Unverified dependency: '+name); - p.integrity=metadata.dist.integrity; - } - } - fs.writeFileSync('package-lock.json',JSON.stringify(lock,null,2)); - console.log('All package sources and integrity hashes verified'); -})().catch(error=>{console.error(error.message);process.exitCode=1;}); -VERIFY -npm_official ci --ignore-scripts --no-audit --no-fund -# Link verified optional platform binaries without running download-capable postinstall scripts. -case "$agent" in - claude) relative="@anthropic-ai/claude-code-$platform-$arch/claude";; - opencode) - variant="opencode-$platform-$arch" - if [ "$arch" = x64 ]; then variant="$variant-baseline"; fi - relative="$variant/bin/opencode";; - *) relative=;; -esac -if [ -n "$relative" ]; then - binary="$stage/package/node_modules/$relative" - [ -x "$binary" ] || { echo 'Official platform binary is unavailable'; exit 1; } - mkdir -p "$stage/package/node_modules/.bin" - # Relative links remain valid when the verified package directory is activated. - ln -sf "../$relative" "$stage/package/node_modules/.bin/$agent" -fi -"$stage/package/node_modules/.bin/$agent" --version -mkdir -p "$root/agents" -old="$root/agents/$agent.previous" -rm -rf "$old" -if [ -d "$root/agents/$agent" ]; then mv "$root/agents/$agent" "$old"; fi -if ! mv "$stage/package" "$root/agents/$agent"; then - if [ -d "$old" ]; then mv "$old" "$root/agents/$agent"; fi - exit 1 -fi -echo "Installed $agent from the official npm registry" -echo "Executable: $root/agents/$agent/node_modules/.bin/$agent" -`; -} - -export function registerInstallRoute( - router: Router, - ctx: PluginContext, - isRunning: (hostId: number) => boolean, -) { - const active = new Set(); - for (const operation of ["install", "forwarding"] as const) - router.post(`/agents/${operation}`, async (req, res) => { - const { hostId, agent } = req.body ?? {}; - if ( - !Number.isSafeInteger(hostId) || - hostId < 1 || - (operation === "install" && !AGENTS.includes(agent)) - ) { - res.status(400).json({ error: "Choose a host and supported agent" }); - return; - } - let dispose: (() => void) | undefined; - let channel: ClientChannel | undefined; - let timer: ReturnType | undefined; - let acquired = false; - const write = (data: object) => { - if (!res.destroyed) res.write(JSON.stringify(data) + "\n"); - }; - try { - if (!(await ctx.hosts.checkAccess(hostId, "connect")).hasAccess) - throw Error("SSH access is required"); - if (isRunning(hostId)) - throw Error( - "Stop active agents on this host before changing its runtime or SSH policy", - ); - if (active.has(hostId)) - throw Error( - "Another agent setup operation is already running on this host", - ); - active.add(hostId); - acquired = true; - const connection = await ctx.ssh.connect(hostId, { - purpose: `ai-agent-${operation}`, - profile: "session", - timeoutMs: 15000, - }); - dispose = connection.dispose; - if (res.destroyed) throw Error("Agent setup request disconnected"); - channel = await new Promise((resolve, reject) => - connection.client.exec( - "sh -lc " + - shellQuote( - operation === "install" - ? installScript(agent) - : forwardingScript(), - ), - (err, ch) => (err ? reject(err) : resolve(ch)), - ), - ); - if (res.destroyed) throw Error("Agent setup request disconnected"); - res.set({ - "Content-Type": "application/x-ndjson", - "Cache-Control": "no-store", - "X-Accel-Buffering": "no", - }); - res.flushHeaders(); - res.once("close", () => { - channel?.signal("TERM"); - dispose?.(); - }); - let bytes = 0; - const log = (data: Buffer) => { - bytes += data.length; - if (bytes <= 256 * 1024) write({ log: data.toString() }); - }; - channel.on("data", log); - channel.stderr.on("data", log); - const exitCode = await new Promise((resolve, reject) => { - timer = setTimeout( - () => reject(Error("Agent setup timed out after 10 minutes")), - 600000, - ); - channel!.once("error", reject); - channel!.once("close", (code: number) => resolve(code ?? -1)); - }); - if (operation === "forwarding" && exitCode === 0) { - const verification = await ctx.ssh.connect(hostId, { - purpose: "ai-agent-forwarding-verify", - profile: "session", - timeoutMs: 15000, - }); - try { - await new Promise((resolve, reject) => { - const deadline = setTimeout( - () => reject(Error("SSH forwarding verification timed out")), - 15000, - ); - verification.client.forwardIn("127.0.0.1", 0, (error) => { - clearTimeout(deadline); - return error - ? reject( - Error( - "SSH configuration was checked, but a new connection still refused remote forwarding. Check authorized_keys restrictions and the active SSH server configuration.", - ), - ) - : resolve(); - }); - }); - write({ - log: "Verified loopback remote forwarding on a new SSH connection\n", - }); - } finally { - verification.dispose(); - } - } - await ctx.audit.record({ - action: `agent_${operation}`, - resourceId: String(hostId), - success: exitCode === 0, - details: JSON.stringify({ - agent: operation === "install" ? agent : undefined, - package: - operation === "install" - ? PACKAGES[agent as AgentKind] - : undefined, - exitCode, - }), - }); - write({ done: true, success: exitCode === 0 }); - res.end(); - } catch (error) { - const message = - error instanceof Error ? error.message : "Agent setup failed"; - if (res.headersSent) { - write({ done: true, success: false, error: message }); - res.end(); - } else res.status(400).json({ error: message }); - } finally { - clearTimeout(timer); - channel?.signal("TERM"); - dispose?.(); - if (acquired) active.delete(hostId); - } - }); -} diff --git a/plugins/ai/src/backend/agents/remote-runner.ts b/plugins/ai/src/backend/agents/remote-runner.ts deleted file mode 100644 index 0339e92c7..000000000 --- a/plugins/ai/src/backend/agents/remote-runner.ts +++ /dev/null @@ -1,192 +0,0 @@ -/** Runs on the selected SSH host. Only a session-scoped proxy token reaches it. */ -export const REMOTE_RUNNER = String.raw` -const fs = require('node:fs'), path = require('node:path'), os = require('node:os'); -const {spawn} = require('node:child_process'), readline = require('node:readline'); -let config, child, nativeId, turnId, serverUrl, serverAuth, ready = false, running = false, rpcId = 0; -const pending = new Map(), approvals = new Map(); -const emit = (kind, text, extra = {}) => process.stdout.write(JSON.stringify({kind,text,...extra})+'\n'); -const send = value => child.stdin.write(JSON.stringify(value)+'\n'); -const request = (method, params) => new Promise((resolve,reject) => { - const id=++rpcId, timer=setTimeout(()=>{pending.delete(id);reject(Error('Agent request timed out: '+method));},60000); - pending.set(id,{resolve,reject,timer}); send({id,method,params}); -}); -function fail(error) { emit('error',error.message || String(error)); if(!ready) { if(child?.pid) {try{process.kill(-child.pid,'SIGTERM');}catch{}} process.stdin.destroy(); } } -function done(initial=false) { if(!initial && !running) return; running=false; emit('status','ready'); } -async function launch(args, env) { - const managed=path.join(os.homedir(),'.local','share','termix-agent-runtime','agents',config.agent,'node_modules','.bin',config.agent); - const executable=(!config.executable || config.executable===config.agent) && fs.existsSync(managed)?managed:(config.executable || config.agent); - child=spawn(executable,args,{cwd:config.cwd,env:{...process.env,...env},stdio:['pipe','pipe','pipe'],detached:true}); - child.on('error',fail); - child.on('exit',(code)=>{emit('status','stopped');process.exitCode=code||0;process.stdin.destroy();}); - child.stderr.on('data',d=>emit('tool',d.toString().slice(0,8000))); - readline.createInterface({input:child.stdout}).on('line',line=>{ - try { receive(JSON.parse(line)); } catch { /* CLI startup chatter is not protocol. */ } - }); - await new Promise((resolve,reject)=>{child.once('spawn',resolve);child.once('error',reject);}); -} -function receive(m) { - if(config.agent==='pi') { - if(m.type==='response' && pending.has(m.id)) {const p=pending.get(m.id);pending.delete(m.id);clearTimeout(p.timer);m.success?p.resolve(m.data):p.reject(Error(m.error));return;} - if(m.type==='assistant_message_event' || m.type==='message_update') { - const e=m.assistantMessageEvent || m.event; - if(e?.type==='text_delta') emit('text',e.delta); - } - if(m.type==='tool_execution_start') emit('tool',m.toolName+' '+JSON.stringify(m.args)); - if(m.type==='tool_execution_end') emit('tool',JSON.stringify(m.result)); - if(m.type==='agent_end') done(); - if(m.type==='response' && m.success===false) {fail(Error(m.error || 'Pi request failed'));done();} - if(m.type==='extension_ui_request' && ['select','confirm','input','editor'].includes(m.method)) { - approvals.set(m.id,m); emit('permission',m.title || m.message || m.method,{requestId:m.id,choices:m.options}); - } - return; - } - if(config.agent==='claude') { - if(m.type==='system' && m.session_id) {nativeId=m.session_id;emit('native','',{nativeId});} - if(m.type==='stream_event' && m.event?.type==='content_block_delta' && m.event.delta?.type==='text_delta') emit('text',m.event.delta.text); - if(m.type==='stream_event' && m.event?.type==='content_block_start' && m.event.content_block?.type==='tool_use') emit('tool',m.event.content_block.name); - if(m.type==='control_request') { - if(m.request?.subtype==='can_use_tool') { - approvals.set(m.request_id,m);emit('permission',m.request.tool_name+' '+JSON.stringify(m.request.input),{requestId:m.request_id}); - } else send({type:'control_response',response:{subtype:'error',request_id:m.request_id,error:'Unsupported control request'}}); - } - if(m.type==='result') {if(m.is_error) fail(Error(m.result || m.errors?.join('\n') || 'Claude turn failed'));done();} - return; - } - if(config.agent==='codex') { - if(m.id!==undefined && !m.method && pending.has(m.id)) { - const p=pending.get(m.id);pending.delete(m.id);clearTimeout(p.timer);m.error?p.reject(Error(m.error.message)):p.resolve(m.result);return; - } - if(m.id!==undefined && m.method) { - approvals.set(String(m.id),m);emit('permission',m.method+' '+JSON.stringify(m.params),{requestId:String(m.id)});return; - } - if(m.method==='item/agentMessage/delta') emit('text',m.params.delta); - if(m.method==='item/started' && m.params.item?.type!=='agentMessage') emit('tool',JSON.stringify(m.params.item)); - if(m.method==='turn/started') turnId=m.params.turn.id; - if(m.method==='turn/completed') {if(m.params.turn.status==='failed') fail(Error(m.params.turn.error?.message || 'Codex turn failed'));done();} - return; - } -} -async function oc(method, route, body) { - const r=await fetch(serverUrl+route,{method,headers:{'Content-Type':'application/json','x-opencode-directory':config.cwd,authorization:serverAuth},body:body===undefined?undefined:JSON.stringify(body),signal:AbortSignal.timeout(5000)}); - if(!r.ok) throw Error('OpenCode '+route+' returned '+r.status); - return r.status===204?null:r.json(); -} -async function ocEvents() { - const response=await fetch(serverUrl+'/event',{headers:{'x-opencode-directory':config.cwd,authorization:serverAuth}}); - if(!response.ok || !response.body) throw Error('OpenCode event stream unavailable'); - let buffer='';const decoder=new TextDecoder(),partTypes=new Map(); - for await(const chunk of response.body) { - buffer+=decoder.decode(chunk,{stream:true}); - let end; - while((end=buffer.indexOf('\n\n'))>=0) { - const frame=buffer.slice(0,end);buffer=buffer.slice(end+2); - const data=frame.split('\n').filter(x=>x.startsWith('data:')).map(x=>x.slice(5).trim()).join('\n'); - if(!data) continue; - let e;try{e=JSON.parse(data);}catch{continue;}const p=e.properties||{}; - const sessionId=p.sessionID || p.part?.sessionID || p.info?.sessionID; - if(sessionId!==nativeId) continue; - if(e.type==='message.part.updated') partTypes.set(p.part.id,p.part.type); - if(e.type==='message.part.delta' && p.field==='text' && partTypes.get(p.partID)==='text') emit('text',p.delta); - if(e.type==='message.part.updated' && p.part?.type==='tool') emit('tool',JSON.stringify(p.part)); - if(e.type==='session.idle' || (e.type==='session.status' && p.status?.type==='idle')) done(); - if(e.type==='session.error') {fail(Error(JSON.stringify(p.error)));done();} - if(e.type==='permission.asked' || e.type==='question.asked') {approvals.set(p.id,{...p,event:e.type});emit('permission',JSON.stringify(p),{requestId:p.id});} - } - } - throw Error('OpenCode event stream closed'); -} -async function start(c) { - config=c; - emit('tool','Starting '+c.agent+' in '+c.cwd); - if(!fs.statSync(c.cwd).isDirectory()) throw Error('Working directory does not exist'); - const dir=path.join(os.homedir(),'.local','state','termix-agents',c.id); - fs.mkdirSync(dir,{recursive:true,mode:0o700}); - const base=c.proxyUrl, token=c.token; - if(c.agent==='pi') { - const agentDir=path.join(dir,'config'); - fs.mkdirSync(agentDir,{recursive:true,mode:0o700}); - fs.writeFileSync(path.join(agentDir,'models.json'),JSON.stringify({providers:{termix:{baseUrl:base+'/v1',api:c.providerType==='anthropic'?'anthropic-messages':'openai-completions',apiKey:token,models:[{id:c.model,name:c.model,reasoning:false,input:['text','image'],contextWindow:131072,maxTokens:8192,cost:{input:0,output:0,cacheRead:0,cacheWrite:0}}]}}}),{mode:0o600}); - await launch(['--mode','rpc','--provider','termix','--model',c.model,'--session',path.join(dir,'session.jsonl')],{PI_CODING_AGENT_DIR:agentDir}); - const state=await new Promise((resolve,reject)=>{const id=String(++rpcId),timer=setTimeout(()=>{pending.delete(id);reject(Error('Pi startup timed out'));},60000);pending.set(id,{resolve,reject,timer});send({id,type:'get_state'});}); - nativeId=state.sessionId;emit('native','',{nativeId}); - emit('tool','Loaded '+state.messageCount+' messages from the Pi session'); - } - if(c.agent==='claude') { - const args=['--print','--verbose','--input-format','stream-json','--output-format','stream-json','--include-partial-messages','--permission-prompt-tool','stdio','--model',c.model]; - if(c.nativeId) args.push('--resume',c.nativeId); - await launch(args,{ANTHROPIC_BASE_URL:base,ANTHROPIC_API_KEY:token,ANTHROPIC_AUTH_TOKEN:'',CLAUDE_CONFIG_DIR:dir,DISABLE_UPDATES:'1',DISABLE_AUTOUPDATER:'1'}); - } - if(c.agent==='codex') { - await launch(['app-server','-c','model_provider="termix"','-c','model_providers.termix.name="Termix"','-c','model_providers.termix.base_url='+JSON.stringify(base+'/v1'),'-c','model_providers.termix.env_key="TERMIX_AGENT_TOKEN"','-c','model_providers.termix.wire_api="responses"'],{CODEX_HOME:dir,TERMIX_AGENT_TOKEN:token}); - await request('initialize',{clientInfo:{name:'termix',version:'1.0.0'}});send({method:'initialized'}); - const result=await request(c.nativeId?'thread/resume':'thread/start',{...(c.nativeId?{threadId:c.nativeId}:{}),cwd:c.cwd,model:c.model,approvalPolicy:'untrusted',sandbox:'workspace-write'}); - nativeId=result.thread.id;emit('native','',{nativeId}); - } - if(c.agent==='opencode') { - const net=require('node:net');const port=await new Promise(resolve=>{const s=net.createServer();s.listen(0,'127.0.0.1',()=>{const p=s.address().port;s.close(()=>resolve(p));});}); - serverUrl='http://127.0.0.1:'+port; - // Loopback is shared with every local account; the password keeps them off the agent. - const password=require('node:crypto').randomBytes(32).toString('hex');serverAuth='Basic '+Buffer.from('opencode:'+password).toString('base64'); - const settings={autoupdate:false,model:'termix/'+c.model,provider:{termix:{npm:c.providerType==='anthropic'?'@ai-sdk/anthropic':'@ai-sdk/openai-compatible',name:'Termix',options:{baseURL:base+'/v1',apiKey:token},models:{[c.model]:{name:c.model}}}},permission:'ask'}; - emit('tool','Starting OpenCode server'); - await launch(['serve','--hostname','127.0.0.1','--port',String(port)],{OPENCODE_CONFIG_CONTENT:JSON.stringify(settings),OPENCODE_SERVER_USERNAME:'opencode',OPENCODE_SERVER_PASSWORD:password,XDG_DATA_HOME:dir,XDG_CONFIG_HOME:dir}); - emit('tool','Waiting for OpenCode health'); - let available=false;for(let i=0;i<100;i++){try{await oc('GET','/global/health');available=true;break;}catch{await new Promise(r=>setTimeout(r,200));}} - if(!available) throw Error('OpenCode startup timed out'); - emit('tool','Opening OpenCode session'); - nativeId=c.nativeId || (await oc('POST','/session',{})).id;emit('native','',{nativeId}); - void ocEvents().catch(fail); - } - ready=true;done(true); -} -async function handle(m) { - if(m.type==='start') return start(m.config); - if(m.type==='stop') {if(child?.pid) {try{process.kill(-child.pid,'SIGTERM');}catch{}};setTimeout(()=>process.exit(0),1500).unref();return;} - if(!ready) throw Error('Agent is not ready'); - if(m.type==='prompt') { - running=true; - emit('status','running'); - try { - const attachments=m.attachments||[],images=[]; - let text=m.text; - for(const a of attachments) { - if(a.mime.startsWith('image/')) { - const fd=fs.openSync(a.path,fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW); - try { - const stat=fs.fstatSync(fd);if(!stat.isFile() || stat.size>1024*1024) throw Error('Image exceeds 1 MiB or is no longer a regular file'); - const data=Buffer.alloc(stat.size),size=fs.readSync(fd,data,0,data.length,0); - images.push({type:'image',data:data.subarray(0,size).toString('base64'),mimeType:a.mime}); - } finally {fs.closeSync(fd);} - } else text+='\nAttached file on this host: '+JSON.stringify(a.path); - } - if(config.agent==='pi') send({type:'prompt',message:text,images}); - if(config.agent==='claude') send({type:'user',message:{role:'user',content:[{type:'text',text:text||'Please inspect the attached images.'},...images.map(i=>({type:'image',source:{type:'base64',media_type:i.mimeType,data:i.data}}))]},parent_tool_use_id:null,session_id:nativeId||''}); - if(config.agent==='codex') await request('turn/start',{threadId:nativeId,input:[{type:'text',text},...images.map(i=>({type:'image',url:'data:'+i.mimeType+';base64,'+i.data}))]}); - if(config.agent==='opencode') await oc('POST','/session/'+nativeId+'/prompt_async',{model:{providerID:'termix',modelID:config.model},parts:[{type:'text',text},...images.map(i=>({type:'file',mime:i.mimeType,url:'data:'+i.mimeType+';base64,'+i.data}))]}); - } catch(error) {fail(error);done();} - } - if(m.type==='cancel') { - if(config.agent==='pi') send({type:'abort'}); - if(config.agent==='claude') send({type:'control_request',request_id:String(++rpcId),request:{subtype:'interrupt'}}); - if(config.agent==='codex' && turnId) await request('turn/interrupt',{threadId:nativeId,turnId}); - if(config.agent==='opencode') await oc('POST','/session/'+nativeId+'/abort',{}); - } - if(m.type==='answer') { - const a=approvals.get(m.requestId);if(!a) throw Error('Approval request expired'); - if(config.agent==='pi') send({type:'extension_ui_response',id:m.requestId,...(a.method==='confirm'?{confirmed:m.allow}:{value:m.value,cancelled:!m.allow})}); - if(config.agent==='claude') send({type:'control_response',response:{subtype:'success',request_id:m.requestId,response:m.allow?{behavior:'allow',updatedInput:a.request.input}:{behavior:'deny',message:'Denied in Termix'}}}); - if(config.agent==='codex') { - let result={decision:m.allow?'accept':'decline'}; - if(a.method==='item/tool/requestUserInput') {if(!m.value) throw Error('Provide JSON answers for this question');result={answers:JSON.parse(m.value)};} - else if(!a.method.endsWith('/requestApproval')) throw Error('Unsupported Codex request; stop the session'); - send({id:a.id,result}); - } - if(config.agent==='opencode') { - if(a.event==='question.asked') await oc('POST','/question/'+m.requestId+(m.allow?'/reply':'/reject'),m.allow?{answers:JSON.parse(m.value||'[]')}:{}); - else await oc('POST','/permission/'+m.requestId+'/reply',{reply:m.allow?'once':'reject'}); - } - approvals.delete(m.requestId); - } -} -readline.createInterface({input:process.stdin}).on('line',line=>{try{Promise.resolve(handle(JSON.parse(line))).catch(fail);}catch(e){fail(e);}}).on('close',()=>{if(child?.pid) {try{process.kill(-child.pid,'SIGTERM');}catch{}};}); -`; diff --git a/plugins/ai/src/backend/agents/routes.ts b/plugins/ai/src/backend/agents/routes.ts deleted file mode 100644 index 7e78bb446..000000000 --- a/plugins/ai/src/backend/agents/routes.ts +++ /dev/null @@ -1,709 +0,0 @@ -import { randomBytes, randomUUID } from "node:crypto"; -import { createServer } from "node:http"; -import { once } from "node:events"; -import type { Client, ClientChannel } from "ssh2"; -import type { Request, Response, Router } from "express"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { - createAiGate, - readPrivateAllowlist, - resolveAiAccess, -} from "../gating.js"; -import { createProviderFetch } from "../providers/http.js"; -import type { AiRepository } from "../repository.js"; -import { - AGENTS, - clipText, - compatible, - shellQuote, - validStart, - type AgentEvent, - type AgentSession, - type QueuedPrompt, - type AgentAttachment, -} from "./types.js"; -import { registerInstallRoute } from "./install.js"; -import { workspaceOperation } from "./workspace.js"; -import { promptInput, updateSession, updateQueue } from "./session-state.js"; -import { REMOTE_RUNNER } from "./remote-runner.js"; -import { EVENT_HISTORY, type AgentStore } from "./store.js"; - -interface Live { - session: AgentSession; - channel?: ClientChannel; - dispose: () => void; - subscribers: Set; - approvals: Set; - /** Events not yet written; flushed together at most once a second. */ - pending: AgentEvent[]; - saved: Promise; - stopping?: Promise; - draining?: boolean; - flush?: ReturnType; -} -const MAX_BODY = 16 * 1024 * 1024; -const PROVIDER_PATHS = new Set([ - "/chat/completions", - "/responses", - "/messages", - "/messages/count_tokens", - "/models", -]); -export function providerPath(raw: string): string | null { - const url = new URL(raw, "http://localhost"); - const path = url.pathname.replace(/^\/v1(?=\/)/, ""); - return PROVIDER_PATHS.has(path) ? path : null; -} - -export function registerAgentRoutes( - router: Router, - repository: AiRepository, - ctx: PluginContext, - store: AgentStore, -): void { - const live = new Map(); - const actor = () => ctx.currentActor() as string; - /** Writes are queued per session, so rows land in the order they happened. */ - const write = (entry: Live, full: boolean) => { - entry.saved = entry.saved - .catch(() => undefined) - .then(async () => { - const session = { ...entry.session }; - await store.append(session, entry.pending.splice(0)); - if (full) await store.save(session); - }); - return entry.saved; - }; - const save = (entry: Live) => write(entry, true); - const logFailure = (error: unknown) => - ctx.log.error( - "Agent session could not be saved", - error instanceof Error ? error : new Error(String(error)), - ); - const event = (entry: Live, input: Omit) => { - const s = entry.session; - const item = { - ...input, - seq: (s.events.at(-1)?.seq ?? 0) + 1, - text: clipText(input.text.slice(0, 32000)), - }; - s.events.push(item); - if (s.events.length > EVENT_HISTORY) s.events.shift(); - entry.pending.push(item); - s.updatedAt = new Date().toISOString(); - for (const res of entry.subscribers) { - if (!res.write(`id: ${item.seq}\ndata: ${JSON.stringify(item)}\n\n`)) - res.end(); - } - if (!entry.flush) - entry.flush = setTimeout(() => { - entry.flush = undefined; - write(entry, false).catch(logFailure); - }, 1000); - }; - async function allowed(s: AgentSession) { - return ( - (await resolveAiAccess(ctx.settings, s.userId)).enabled && - (await ctx.rbac.hasFor(s.userId, "agents")) && - (await ctx.hosts.checkAccess(s.hostId, "connect")).hasAccess - ); - } - async function find(id: string): Promise { - if (!/^[a-f0-9-]{36}$/.test(id)) throw Error("Invalid session ID"); - const s = live.get(id)?.session ?? (await store.get(actor(), id)); - if (!s || s.userId !== actor() || !(await allowed(s))) - throw Error("Agent session is not accessible"); - return live.has(id) ? s : { ...s, status: "stopped" }; - } - function stop(entry: Live): Promise { - if (entry.stopping) return entry.stopping; - if (live.get(entry.session.id) !== entry) return Promise.resolve(); - entry.stopping = (async () => { - if (entry.channel && !entry.channel.destroyed) { - await new Promise((resolve) => { - const timer = setTimeout(resolve, 2000); - entry.channel!.once("close", () => { - clearTimeout(timer); - resolve(); - }); - entry.channel!.write(JSON.stringify({ type: "stop" }) + "\n"); - }); - } - if (live.get(entry.session.id) === entry) live.delete(entry.session.id); - entry.dispose(); - entry.session.status = "stopped"; - event(entry, { kind: "status", text: "stopped" }); - // The save below writes everything pending. - clearTimeout(entry.flush); - for (const res of entry.subscribers) res.end(); - await save(entry); - })(); - return entry.stopping; - } - ctx.disposables.add(() => { - for (const entry of live.values()) stop(entry); - }); - ctx.events.on("user.deleted", (payload) => { - const id = (payload as { userId?: string }).userId; - for (const entry of live.values()) - if (entry.session.userId === id) stop(entry); - }); - - async function persist(s: AgentSession) { - const entry = live.get(s.id); - s.updatedAt = new Date().toISOString(); - if (entry) { - event(entry, { kind: "state", text: "" }); - await save(entry); - } else await store.save(s); - } - async function dispatch(entry: Live, prompt: QueuedPrompt) { - const s = entry.session; - if (s.status !== "ready" || entry.stopping || !entry.channel) - throw Error("Agent is not ready"); - s.status = "running"; - s.queue = s.queue?.filter((p) => p.id !== prompt.id); - event(entry, { - kind: "user", - text: - prompt.text + - (prompt.attachmentIds.length - ? "\n" + - prompt.attachmentIds - .map((id) => s.attachments!.find((a) => a.id === id)!.name) - .join("\n") - : ""), - }); - event(entry, { kind: "status", text: "running" }); - event(entry, { kind: "state", text: "" }); - await save(entry); - entry.channel.write( - JSON.stringify({ - type: "prompt", - text: prompt.text, - attachments: prompt.attachmentIds.map((id) => - s.attachments!.find((a) => a.id === id), - ), - }) + "\n", - ); - } - function drain(entry: Live) { - if (entry.draining || entry.stopping) return; - entry.draining = true; - void ctx - .asUser(entry.session.userId, async () => { - const s = entry.session; - if (!(await allowed(s))) return stop(entry); - if ( - s.status === "ready" && - !s.queuePaused && - !s.archived && - s.queue?.length && - !entry.stopping - ) - await dispatch(entry, s.queue[0]); - }) - .catch((error) => { - entry.session.queuePaused = true; - event(entry, { kind: "error", text: String(error.message || error) }); - }) - .finally(() => { - entry.draining = false; - }); - } - - async function start(s: AgentSession): Promise { - const existing = live.get(s.id); - if (existing?.stopping) await existing.stopping; - else if (existing) return existing; - if ( - [...live.values()].filter((e) => e.session.userId === s.userId).length >= - 4 - ) - throw Error( - "Stop an agent before opening another (maximum 4 active sessions)", - ); - const provider = await repository.findProviderWithSecret( - s.providerId, - s.userId, - ); - if (!provider?.enabled || !compatible(s.agent, provider.providerType)) - throw Error("Provider protocol is not compatible with this agent"); - const connection = await ctx.ssh.connect(s.hostId, { - purpose: "ai-agent", - profile: "session", - timeoutMs: 15000, - }); - const entry: Live = { - // History is loaded so a resumed session keeps numbering its events. - session: { ...s, status: "starting", events: await store.events(s.id) }, - dispose: connection.dispose, - subscribers: new Set(), - approvals: new Set(), - pending: [], - saved: Promise.resolve(), - }; - live.set(s.id, entry); - const token = randomBytes(32).toString("hex"); - const aborters = new Set(); - let port = 0; - const proxy = createServer((req, res) => { - const controller = new AbortController(); - aborters.add(controller); - res.on("close", () => { - controller.abort(); - aborters.delete(controller); - }); - void ctx - .asUser(s.userId, async () => { - const path = providerPath(req.url ?? "/"); - const auth = - req.headers.authorization === `Bearer ${token}` || - req.headers["x-api-key"] === token; - if (!auth || !path || !["GET", "POST"].includes(req.method ?? "")) { - res.writeHead(403).end(); - return; - } - if (!(await allowed(s))) { - res.writeHead(403).end(); - stop(entry); - return; - } - const current = await repository.findProviderWithSecret( - s.providerId, - s.userId, - ); - if (!current?.enabled) { - res.writeHead(403).end(); - return; - } - let body = ""; - for await (const chunk of req) { - body += chunk.toString(); - if (Buffer.byteLength(body) > MAX_BODY) { - res.writeHead(413).end(); - return; - } - } - if (body) { - const parsed = JSON.parse(body); - if (parsed.model && parsed.model !== s.model) { - res.writeHead(403).end(); - return; - } - } - const base = - current.baseUrl || - (current.providerType === "anthropic" - ? "https://api.anthropic.com/v1" - : "https://api.openai.com/v1"); - const headers: Record = { - "content-type": "application/json", - }; - if (current.providerType === "anthropic") { - headers["x-api-key"] = current.apiKey ?? ""; - headers["anthropic-version"] = "2023-06-01"; - } else if (current.apiKey) - headers.authorization = `Bearer ${current.apiKey}`; - const response = await createProviderFetch( - ctx.fetch, - await readPrivateAllowlist(ctx.settings), - )(base.replace(/\/$/, "") + path, { - method: req.method, - headers, - body: body || undefined, - signal: controller.signal, - }); - res.writeHead(response.status, { - "Content-Type": - response.headers.get("content-type") ?? "application/json", - "Cache-Control": "no-store", - }); - if (response.body) - for await (const chunk of response.body as unknown as AsyncIterable) { - if (!res.write(chunk)) - await once(res, "drain", { signal: controller.signal }); - } - res.end(); - }) - .catch(() => { - if (!res.headersSent) res.writeHead(502); - res.end('{"error":"Termix provider request failed"}'); - }); - }); - proxy.maxHeadersCount = 30; - connection.client.on("tcp connection", (info, accept, reject) => { - if (info.destPort !== port || info.destIP !== "127.0.0.1") { - reject(); - return; - } - proxy.emit("connection", accept()); - }); - const startup = setTimeout(() => { - if (entry.session.status === "starting") { - event(entry, { kind: "error", text: "Agent startup timed out" }); - void stop(entry); - } - }, 60000); - startup.unref(); - const expiry = setTimeout(() => stop(entry), 8 * 60 * 60 * 1000); - expiry.unref(); - const accessTimer = setInterval(() => { - void ctx - .asUser(s.userId, async () => { - if (!(await allowed(s))) stop(entry); - }) - .catch(() => stop(entry)); - }, 30000); - accessTimer.unref(); - entry.dispose = () => { - clearTimeout(startup); - clearTimeout(expiry); - clearInterval(accessTimer); - for (const c of aborters) c.abort(); - proxy.close(); - connection.dispose(); - }; - try { - port = await new Promise((resolve, reject) => - connection.client.forwardIn("127.0.0.1", 0, (err, p) => - err - ? reject( - Error( - "SSH remote forwarding was refused. Allow loopback remote forwarding for this SSH account (AllowTcpForwarding remote and PermitListen 127.0.0.1:*). Installing an agent does not change SSH policy.", - ), - ) - : resolve(p), - ), - ); - const source = Buffer.from(REMOTE_RUNNER).toString("base64"); - const command = - 'export PATH="$HOME/.local/share/termix-agent-runtime/node/bin:$PATH"; command -v node >/dev/null || { echo "Node.js is missing. Use Install runtime first." >&2; exit 1; }; exec node -e ' + - shellQuote(`eval(Buffer.from('${source}','base64').toString())`); - const channel = await new Promise((resolve, reject) => - connection.client.exec("sh -lc " + shellQuote(command), (err, ch) => - err ? reject(err) : resolve(ch), - ), - ); - entry.channel = channel; - let buffer = ""; - channel.on("data", (chunk: Buffer) => { - buffer += chunk.toString(); - if (buffer.length > MAX_BODY) { - event(entry, { - kind: "error", - text: "Agent output exceeded the protocol limit", - }); - stop(entry); - return; - } - let n: number; - while ((n = buffer.indexOf("\n")) >= 0) { - const line = buffer.slice(0, n); - buffer = buffer.slice(n + 1); - try { - const m = JSON.parse(line); - if (m.kind === "native" && typeof m.nativeId === "string") { - entry.session.nativeId = m.nativeId; - save(entry).catch(logFailure); - continue; - } - if ( - !["text", "tool", "permission", "status", "error"].includes( - m.kind, - ) - ) - continue; - if ( - m.kind === "status" && - ["ready", "running", "stopped"].includes(m.text) - ) { - if (entry.session.status === m.text) continue; - entry.session.status = m.text; - } - if (m.kind === "error") entry.session.queuePaused = true; - if (m.kind === "permission" && typeof m.requestId === "string") - entry.approvals.add(m.requestId); - event(entry, { - kind: m.kind, - text: String(m.text ?? "").replaceAll(token, "[session token]"), - requestId: m.requestId, - choices: m.choices, - }); - if (m.kind === "status" && m.text === "ready") drain(entry); - } catch { - /* Ignore non-protocol startup chatter. */ - } - } - }); - channel.stderr.on("data", (d: Buffer) => - event(entry, { - kind: "error", - text: d.toString().replaceAll(token, "[session token]"), - }), - ); - channel.on("close", () => stop(entry)); - channel.write( - JSON.stringify({ - type: "start", - config: { - ...s, - userId: undefined, - events: undefined, - proxyUrl: `http://127.0.0.1:${port}`, - token, - providerType: provider.providerType, - }, - }) + "\n", - ); - await save(entry); - await ctx.audit.record({ - action: "agent_start", - resourceId: String(s.hostId), - success: true, - details: JSON.stringify({ - agent: s.agent, - sessionId: s.id, - providerId: s.providerId, - }), - }); - return entry; - } catch (error) { - stop(entry); - throw error; - } - } - - const gate = createAiGate(ctx.settings, () => ctx.currentActor()); - router.use("/agents", ctx.rbac.require("agents") as never, gate as never); - const mutations = new Map>(); - const route = - (fn: (req: Request, res: Response) => Promise) => - (req: Request, res: Response) => { - const id = req.method !== "GET" ? String(req.params.id ?? "") : ""; - const previous = id ? mutations.get(id) : undefined; - const operation = (previous ?? Promise.resolve()) - .catch(() => undefined) - .then(() => fn(req, res)); - if (id) mutations.set(id, operation); - void operation - .catch((error) => { - if (!res.headersSent) - res.status(400).json({ - error: - error instanceof Error - ? error.message - : "Agent operation failed", - }); - }) - .finally(() => { - if (id && mutations.get(id) === operation) mutations.delete(id); - }); - }; - registerInstallRoute(router, ctx, (hostId) => - [...live.values()].some((entry) => entry.session.hostId === hostId), - ); - router.get( - "/agents", - route(async (_req, res) => { - const sessions: AgentSession[] = []; - for (const stored of await store.list(actor())) { - if (await allowed(stored)) - sessions.push( - live.get(stored.id)?.session ?? { ...stored, status: "stopped" }, - ); - } - res.json({ - agents: AGENTS, - sessions: sessions - .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)) - .map((s) => ({ ...s, events: undefined, userId: undefined })), - }); - }), - ); - router.post( - "/agents", - route(async (req, res) => { - if (!validStart(req.body ?? {})) - throw Error( - "Choose an agent, host, provider, model and absolute working directory", - ); - const s: AgentSession = { - id: randomUUID(), - userId: actor(), - hostId: req.body.hostId, - providerId: req.body.providerId, - agent: req.body.agent, - model: req.body.model, - cwd: req.body.cwd, - executable: req.body.executable || req.body.agent, - status: "starting", - events: [], - updatedAt: new Date().toISOString(), - }; - if (!(await allowed(s))) throw Error("SSH access is required"); - await start(s); - res.status(201).json({ id: s.id }); - }), - ); - router.post( - "/agents/:id/workspace", - route(async (req, res) => { - const s = await find(String(req.params.id)); - const body = req.body ?? {}; - if ( - ["upload", "reference"].includes(body.operation) && - (s.attachments?.length ?? 0) >= 100 - ) - throw Error("Maximum 100 attachments per session"); - const result = await workspaceOperation(ctx, s, body); - if (["upload", "reference"].includes(body.operation)) { - s.attachments = [ - ...(s.attachments ?? []), - result as unknown as AgentAttachment, - ]; - await persist(s); - } - if (["upload", "worktree"].includes(body.operation)) - await ctx.audit.record({ - action: "agent_workspace_" + body.operation, - resourceId: String(s.hostId), - success: true, - details: JSON.stringify({ sessionId: s.id }), - }); - res.json(result); - }), - ); - router.patch( - "/agents/:id", - route(async (req, res) => { - const s = await find(String(req.params.id)); - updateSession(s, req.body ?? {}); - if (s.archived) { - s.queuePaused = true; - const entry = live.get(s.id); - if (entry) await stop(entry); - } - await persist(s); - res.json(s); - }), - ); - router.post( - "/agents/:id/queue", - route(async (req, res) => { - const s = await find(String(req.params.id)); - updateQueue(s, req.body ?? {}); - await persist(s); - const entry = live.get(s.id); - if (entry) drain(entry); - res.json({ queue: s.queue ?? [], queuePaused: !!s.queuePaused }); - }), - ); - router.get( - "/agents/:id", - route(async (req, res) => { - const s = await find(String(req.params.id)); - res.json(live.has(s.id) ? s : { ...s, events: await store.events(s.id) }); - }), - ); - router.get( - "/agents/:id/events", - route(async (req, res) => { - const s = await find(String(req.params.id)); - res.set({ - "Content-Type": "text/event-stream", - "Cache-Control": "no-cache", - "X-Accel-Buffering": "no", - }); - res.flushHeaders(); - const after = Number( - req.headers["last-event-id"] || req.query.after || 0, - ); - const entry = live.get(s.id); - const history = entry - ? s.events.filter((e) => e.seq > after) - : await store.events(s.id, after); - for (const e of history) - res.write(`id: ${e.seq}\ndata: ${JSON.stringify(e)}\n\n`); - if (!entry) { - res.write( - `event: snapshot\ndata: ${JSON.stringify({ status: "stopped" })}\n\n`, - ); - res.end(); - return; - } - entry.subscribers.add(res); - const heartbeat = setInterval(() => res.write(": heartbeat\n\n"), 15000); - req.on("close", () => { - clearInterval(heartbeat); - entry.subscribers.delete(res); - }); - }), - ); - router.post( - "/agents/:id/resume", - route(async (req, res) => { - const s = await find(String(req.params.id)); - if (s.archived) throw Error("Restore the archived session first"); - await start(s); - res.json({ id: s.id }); - }), - ); - router.post( - "/agents/:id/input", - route(async (req, res) => { - const s = await find(String(req.params.id)); - const entry = live.get(s.id); - if (!entry?.channel) throw Error("Resume the agent first"); - const { type, text, requestId, allow, value } = req.body ?? {}; - if (type === "prompt") { - if (s.archived) throw Error("Restore the archived session first"); - await dispatch(entry, promptInput(s, req.body)); - res.json({ ok: true }); - return; - } else if (type === "answer") { - if ( - !entry.approvals.has(requestId) || - typeof allow !== "boolean" || - (value !== undefined && - (typeof value !== "string" || value.length > 64000)) - ) - throw Error("Invalid approval response"); - entry.approvals.delete(requestId); - await ctx.audit.record({ - action: "agent_approval", - resourceId: String(s.hostId), - success: true, - details: JSON.stringify({ sessionId: s.id, requestId, allow }), - }); - } else if (type === "cancel") { - s.queuePaused = true; - await persist(s); - } else throw Error("Unknown input type"); - entry.channel.write( - JSON.stringify({ type, text, requestId, allow, value }) + "\n", - ); - res.json({ ok: true }); - }), - ); - router.post( - "/agents/:id/stop", - route(async (req, res) => { - const s = await find(String(req.params.id)); - const entry = live.get(s.id); - if (entry) await stop(entry); - res.json({ ok: true }); - }), - ); - router.delete( - "/agents/:id", - route(async (req, res) => { - const s = await find(String(req.params.id)); - const entry = live.get(s.id); - if (entry) await stop(entry); - await entry?.saved.catch(() => undefined); - await store.delete(s.id); - res.json({ ok: true }); - }), - ); -} diff --git a/plugins/ai/src/backend/agents/session-state.ts b/plugins/ai/src/backend/agents/session-state.ts deleted file mode 100644 index 179692374..000000000 --- a/plugins/ai/src/backend/agents/session-state.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { randomUUID } from "node:crypto"; -import { fitsText, type AgentSession, type QueuedPrompt } from "./types.js"; - -export function promptInput( - s: AgentSession, - body: Record, -): QueuedPrompt { - const text = body.text; - const attachmentIds = body.attachmentIds ?? []; - if ( - typeof text !== "string" || - !fitsText(text) || - !Array.isArray(attachmentIds) || - attachmentIds.length > 4 || - attachmentIds.some( - (id) => - typeof id !== "string" || !s.attachments?.some((a) => a.id === id), - ) || - (!text.trim() && !attachmentIds.length) - ) - throw Error("Provide a message and up to four session attachments"); - return { id: randomUUID(), text, attachmentIds: [...new Set(attachmentIds)] }; -} -export function updateSession( - s: AgentSession, - body: Record, -): void { - if (body.title !== undefined) { - if (typeof body.title !== "string" || body.title.length > 120) - throw Error("Session title must be at most 120 characters"); - } - if (body.archived !== undefined && typeof body.archived !== "boolean") - throw Error("Invalid archive state"); - if ( - body.draft !== undefined && - (typeof body.draft !== "string" || !fitsText(body.draft)) - ) - throw Error("Draft is too long"); - if (body.title !== undefined) s.title = (body.title as string).trim(); - if (body.archived !== undefined) s.archived = body.archived as boolean; - if (body.draft !== undefined) s.draft = body.draft as string; -} -export function updateQueue( - s: AgentSession, - body: Record, -): void { - const queue = s.queue ?? []; - if (body.operation === "add") { - if (s.archived || queue.length >= 20) - throw Error( - "Restore the session or remove a queued message (maximum 20)", - ); - s.queue = [...queue, promptInput(s, body)]; - } else if (body.operation === "update") { - const i = queue.findIndex((p) => p.id === body.id); - if (i < 0) throw Error("Queued message has already been sent or removed"); - const updated = promptInput(s, body); - s.queue = queue.map((p, n) => (n === i ? { ...updated, id: p.id } : p)); - } else if (body.operation === "remove") { - s.queue = queue.filter((p) => p.id !== body.id); - } else if (body.operation === "continue") s.queuePaused = false; - else if (body.operation === "pause") s.queuePaused = true; - else throw Error("Unknown queue operation"); -} diff --git a/plugins/ai/src/backend/agents/store.ts b/plugins/ai/src/backend/agents/store.ts deleted file mode 100644 index 50032923c..000000000 --- a/plugins/ai/src/backend/agents/store.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { and, asc, desc, eq, gt, lte } from "drizzle-orm"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { agentEvents, agentQueue, agentSessions } from "../tables.js"; -import type { AgentEvent, AgentSession } from "./types.js"; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -// Tables come from ctx.db.define, which the SDK hands back untyped. -type Table = any; -type Drizzle = any; -/* eslint-enable @typescript-eslint/no-explicit-any */ - -/** Events kept per session; older ones are pruned as new ones land. */ -export const EVENT_HISTORY = 2000; - -type SessionRow = { - sessionId: string; - userId: string; - hostId: number; - providerId: number; - agent: AgentSession["agent"]; - model: string; - cwd: string; - executable: string; - nativeId: string | null; - title: string | null; - archived: boolean; - queuePaused: boolean; - draft: string | null; - attachments: string; - updatedAt: string; -}; - -function toRow(s: AgentSession): SessionRow { - return { - sessionId: s.id, - userId: s.userId, - hostId: s.hostId, - providerId: s.providerId, - agent: s.agent, - model: s.model, - cwd: s.cwd, - executable: s.executable, - nativeId: s.nativeId ?? null, - title: s.title ?? null, - archived: !!s.archived, - queuePaused: !!s.queuePaused, - draft: s.draft ?? null, - attachments: JSON.stringify(s.attachments ?? []), - updatedAt: s.updatedAt, - }; -} - -function fromRow(row: SessionRow): AgentSession { - return { - id: row.sessionId, - userId: row.userId, - hostId: Number(row.hostId), - providerId: Number(row.providerId), - agent: row.agent, - model: row.model, - cwd: row.cwd, - executable: row.executable, - nativeId: row.nativeId ?? undefined, - title: row.title ?? undefined, - archived: !!row.archived, - queuePaused: !!row.queuePaused, - draft: row.draft ?? undefined, - attachments: JSON.parse(row.attachments), - status: "stopped", - events: [], - updatedAt: row.updatedAt, - }; -} - -/** - * Agent sessions in the plugin's own tables. A session row holds what the - * user configured; events and queued prompts are separate rows, so a long run - * appends a few rows a second instead of rewriting everything it said. - */ -export async function createAgentStore(ctx: Pick) { - const { db } = ctx; - const sessions: Table = await db.define(agentSessions); - const events: Table = await db.define(agentEvents); - const queue: Table = await db.define(agentQueue); - const client = () => db.client(); - - async function writeSession(d: Drizzle, s: AgentSession) { - const row = toRow(s); - const where = eq(sessions.sessionId, s.id); - const existing = await d.select().from(sessions).where(where).limit(1); - if (existing.length) await d.update(sessions).set(row).where(where); - else await d.insert(sessions).values(row); - } - - return { - async list(userId: string): Promise { - const rows = await ( - await client() - ) - .select() - .from(sessions) - .where(eq(sessions.userId, userId)); - return rows.map(fromRow); - }, - - /** The session without its events, which only the event stream needs. */ - async get(userId: string, id: string): Promise { - const d = await client(); - const [row] = await d - .select() - .from(sessions) - .where(and(eq(sessions.sessionId, id), eq(sessions.userId, userId))) - .limit(1); - if (!row) return null; - const s = fromRow(row); - const prompts = await d - .select() - .from(queue) - .where(eq(queue.sessionId, id)) - .orderBy(asc(queue.position)); - if (prompts.length) - s.queue = prompts.map( - (p: { promptId: string; text: string; attachmentIds: string }) => ({ - id: p.promptId, - text: p.text, - attachmentIds: JSON.parse(p.attachmentIds), - }), - ); - return s; - }, - - async events(id: string, after = 0): Promise { - const rows = await ( - await client() - ) - .select() - .from(events) - .where(and(eq(events.sessionId, id), gt(events.seq, after))) - .orderBy(desc(events.seq)) - .limit(EVENT_HISTORY); - return rows - .reverse() - .map( - (e: { - seq: number; - kind: AgentEvent["kind"]; - text: string; - requestId: string | null; - choices: string | null; - }) => ({ - seq: Number(e.seq), - kind: e.kind, - text: e.text, - ...(e.requestId ? { requestId: e.requestId } : {}), - ...(e.choices ? { choices: JSON.parse(e.choices) } : {}), - }), - ); - }, - - /** Session fields and queue. Durable before it resolves. */ - async save(s: AgentSession): Promise { - const d = await client(); - await writeSession(d, s); - await d.delete(queue).where(eq(queue.sessionId, s.id)); - const prompts = s.queue ?? []; - if (prompts.length) - await d.insert(queue).values( - prompts.map((p, position) => ({ - sessionId: s.id, - userId: s.userId, - promptId: p.id, - position, - text: p.text, - attachmentIds: JSON.stringify(p.attachmentIds), - })), - ); - await db.persist(); - }, - - /** - * Session fields plus new events. Informational, so it joins the next - * periodic save rather than forcing one per batch. - */ - async append(s: AgentSession, batch: AgentEvent[]): Promise { - if (!batch.length) return; - const d = await client(); - const lastSeq = batch[batch.length - 1].seq; - await d.insert(events).values( - batch.map((e) => ({ - sessionId: s.id, - userId: s.userId, - seq: e.seq, - kind: e.kind, - text: e.text, - requestId: e.requestId ?? null, - choices: e.choices ? JSON.stringify(e.choices) : null, - })), - ); - await d - .delete(events) - .where( - and( - eq(events.sessionId, s.id), - lte(events.seq, lastSeq - EVENT_HISTORY), - ), - ); - await writeSession(d, s); - await db.persist({ lazy: true }); - }, - - async delete(id: string): Promise { - const d = await client(); - await d.delete(events).where(eq(events.sessionId, id)); - await d.delete(queue).where(eq(queue.sessionId, id)); - await d.delete(sessions).where(eq(sessions.sessionId, id)); - await db.persist(); - }, - }; -} - -export type AgentStore = Awaited>; diff --git a/plugins/ai/src/backend/agents/types.ts b/plugins/ai/src/backend/agents/types.ts deleted file mode 100644 index 42660e316..000000000 --- a/plugins/ai/src/backend/agents/types.ts +++ /dev/null @@ -1,84 +0,0 @@ -export const AGENTS = ["pi", "opencode", "claude", "codex"] as const; -export type AgentKind = (typeof AGENTS)[number]; -export interface AgentEvent { - seq: number; - kind: "text" | "tool" | "permission" | "status" | "error" | "user" | "state"; - text: string; - requestId?: string; - choices?: string[]; -} -export interface AgentAttachment { - id: string; - name: string; - path: string; - mime: string; - size: number; -} -export interface QueuedPrompt { - id: string; - text: string; - attachmentIds: string[]; -} -export interface AgentSession { - title?: string; - archived?: boolean; - draft?: string; - queue?: QueuedPrompt[]; - queuePaused?: boolean; - attachments?: AgentAttachment[]; - - id: string; - userId: string; - hostId: number; - providerId: number; - agent: AgentKind; - model: string; - cwd: string; - executable: string; - nativeId?: string; - status: "starting" | "ready" | "running" | "stopped" | "error"; - events: AgentEvent[]; - updatedAt: string; -} -export function compatible(agent: AgentKind, providerType: string): boolean { - if (agent === "claude") return providerType === "anthropic"; - if (agent === "codex") - return ["openai", "openai_compatible"].includes(providerType); - return ["openai", "openai_compatible", "anthropic", "ollama"].includes( - providerType, - ); -} -/** MySQL stores plugin text as TEXT, which holds 65535 bytes. */ -export const MAX_TEXT_BYTES = 65535; -export function fitsText(value: string): boolean { - return Buffer.byteLength(value, "utf8") <= MAX_TEXT_BYTES; -} -/** Cut to fit; the slack covers a split character decoding to U+FFFD. */ -export function clipText(value: string): string { - if (fitsText(value)) return value; - return Buffer.from(value, "utf8") - .subarray(0, MAX_TEXT_BYTES - 3) - .toString("utf8"); -} -export function shellQuote(value: string): string { - return "'" + value.replaceAll("'", "'\\''") + "'"; -} -export function validStart(value: Record): boolean { - return ( - AGENTS.includes(value.agent as AgentKind) && - Number.isSafeInteger(value.hostId) && - Number(value.hostId) > 0 && - Number.isSafeInteger(value.providerId) && - Number(value.providerId) > 0 && - typeof value.model === "string" && - value.model.length > 0 && - value.model.length <= 200 && - typeof value.cwd === "string" && - value.cwd.startsWith("/") && - value.cwd.length <= 4096 && - !value.cwd.includes("\0") && - (value.executable === undefined || - (typeof value.executable === "string" && - /^[\w./-]{1,1024}$/.test(value.executable))) - ); -} diff --git a/plugins/ai/src/backend/agents/workspace.ts b/plugins/ai/src/backend/agents/workspace.ts deleted file mode 100644 index c3596648b..000000000 --- a/plugins/ai/src/backend/agents/workspace.ts +++ /dev/null @@ -1,153 +0,0 @@ -import type { Client, ClientChannel } from "ssh2"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { shellQuote, type AgentSession } from "./types.js"; - -/** Fixed operations; all paths and content arrive as JSON on stdin, never shell source. */ -export const REMOTE_WORKSPACE = String.raw` -const fs=require('node:fs'),path=require('node:path'),os=require('node:os'); -const {execFileSync}=require('node:child_process'); -const MAX=1024*1024; -function imageMime(data) { - if(data.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))) return 'image/png'; - if(data[0]===255 && data[1]===216 && data[2]===255) return 'image/jpeg'; - if(['GIF87a','GIF89a'].includes(data.subarray(0,6).toString())) return 'image/gif'; - if(data.subarray(0,4).toString()==='RIFF' && data.subarray(8,12).toString()==='WEBP') return 'image/webp'; - return 'application/octet-stream'; -} -function main(m) { - const cwd=fs.realpathSync(m.cwd), body=m.body; - const inside=(value,missing=false)=>{ - if(typeof value!=='string' || value.includes('\0')) throw Error('Invalid path'); - const requested=path.resolve(cwd,value); - let ancestor=requested; - if(missing) while(!fs.existsSync(ancestor) && path.dirname(ancestor)!==ancestor) ancestor=path.dirname(ancestor); - const p=path.resolve(fs.realpathSync(ancestor),path.relative(ancestor,requested)); - const relative=path.relative(cwd,p); - if(relative==='..' || relative.startsWith('../') || path.isAbsolute(relative)) throw Error('Choose a file inside the working directory'); - return p; - }; - function file(p) { - const fd=fs.openSync(p,fs.constants.O_RDONLY|fs.constants.O_NOFOLLOW); - try { - const stat=fs.fstatSync(fd); - if(!stat.isFile() || stat.size>MAX) throw Error('Choose a regular file no larger than 1 MiB'); - const data=Buffer.alloc(stat.size), size=fs.readSync(fd,data,0,data.length,0); - return data.subarray(0,size); - } finally {fs.closeSync(fd);} - } - function git(args) { - return execFileSync('git',['--no-pager','-c','core.hooksPath=/dev/null','-c','core.fsmonitor=false',...args],{cwd,encoding:'utf8',maxBuffer:2*MAX,timeout:20000,env:{...process.env,GIT_TERMINAL_PROMPT:'0',GIT_OPTIONAL_LOCKS:'0'}}); - } - if(body.operation==='upload' || body.operation==='reference') { - let p,data; - if(body.operation==='reference') {p=inside(body.path);data=file(p);} - else { - if(typeof body.name!=='string' || !body.name || body.name.length>255 || body.name.includes('\0') || typeof body.data!=='string' || body.data.length>Math.ceil(MAX/3)*4 || !/^[A-Za-z0-9+/]*={0,2}$/.test(body.data)) throw Error('Invalid upload (maximum 1 MiB)'); - data=Buffer.from(body.data,'base64'); - if(data.length>MAX) throw Error('Upload exceeds 1 MiB'); - const dir=path.join(os.homedir(),'.local','state','termix-agents',m.id,'attachments'); - fs.mkdirSync(dir,{recursive:true,mode:0o700}); - p=path.join(dir,require('node:crypto').randomUUID()+'-'+path.basename(body.name).replace(/[^\p{L}\p{N}._-]/gu,'_')); - fs.writeFileSync(p,data,{mode:0o600,flag:'wx'}); - } - return {id:require('node:crypto').randomUUID(),name:body.operation==='upload'?path.basename(body.name):path.basename(p),path:p,mime:imageMime(data),size:data.length}; - } - if(body.operation==='status') { - const root=git(['rev-parse','--show-toplevel']).trim(); - const records=git(['status','--porcelain=v1','-z','--untracked-files=all']).split('\0'),files=[]; - for(let i=0;i'+'+l).join('\n')}; - } - return {diff:'--- Unstaged ---\n'+git(['diff','--no-ext-diff','--no-textconv','--',rel])+'\n--- Staged ---\n'+git(['diff','--cached','--no-ext-diff','--no-textconv','--',rel])}; - } - if(body.operation==='worktree') { - if(typeof body.branch!=='string' || !/^[A-Za-z0-9][A-Za-z0-9._/-]{0,100}$/.test(body.branch)) throw Error('Invalid branch name'); - git(['check-ref-format','--branch',body.branch]); - const dir=path.join(os.homedir(),'.local','state','termix-agents','worktrees');fs.mkdirSync(dir,{recursive:true,mode:0o700}); - const target=path.join(dir,require('node:crypto').randomUUID()); - git(['worktree','add','-b',body.branch,'--',target,'HEAD']); - return {path:target,branch:body.branch}; - } - throw Error('Unknown workspace operation'); -} -let input='';process.stdin.on('data',d=>{input+=d;if(input.length>2*MAX) {process.stderr.write('Request too large');process.exit(1);}}); -process.stdin.on('end',()=>{try{process.stdout.write(JSON.stringify(main(JSON.parse(input))));}catch(e){process.stderr.write(e.message);process.exitCode=1;}}); -`; - -export async function workspaceOperation( - ctx: PluginContext, - s: AgentSession, - body: Record, -): Promise> { - if ( - !["upload", "reference", "status", "diff", "worktree"].includes( - String(body.operation), - ) - ) - throw Error("Unknown workspace operation"); - if (JSON.stringify(body).length > 1500000) - throw Error("Upload exceeds 1 MiB"); - const connection = await ctx.ssh.connect(s.hostId, { - purpose: "ai-agent-workspace", - profile: "session", - timeoutMs: 15000, - }); - let channel: ClientChannel | undefined; - try { - const command = - 'export PATH="$HOME/.local/share/termix-agent-runtime/node/bin:$PATH"; exec node -e ' + - shellQuote(REMOTE_WORKSPACE); - return await new Promise((resolve, reject) => { - const timer = setTimeout(() => { - channel?.close(); - reject(Error("Workspace operation timed out")); - }, 30000); - connection.client.exec("sh -lc " + shellQuote(command), (error, ch) => { - if (error) { - clearTimeout(timer); - reject(error); - return; - } - channel = ch; - let output = "", - stderr = ""; - ch.on("data", (d: Buffer) => { - output += d.toString(); - if (output.length > 3 * 1024 * 1024) { - ch.close(); - reject(Error("Result exceeds 3 MiB; choose a smaller file")); - } - }); - ch.stderr.on("data", (d: Buffer) => { - stderr = (stderr + d.toString()).slice(-8000); - }); - ch.on("error", (error) => { - clearTimeout(timer); - reject(error); - }); - ch.on("close", (code: number) => { - clearTimeout(timer); - if (code !== 0) { - reject(Error(stderr || "Workspace operation failed")); - return; - } - try { - resolve(JSON.parse(output)); - } catch { - reject(Error("Invalid workspace response")); - } - }); - ch.end(JSON.stringify({ id: s.id, cwd: s.cwd, body })); - }); - }); - } finally { - connection.dispose(); - } -} diff --git a/plugins/ai/src/backend/context.ts b/plugins/ai/src/backend/context.ts deleted file mode 100644 index a66a79513..000000000 --- a/plugins/ai/src/backend/context.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * The system prompt. - * - * Deliberately small: the assistant starts with almost no context and must call - * a read tool to learn anything. That keeps token cost predictable, makes every - * data access visible in the transcript, and means nothing is sent to a - * third-party provider that the user did not implicitly ask for. - */ -export function buildSystemPrompt(options: { - hostCount: number; - activeTab?: string | null; - allowReadOnlyCommands: boolean; -}): string { - const lines: string[] = [ - "You are the assistant built into Termix, a self-hosted server management app.", - "You help the user manage their servers, snippets, automations and fleets.", - "", - "How you work:", - "- You start with no knowledge of this user's setup. Call a read tool to find out anything you need.", - "- You cannot change anything directly. To make a change, call a propose_* tool; the user sees a card and approves or rejects it.", - "- You have no access to passwords, SSH keys, API keys or any other credential, and you never ask the user to paste one into the chat.", - "", - "Scope:", - "- Do exactly what was asked, and nothing beyond it. A question is a request for an answer, not for changes.", - "- Questions like 'what is running on this server', 'check this host' or 'why is this slow' are answered with information. Read, then report. Do not propose anything.", - "- Only propose a change when the user asked for one, in words like 'add', 'create', 'set up', 'fix' or 'change'.", - "- Do not propose follow-up work you thought of yourself: no monitoring automations, no scripts, no snippets, no cleanup, unless that is what was requested.", - "- If you think something is worth doing, say so in one sentence and stop. Let the user ask.", - "- One request means one proposal at most. Do not bundle extras alongside it.", - "", - "How to answer:", - "- Lead with the answer. Keep responses short and concrete.", - "- When you propose something, say in one line what it does and why.", - "- If a request is ambiguous in a way that changes what you would propose, ask before proposing.", - "- Never claim you have done something. You propose; the user applies.", - ]; - - if (options.allowReadOnlyCommands) { - lines.push( - "- The user has allowed you to run read-only diagnostic commands directly. Anything that changes state still has to be proposed.", - ); - } - - lines.push( - "", - "Current context:", - `- The user has ${options.hostCount} host${options.hostCount === 1 ? "" : "s"} configured.`, - ); - - if (options.activeTab) { - lines.push(`- They are currently looking at: ${options.activeTab}.`); - } - - return lines.join("\n"); -} diff --git a/plugins/ai/src/backend/egress.ts b/plugins/ai/src/backend/egress.ts deleted file mode 100644 index c8f7026ad..000000000 --- a/plugins/ai/src/backend/egress.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { BlockList, isIP } from "net"; - -/** - * Where the assistant is allowed to send requests. - * - * Every provider request goes through ctx.fetch, which refuses private and - * loopback addresses (after DNS, too) unless the exact host is listed. That - * guard is exactly what a self-hosted Ollama on localhost trips over, so the - * admin setting "privateEndpoints" names the hosts that may be private. - * Without that split, any logged-in user could point a "provider" at an - * internal service and use the backend as an authenticated probe of the - * server's own network. - * - * The checks here only exist to give a useful message before the request is - * made. ctx.fetch is the control. - */ - -/** Hosts a self-hoster almost certainly wants, and which reach only this machine. */ -export const DEFAULT_PRIVATE_ALLOWLIST = [ - "localhost", - "127.0.0.1", - "::1", - "host.docker.internal", -]; - -export const PRIVATE_DESTINATION_MESSAGE = - "This address is on a private network. An administrator must add its host to the AI endpoint allowlist first."; - -/** A bare host, not a URL: no scheme, path, port or whitespace. */ -const HOST_PATTERN = /^[a-z0-9._:-]+$/; - -/** - * The admin setting is one host per line. Anything that is not a bare host - * is ignored rather than failing the whole list. - */ -export function parseAllowlist(raw: unknown): string[] { - if (typeof raw !== "string") return [...DEFAULT_PRIVATE_ALLOWLIST]; - const hosts: string[] = []; - for (const line of raw.split(/[\r\n,]+/)) { - const host = line.trim().toLowerCase(); - if (host && HOST_PATTERN.test(host) && !hosts.includes(host)) { - hosts.push(host); - } - } - return hosts; -} - -const PRIVATE_RANGES = (() => { - const list = new BlockList(); - list.addSubnet("0.0.0.0", 8, "ipv4"); - list.addSubnet("10.0.0.0", 8, "ipv4"); - list.addSubnet("100.64.0.0", 10, "ipv4"); - list.addSubnet("127.0.0.0", 8, "ipv4"); - list.addSubnet("169.254.0.0", 16, "ipv4"); - list.addSubnet("172.16.0.0", 12, "ipv4"); - list.addSubnet("192.168.0.0", 16, "ipv4"); - list.addAddress("::", "ipv6"); - list.addAddress("::1", "ipv6"); - list.addSubnet("fc00::", 7, "ipv6"); - list.addSubnet("fe80::", 10, "ipv6"); - return list; -})(); - -function normalizeHost(hostname: string): string { - return hostname.replace(/^\[|\]$/g, "").toLowerCase(); -} - -/** - * True when the URL names a destination the outbound guard would refuse. A - * bare hostname that is not an IP literal (e.g. "ollama.internal") counts as - * private only if it is "localhost"; anything else needs DNS resolution, - * which ctx.fetch does. - */ -export function isPrivateDestination(rawUrl: string): boolean { - let url: URL; - try { - url = new URL(rawUrl); - } catch { - return false; - } - const host = normalizeHost(url.hostname); - if (host === "localhost" || host.endsWith(".localhost")) return true; - const family = isIP(host); - if (family === 4) return PRIVATE_RANGES.check(host, "ipv4"); - if (family === 6) return PRIVATE_RANGES.check(host, "ipv6"); - return false; -} - -export interface EgressDecision { - allowed: boolean; - /** True when the destination is an allowlisted private host. */ - isPrivate: boolean; - reason?: string; -} - -export function evaluateEgress( - rawUrl: string, - allowlist: string[], -): EgressDecision { - let url: URL; - try { - url = new URL(rawUrl); - } catch { - return { allowed: false, isPrivate: false, reason: "Invalid URL" }; - } - - if (!["http:", "https:"].includes(url.protocol)) { - return { allowed: false, isPrivate: false, reason: "Unsupported protocol" }; - } - if (url.username || url.password) { - return { - allowed: false, - isPrivate: false, - reason: "Credentials in URL are not allowed", - }; - } - - const host = normalizeHost(url.hostname); - const normalized = allowlist.map((entry) => entry.trim().toLowerCase()); - - // An allowlisted hostname may resolve to a private address; ctx.fetch - // lets it through because the same list is passed as allowPrivateHosts. - if (normalized.includes(host)) { - return { allowed: true, isPrivate: true }; - } - - if (!isPrivateDestination(rawUrl)) return { allowed: true, isPrivate: false }; - - return { - allowed: false, - isPrivate: true, - reason: PRIVATE_DESTINATION_MESSAGE, - }; -} diff --git a/plugins/ai/src/backend/engine.ts b/plugins/ai/src/backend/engine.ts deleted file mode 100644 index 1cf4e9096..000000000 --- a/plugins/ai/src/backend/engine.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { getErrorMessage } from "./errors.js"; -import { describeProviderError } from "./providers/http.js"; -import { getAdapter } from "./providers/registry.js"; -import type { - ChatMessage, - ProviderConfig, - ToolCall, -} from "./providers/types.js"; -import { redact, redactToJson } from "./redaction.js"; -import { toolDefinitions } from "./tools/catalog.js"; -import { - isProposalDraft, - type AiTool, - type ProposalDraft, - type ToolContext, -} from "./tools/types.js"; - -/** - * The agent loop: stream a turn, run any tools the model asked for, feed the - * results back, repeat. Bounded so a model that keeps calling tools cannot spin - * forever. - */ - -const MAX_TURNS = 8; - -export type EngineEvent = - | { type: "token"; text: string } - | { type: "tool_call"; name: string; arguments: Record } - | { type: "tool_result"; name: string; result: unknown } - | { type: "proposal"; draft: ProposalDraft } - /** A message added to the conversation, for the caller to store. */ - | { type: "message"; message: ChatMessage } - | { type: "done" } - | { type: "error"; message: string }; - -export interface EngineOptions { - config: ProviderConfig; - model: string; - system: string; - history: ChatMessage[]; - context: ToolContext; - /** What the model may call this turn; availableTools() decides. */ - tools: AiTool[]; - signal?: AbortSignal; -} - -export async function* runAgent( - options: EngineOptions, -): AsyncGenerator { - const adapter = getAdapter(options.config.providerType); - const tools = toolDefinitions(options.tools); - const byName = new Map(options.tools.map((tool) => [tool.name, tool])); - const messages: ChatMessage[] = [...options.history]; - - for (let turn = 0; turn < MAX_TURNS; turn += 1) { - let text = ""; - const calls: ToolCall[] = []; - let failed = false; - - try { - for await (const chunk of adapter.streamChat(options.config, { - model: options.model, - system: options.system, - messages, - tools, - signal: options.signal, - })) { - if (chunk.type === "text") { - text += chunk.text; - yield { type: "token", text: chunk.text }; - } else if (chunk.type === "tool_call") { - calls.push(chunk.call); - } else if (chunk.type === "error") { - failed = true; - yield { type: "error", message: chunk.message }; - } - } - } catch (error) { - // The user stopped it; there is no one left to show an error to. - if (options.signal?.aborted) return; - const message = describeProviderError( - error, - "The provider request failed", - ); - yield { type: "error", message }; - return; - } - - if (failed) return; - - if (!calls.length) { - yield { type: "message", message: { role: "assistant", content: text } }; - yield { type: "done" }; - return; - } - - const assistant: ChatMessage = { - role: "assistant", - content: text, - toolCalls: calls, - }; - messages.push(assistant); - yield { type: "message", message: assistant }; - - for (const call of calls) { - if (options.signal?.aborted) return; - yield { type: "tool_call", name: call.name, arguments: call.arguments }; - - const result = await runTool(byName, call, options.context); - - if (isProposalDraft(result)) { - // Closes the tool call before the proposal card is emitted. Without - // this the call has no matching result and renders as permanently - // running, even though the work is done and awaiting the user. - yield { - type: "tool_result", - name: call.name, - result: { status: "awaiting_user_approval" }, - }; - yield { type: "proposal", draft: result }; - // The model is told the proposal is awaiting the user rather than done, - // so it does not go on to describe the change as applied. - const pending: ChatMessage = { - role: "tool", - content: JSON.stringify({ - status: "awaiting_user_approval", - summary: result.summary, - }), - toolCallId: call.id, - toolName: call.name, - }; - messages.push(pending); - yield { type: "message", message: pending }; - continue; - } - - yield { type: "tool_result", name: call.name, result: redact(result) }; - const answer: ChatMessage = { - role: "tool", - content: redactToJson(result), - toolCallId: call.id, - toolName: call.name, - }; - messages.push(answer); - yield { type: "message", message: answer }; - } - } - - // Ran out of turns with the model still calling tools. - yield { - type: "error", - message: "The assistant used too many steps without finishing.", - }; -} - -async function runTool( - tools: Map, - call: ToolCall, - context: ToolContext, -): Promise { - const tool = tools.get(call.name); - - // A model can emit any name it likes; only what was offered runs. - if (!tool) { - return { error: `Unknown tool: ${call.name}` }; - } - - try { - return await tool.handler(call.arguments ?? {}, context); - } catch (error) { - return { - error: getErrorMessage(error, "The tool failed"), - }; - } -} diff --git a/plugins/ai/src/backend/errors.ts b/plugins/ai/src/backend/errors.ts deleted file mode 100644 index a0766fbd5..000000000 --- a/plugins/ai/src/backend/errors.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** The message of an error, or the fallback when there is none worth showing. */ -export function getErrorMessage(error: unknown, fallback: string): string { - if (error instanceof Error && error.message) return error.message; - if (typeof error === "string" && error) return error; - return fallback; -} diff --git a/plugins/ai/src/backend/gating.ts b/plugins/ai/src/backend/gating.ts deleted file mode 100644 index dac62d2f1..000000000 --- a/plugins/ai/src/backend/gating.ts +++ /dev/null @@ -1,90 +0,0 @@ -import type { PluginSettings } from "@termix/plugin-sdk/backend"; -import { DEFAULT_PRIVATE_ALLOWLIST, parseAllowlist } from "./egress.js"; - -/** - * Three gates, all checked on the server. - * - * The admin switch is a hard kill switch: when it is off the feature does not - * exist for anyone, regardless of what any user has enabled. It defaults to - * false so upgrading an existing install turns nothing on by surprise. Then - * each user opts in for themselves, and RBAC decides what they may do. - */ - -export type SettingsReader = Pick; - -export async function isAiGloballyEnabled( - settings: SettingsReader, -): Promise { - return (await settings.get("globallyEnabled")) === true; -} - -/** The admin's private endpoint hosts, one per line. */ -export async function readPrivateAllowlist( - settings: SettingsReader, -): Promise { - const raw = await settings.get("privateEndpoints"); - if (raw === undefined || raw === null) return [...DEFAULT_PRIVATE_ALLOWLIST]; - return parseAllowlist(raw); -} - -export interface AiAccess { - enabled: boolean; - allowReadOnlyCommands: boolean; -} - -export async function resolveAiAccess( - settings: SettingsReader, - userId: string, -): Promise { - if (!(await isAiGloballyEnabled(settings))) { - return { enabled: false, allowReadOnlyCommands: false }; - } - - // Unset means the user was never asked, which is not consent. - const enabled = (await settings.getUser(userId, "enabled")) === true; - return { - enabled, - allowReadOnlyCommands: - enabled && - (await settings.getUser(userId, "allowReadOnlyCommands")) === - true, - }; -} - -interface GateRequest { - aiAccess?: AiAccess; -} - -interface GateResponse { - status: (code: number) => { json: (body: unknown) => unknown }; -} - -/** Rejects any AI request unless both gates are open. */ -export function createAiGate( - settings: SettingsReader, - currentActor: () => string | undefined, -) { - return async ( - req: GateRequest, - res: GateResponse, - next: (error?: unknown) => void, - ): Promise => { - const userId = currentActor(); - if (!userId) { - res.status(401).json({ error: "Authentication required" }); - return; - } - - try { - const access = await resolveAiAccess(settings, userId); - if (!access.enabled) { - res.status(403).json({ error: "The AI assistant is not enabled" }); - return; - } - req.aiAccess = access; - next(); - } catch (error) { - next(error); - } - }; -} diff --git a/plugins/ai/src/backend/history.ts b/plugins/ai/src/backend/history.ts deleted file mode 100644 index c5be09cff..000000000 --- a/plugins/ai/src/backend/history.ts +++ /dev/null @@ -1,98 +0,0 @@ -import type { ChatMessage, ToolCall } from "./providers/types.js"; - -export interface StoredMessage { - role: string; - content: string; - toolCalls: string | null; -} - -const INTERRUPTED_RESULT = JSON.stringify({ - error: "No result: the reply was interrupted before this tool finished.", -}); - -function parseCalls(raw: string | null): ToolCall[] { - if (!raw) return []; - try { - const parsed = JSON.parse(raw); - return Array.isArray(parsed) ? parsed : []; - } catch { - return []; - } -} - -/** A tool row keeps the call it answers in toolCalls, as a one-item list. */ -export function toStoredMessage(message: ChatMessage): StoredMessage { - if (message.role === "tool") { - return { - role: "tool", - content: message.content, - toolCalls: JSON.stringify([ - { id: message.toolCallId, name: message.toolName }, - ]), - }; - } - return { - role: message.role, - content: message.content, - toolCalls: message.toolCalls?.length - ? JSON.stringify(message.toolCalls) - : null, - }; -} - -/** - * Rebuilds what the provider sees from the stored rows. - * - * Every provider rejects an assistant turn whose tool calls have no results, - * which is what a run cut off mid-way leaves behind, so any missing result is - * filled in as interrupted. Tool rows that answer nothing are dropped. - */ -export function toChatHistory(rows: StoredMessage[]): ChatMessage[] { - const history: ChatMessage[] = []; - let open: ToolCall[] = []; - - const closeOpen = () => { - for (const call of open) { - history.push({ - role: "tool", - content: INTERRUPTED_RESULT, - toolCallId: call.id, - toolName: call.name, - }); - } - open = []; - }; - - for (const row of rows) { - if (row.role === "tool") { - const answered = parseCalls(row.toolCalls)[0]; - const index = open.findIndex((call) => call.id === answered?.id); - if (index === -1) continue; - const [call] = open.splice(index, 1); - history.push({ - role: "tool", - content: row.content, - toolCallId: call.id, - toolName: call.name, - }); - continue; - } - - closeOpen(); - - if (row.role === "assistant") { - const calls = parseCalls(row.toolCalls); - history.push({ - role: "assistant", - content: row.content, - ...(calls.length ? { toolCalls: calls } : {}), - }); - open = [...calls]; - } else if (row.role === "user") { - history.push({ role: "user", content: row.content }); - } - } - - closeOpen(); - return history; -} diff --git a/plugins/ai/src/backend/host-import.ts b/plugins/ai/src/backend/host-import.ts deleted file mode 100644 index a1b95d26b..000000000 --- a/plugins/ai/src/backend/host-import.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Registered as "ai.hostImportNormalizer" so core's host import can carry the - * assistant switch without knowing it exists. Reads a Termix export's - * pluginSettings.ai first, then the flat field hosts had before 2.9.0. - */ -export function hostImportNormalizer( - raw: Record, -): Record | null { - const pluginSettings = raw.pluginSettings as - Record | undefined> | undefined; - const value = pluginSettings?.ai?.enableAiAssistant ?? raw.enableAiAssistant; - if (value === undefined || value === null) return null; - return { - enableAiAssistant: value === true || value === 1 || value === "true", - }; -} diff --git a/plugins/ai/src/backend/index.ts b/plugins/ai/src/backend/index.ts deleted file mode 100644 index 7d90fe487..000000000 --- a/plugins/ai/src/backend/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Router } from "express"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { hostImportNormalizer } from "./host-import.js"; -import { createAiRepository } from "./repository.js"; -import { registerProviderSync } from "./sync.js"; -import { registerAgentRoutes } from "./agents/routes.js"; -import { createAgentStore } from "./agents/store.js"; -import { registerAiRoutes } from "./routes.js"; - -export async function activate(ctx: PluginContext) { - const repository = await createAiRepository(ctx); - await registerProviderSync(ctx, repository); - - const router = ctx.http.router(); - registerAiRoutes(router, repository, ctx); - registerAgentRoutes(router, repository, ctx, await createAgentStore(ctx)); - - ctx.registry.provide("ai.hostImportNormalizer", hostImportNormalizer); - ctx.disposables.add( - () => - void ctx.registry.revoke("ai.hostImportNormalizer", hostImportNormalizer), - ); - - // Offered by reference, not copied: this hands over a resolver, so the key - // stays ours. Rotating or deleting the provider takes effect on the next - // read by any borrower, and deactivate withdraws the offer. Keys are per - // user, so a borrowed read returns the acting user's own key. - ctx.secrets.offer("api-key", (userId: string) => - ctx.asUser(userId, async () => { - const providers = await repository.listProviders(userId); - const preferred = providers.find((provider) => provider.apiKeyPrefix); - if (!preferred) return null; - const withSecret = await repository.findProviderWithSecret( - preferred.id, - userId, - ); - return withSecret?.apiKey ?? null; - }), - ); - - ctx.log.info("AI assistant routes mounted at /plugin-api/ai"); -} - -export async function deactivate() {} diff --git a/plugins/ai/src/backend/providers/anthropic.ts b/plugins/ai/src/backend/providers/anthropic.ts deleted file mode 100644 index 34d7d0d85..000000000 --- a/plugins/ai/src/backend/providers/anthropic.ts +++ /dev/null @@ -1,161 +0,0 @@ -import Anthropic from "@anthropic-ai/sdk"; -import type { - ChatChunk, - ChatRequest, - ProviderAdapter, - ProviderConfig, -} from "./types.js"; -import { AiProviderError } from "./types.js"; - -/** - * Model ids offered in the picker. Users can type any other id; this is a - * convenience list, not a restriction. - */ -export const ANTHROPIC_MODELS = [ - "claude-opus-5", - "claude-sonnet-5", - "claude-haiku-4-5", -]; - -function createClient(config: ProviderConfig): Anthropic { - if (!config.apiKey) { - throw new AiProviderError("This provider needs an API key"); - } - return new Anthropic({ - apiKey: config.apiKey, - ...(config.baseUrl?.trim() ? { baseURL: config.baseUrl.trim() } : {}), - // Routes the SDK's HTTP through the shared egress guard. - fetch: config.fetch as unknown as typeof fetch, - }); -} - -function toAnthropicMessages(request: ChatRequest): Anthropic.MessageParam[] { - const messages: Anthropic.MessageParam[] = []; - - for (const message of request.messages) { - if (message.role === "tool") { - messages.push({ - role: "user", - content: [ - { - type: "tool_result", - tool_use_id: message.toolCallId ?? "", - content: message.content, - }, - ], - }); - continue; - } - - if (message.role === "assistant" && message.toolCalls?.length) { - const content: Anthropic.ContentBlockParam[] = []; - if (message.content) - content.push({ type: "text", text: message.content }); - for (const call of message.toolCalls) { - content.push({ - type: "tool_use", - id: call.id, - name: call.name, - input: call.arguments, - }); - } - messages.push({ role: "assistant", content }); - continue; - } - - if (message.role === "system") continue; - messages.push({ role: message.role, content: message.content }); - } - - return messages; -} - -/** - * The SDK throws its own typed errors rather than going through assertOk, so - * they are translated here to match what every other provider reports. - */ -function translateSdkError(error: unknown): never { - const status = - typeof (error as { status?: unknown })?.status === "number" - ? (error as { status: number }).status - : undefined; - const detail = error instanceof Error ? error.message : String(error); - - if (status === 429) { - throw new AiProviderError( - `Anthropic rate limit reached. Wait a moment and try again, or check your plan's quota. (${detail})`, - 429, - ); - } - if (status === 401 || status === 403) { - throw new AiProviderError( - `Anthropic rejected the API key. Check that it is correct and still active. (${detail})`, - status, - ); - } - throw new AiProviderError( - status ? `Anthropic request failed (${status}): ${detail}` : detail, - status, - ); -} - -export const anthropicAdapter: ProviderAdapter = { - async *streamChat( - config: ProviderConfig, - request: ChatRequest, - ): AsyncIterable { - const client = createClient(config); - - const stream = client.messages.stream({ - model: request.model, - max_tokens: 16000, - system: request.system, - messages: toAnthropicMessages(request), - thinking: { type: "adaptive" }, - ...(request.tools.length - ? { - tools: request.tools.map((tool) => ({ - name: tool.name, - description: tool.description, - input_schema: tool.parameters as Anthropic.Tool.InputSchema, - })), - } - : {}), - ...(request.signal ? { signal: request.signal } : {}), - }); - - let final: Anthropic.Message; - try { - for await (const event of stream) { - if ( - event.type === "content_block_delta" && - event.delta.type === "text_delta" - ) { - yield { type: "text", text: event.delta.text }; - } - } - final = await stream.finalMessage(); - } catch (error) { - translateSdkError(error); - } - - for (const block of final.content) { - if (block.type === "tool_use") { - yield { - type: "tool_call", - call: { - id: block.id, - name: block.name, - arguments: (block.input ?? {}) as Record, - }, - }; - } - } - - yield { type: "done", stopReason: final.stop_reason ?? undefined }; - }, - - async listModels(): Promise { - return [...ANTHROPIC_MODELS]; - }, -}; diff --git a/plugins/ai/src/backend/providers/gemini.ts b/plugins/ai/src/backend/providers/gemini.ts deleted file mode 100644 index 6cd6faaed..000000000 --- a/plugins/ai/src/backend/providers/gemini.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { assertOk, readSseLines } from "./http.js"; -import type { - ChatChunk, - ChatRequest, - ProviderAdapter, - ProviderConfig, -} from "./types.js"; -import { AiProviderError } from "./types.js"; - -const GEMINI_DEFAULT_BASE = "https://generativelanguage.googleapis.com/v1beta"; - -function baseFor(config: ProviderConfig): string { - return config.baseUrl?.trim() || GEMINI_DEFAULT_BASE; -} - -/** - * Gemini has no tool role: a tool result is a user-side functionResponse part, - * and an assistant tool request is a model-side functionCall part. - */ -function toGeminiContents(request: ChatRequest): unknown[] { - const contents: unknown[] = []; - - for (const message of request.messages) { - if (message.role === "tool") { - contents.push({ - role: "user", - parts: [ - { - functionResponse: { - name: message.toolName ?? "tool", - response: { result: message.content }, - }, - }, - ], - }); - continue; - } - - if (message.role === "assistant" && message.toolCalls?.length) { - const parts: unknown[] = []; - if (message.content) parts.push({ text: message.content }); - for (const call of message.toolCalls) { - // thoughtSignature must be returned exactly as received or Gemini - // 2.5+ rejects the turn with a 400. - parts.push({ - functionCall: { name: call.name, args: call.arguments }, - ...(call.providerSignature - ? { thoughtSignature: call.providerSignature } - : {}), - }); - } - contents.push({ role: "model", parts }); - continue; - } - - if (message.role === "system") continue; - - contents.push({ - role: message.role === "assistant" ? "model" : "user", - parts: [{ text: message.content }], - }); - } - - return contents; -} - -/** - * Gemini rejects the JSON Schema keywords it does not implement, so the tool - * schemas are trimmed to the subset it accepts. - */ -function toGeminiSchema(schema: unknown): unknown { - if (!schema || typeof schema !== "object") return schema; - if (Array.isArray(schema)) return schema.map(toGeminiSchema); - - const source = schema as Record; - const output: Record = {}; - - for (const [key, value] of Object.entries(source)) { - if (key === "additionalProperties" || key === "$schema") continue; - if (key === "properties" && value && typeof value === "object") { - const properties: Record = {}; - for (const [name, child] of Object.entries( - value as Record, - )) { - properties[name] = toGeminiSchema(child); - } - output[key] = properties; - continue; - } - output[key] = toGeminiSchema(value); - } - - return output; -} - -export const geminiAdapter: ProviderAdapter = { - async *streamChat( - config: ProviderConfig, - request: ChatRequest, - ): AsyncIterable { - if (!config.apiKey) { - throw new AiProviderError("This provider needs an API key"); - } - - const url = `${baseFor(config).replace(/\/+$/, "")}/models/${encodeURIComponent(request.model)}:streamGenerateContent?alt=sse`; - - const response = await config.fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-goog-api-key": config.apiKey, - }, - signal: request.signal, - body: JSON.stringify({ - systemInstruction: { parts: [{ text: request.system }] }, - contents: toGeminiContents(request), - ...(request.tools.length - ? { - tools: [ - { - functionDeclarations: request.tools.map((tool) => ({ - name: tool.name, - description: tool.description, - parameters: toGeminiSchema(tool.parameters), - })), - }, - ], - } - : {}), - }), - }); - - await assertOk(response, "Gemini"); - - let index = 0; - let stopReason: string | undefined; - - for await (const data of readSseLines(response)) { - let frame: any; - try { - frame = JSON.parse(data); - } catch { - continue; - } - - const candidate = frame.candidates?.[0]; - if (!candidate) continue; - if (candidate.finishReason) stopReason = candidate.finishReason; - - for (const part of candidate.content?.parts ?? []) { - if (typeof part.text === "string" && part.text) { - yield { type: "text", text: part.text }; - } - if (part.functionCall?.name) { - yield { - type: "tool_call", - call: { - id: `call_${part.functionCall.name}_${index++}`, - name: part.functionCall.name, - arguments: (part.functionCall.args ?? {}) as Record< - string, - unknown - >, - // Carried so the next turn can echo it back; without it Gemini - // 400s as soon as a tool has been used once. - providerSignature: part.thoughtSignature, - }, - }; - } - } - } - - yield { type: "done", stopReason }; - }, - - async listModels(config: ProviderConfig): Promise { - if (!config.apiKey) return []; - - const response = await config.fetch( - `${baseFor(config).replace(/\/+$/, "")}/models`, - { method: "GET", headers: { "x-goog-api-key": config.apiKey } }, - ); - await assertOk(response, "Gemini"); - - const body: any = await response.json(); - return (body.models ?? []) - .map((entry: any) => String(entry.name ?? "").replace(/^models\//, "")) - .filter((name: string) => name.length > 0) - .sort(); - }, -}; diff --git a/plugins/ai/src/backend/providers/http.ts b/plugins/ai/src/backend/providers/http.ts deleted file mode 100644 index 067e64dce..000000000 --- a/plugins/ai/src/backend/providers/http.ts +++ /dev/null @@ -1,257 +0,0 @@ -import type { PluginFetch } from "@termix/plugin-sdk/backend"; -import { evaluateEgress, PRIVATE_DESTINATION_MESSAGE } from "../egress.js"; -import { AiProviderError, type ProviderFetch } from "./types.js"; - -/** A model can take a while to load before the first token. */ -const PROVIDER_TIMEOUT_MS = 10 * 60 * 1000; - -function toHeaderRecord( - headers: RequestInit["headers"] | undefined, -): Record { - const record: Record = {}; - new Headers(headers ?? {}).forEach((value, key) => { - record[key] = value; - }); - return record; -} - -/** - * Every outbound provider request goes through here so the egress rules cannot - * be bypassed by an adapter calling fetch directly. The actual request is - * ctx.fetch, which pins DNS and refuses private addresses unless the host is - * on the admin allowlist. - */ -export function createProviderFetch( - fetch: PluginFetch, - allowlist: string[], -): ProviderFetch { - return async (url, init) => { - const decision = evaluateEgress(url, allowlist); - if (!decision.allowed) { - throw new AiProviderError(decision.reason ?? "Destination not allowed"); - } - - if ( - init.body !== undefined && - init.body !== null && - typeof init.body !== "string" - ) { - throw new AiProviderError("Provider requests must send a text body"); - } - - try { - return await fetch(url, { - method: init.method ?? "GET", - headers: toHeaderRecord(init.headers), - body: (init.body as string | null | undefined) ?? undefined, - signal: init.signal ?? undefined, - timeoutMs: PROVIDER_TIMEOUT_MS, - allowPrivateHosts: allowlist, - }); - } catch (error) { - // A hostname that resolved to a private address is refused after DNS, - // which the check above cannot see coming. - const message = error instanceof Error ? error.message : ""; - if (/private destinations/i.test(message)) { - throw new AiProviderError(PRIVATE_DESTINATION_MESSAGE); - } - throw error; - } - }; -} - -const NETWORK_MESSAGES: Record = { - UND_ERR_BODY_TIMEOUT: - "The provider stopped sending data for too long and the reply was cut off. A slow or overloaded model can cause this.", - UND_ERR_SOCKET: - "The provider closed the connection before the reply finished.", - ECONNRESET: "The provider closed the connection before the reply finished.", - UND_ERR_HEADERS_TIMEOUT: "The provider did not answer in time.", - UND_ERR_CONNECT_TIMEOUT: - "Timed out connecting to the provider. Check the base URL.", - ETIMEDOUT: "Timed out connecting to the provider. Check the base URL.", - ECONNREFUSED: - "The provider refused the connection. Check the base URL and that the provider is running.", - ENOTFOUND: "The provider's host name could not be found. Check the base URL.", - EAI_AGAIN: "The provider's host name could not be found. Check the base URL.", - EHOSTUNREACH: "The provider's host is unreachable from this server.", - ENETUNREACH: "The provider's host is unreachable from this server.", -}; - -/** - * undici reports network failures as a bare "fetch failed" or "terminated" - * with the real reason on `cause`, which is useless to show as is. - */ -export function describeProviderError( - error: unknown, - fallback: string, -): string { - if (error instanceof AiProviderError) return error.message; - if (!(error instanceof Error)) { - return typeof error === "string" && error ? error : fallback; - } - - const cause = (error as { cause?: unknown }).cause as - (Error & { code?: string }) | undefined; - const code = cause?.code ?? (error as { code?: string }).code; - if (code && NETWORK_MESSAGES[code]) return NETWORK_MESSAGES[code]; - - const causeMessage = cause instanceof Error ? cause.message : ""; - if (/private destinations/i.test(causeMessage)) { - return PRIVATE_DESTINATION_MESSAGE; - } - if (code && /CERT|SELF_SIGNED|UNABLE_TO_VERIFY/i.test(code)) { - return `The provider's TLS certificate is not trusted (${code}).`; - } - - if (/^(fetch failed|terminated)$/i.test(error.message)) { - if (causeMessage) return `The provider request failed: ${causeMessage}`; - return error.message === "terminated" - ? NETWORK_MESSAGES.UND_ERR_SOCKET - : fallback; - } - return error.message || fallback; -} - -export function joinUrl(base: string, path: string): string { - return `${base.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`; -} - -/** - * Yields the data payload of each SSE frame. Providers differ in what they put - * inside, so parsing the JSON is left to the caller. - */ -export async function* readSseLines( - response: Response, -): AsyncGenerator { - const body = response.body; - if (!body) return; - - const reader = body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - buffer += decoder.decode(value, { stream: true }); - - let newlineIndex = buffer.indexOf("\n"); - while (newlineIndex !== -1) { - const line = buffer.slice(0, newlineIndex).trim(); - buffer = buffer.slice(newlineIndex + 1); - if (line.startsWith("data:")) { - yield line.slice(5).trim(); - } - newlineIndex = buffer.indexOf("\n"); - } - } - } finally { - reader.releaseLock(); - } -} - -/** Yields one parsed JSON object per line, for newline-delimited streams. */ -export async function* readJsonLines( - response: Response, -): AsyncGenerator { - const body = response.body; - if (!body) return; - - const reader = body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - - try { - while (true) { - const { done, value } = await reader.read(); - if (done) break; - buffer += decoder.decode(value, { stream: true }); - - let newlineIndex = buffer.indexOf("\n"); - while (newlineIndex !== -1) { - const line = buffer.slice(0, newlineIndex).trim(); - buffer = buffer.slice(newlineIndex + 1); - if (line) { - try { - yield JSON.parse(line); - } catch { - // A partial or malformed frame is skipped rather than failing the - // whole stream. - } - } - newlineIndex = buffer.indexOf("\n"); - } - } - } finally { - reader.releaseLock(); - } -} - -/** - * Digs the human-readable message out of an error body. - * - * Every provider nests it differently, and dumping the raw JSON produced - * something that got cut off mid-sentence. Falls back to a trimmed snippet - * when the shape is unfamiliar. - */ -function extractProviderMessage(body: string): string { - try { - const parsed = JSON.parse(body); - const message = - parsed?.error?.message ?? - parsed?.error?.["message"] ?? - parsed?.message ?? - (typeof parsed?.error === "string" ? parsed.error : null); - if (typeof message === "string" && message.trim()) { - return message.trim(); - } - } catch { - // Not JSON; fall through to the snippet. - } - - const trimmed = body.trim(); - if (!trimmed) return ""; - return trimmed.length > 300 ? `${trimmed.slice(0, 300)}...` : trimmed; -} - -export async function assertOk( - response: Response, - provider: string, -): Promise { - if (response.ok) return; - - let body = ""; - try { - body = await response.text(); - } catch { - body = ""; - } - - const detail = extractProviderMessage(body); - - // Rate limits and auth failures are the two the user can actually act on, - // so they say what to do instead of reading like an internal failure. - if (response.status === 429) { - throw new AiProviderError( - `${provider} rate limit reached. Wait a moment and try again, or check your plan's quota.${ - detail ? ` (${detail})` : "" - }`, - 429, - ); - } - if (response.status === 401 || response.status === 403) { - throw new AiProviderError( - `${provider} rejected the API key. Check that it is correct and still active.${ - detail ? ` (${detail})` : "" - }`, - response.status, - ); - } - - throw new AiProviderError( - `${provider} request failed (${response.status})${detail ? `: ${detail}` : ""}`, - response.status, - ); -} diff --git a/plugins/ai/src/backend/providers/ollama.ts b/plugins/ai/src/backend/providers/ollama.ts deleted file mode 100644 index 6deb4a5f0..000000000 --- a/plugins/ai/src/backend/providers/ollama.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { assertOk, joinUrl, readJsonLines } from "./http.js"; -import type { - ChatChunk, - ChatRequest, - ProviderAdapter, - ProviderConfig, -} from "./types.js"; - -export const OLLAMA_DEFAULT_BASE = "http://localhost:11434"; - -function baseFor(config: ProviderConfig): string { - return config.baseUrl?.trim() || OLLAMA_DEFAULT_BASE; -} - -function toOllamaMessages(request: ChatRequest): unknown[] { - const messages: unknown[] = [{ role: "system", content: request.system }]; - - for (const message of request.messages) { - if (message.role === "tool") { - messages.push({ - role: "tool", - content: message.content, - ...(message.toolName ? { tool_name: message.toolName } : {}), - }); - continue; - } - - if (message.role === "assistant" && message.toolCalls?.length) { - messages.push({ - role: "assistant", - content: message.content, - tool_calls: message.toolCalls.map((call) => ({ - function: { name: call.name, arguments: call.arguments }, - })), - }); - continue; - } - - messages.push({ role: message.role, content: message.content }); - } - - return messages; -} - -export const ollamaAdapter: ProviderAdapter = { - async *streamChat( - config: ProviderConfig, - request: ChatRequest, - ): AsyncIterable { - const response = await config.fetch(joinUrl(baseFor(config), "api/chat"), { - method: "POST", - headers: { "Content-Type": "application/json" }, - signal: request.signal, - body: JSON.stringify({ - model: request.model, - messages: toOllamaMessages(request), - stream: true, - ...(request.tools.length - ? { - tools: request.tools.map((tool) => ({ - type: "function", - function: { - name: tool.name, - description: tool.description, - parameters: tool.parameters, - }, - })), - } - : {}), - }), - }); - - await assertOk(response, "Ollama"); - - let index = 0; - let stopReason: string | undefined; - - // Ollama streams newline-delimited JSON rather than SSE. - for await (const frame of readJsonLines(response)) { - const payload = frame as any; - - if ( - typeof payload.message?.content === "string" && - payload.message.content - ) { - yield { type: "text", text: payload.message.content }; - } - - for (const call of payload.message?.tool_calls ?? []) { - const name = call.function?.name; - if (!name) continue; - const rawArgs = call.function?.arguments; - // Ollama sends an object, but some builds send a JSON string. - let args: Record = {}; - if (rawArgs && typeof rawArgs === "object") { - args = rawArgs as Record; - } else if (typeof rawArgs === "string" && rawArgs.trim()) { - try { - args = JSON.parse(rawArgs); - } catch { - args = {}; - } - } - yield { - type: "tool_call", - call: { id: `call_${name}_${index++}`, name, arguments: args }, - }; - } - - if (payload.done) { - stopReason = payload.done_reason ?? "stop"; - break; - } - } - - yield { type: "done", stopReason }; - }, - - async listModels(config: ProviderConfig): Promise { - const response = await config.fetch(joinUrl(baseFor(config), "api/tags"), { - method: "GET", - }); - await assertOk(response, "Ollama"); - - const body: any = await response.json(); - return (body.models ?? []) - .map((entry: any) => entry.name) - .filter((name: unknown): name is string => typeof name === "string") - .sort(); - }, -}; diff --git a/plugins/ai/src/backend/providers/openai.ts b/plugins/ai/src/backend/providers/openai.ts deleted file mode 100644 index 3c5f7fb56..000000000 --- a/plugins/ai/src/backend/providers/openai.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { assertOk, joinUrl, readSseLines } from "./http.js"; -import type { - ChatChunk, - ChatRequest, - ProviderAdapter, - ProviderConfig, - ToolCall, -} from "./types.js"; -import { AiProviderError } from "./types.js"; - -const OPENAI_DEFAULT_BASE = "https://api.openai.com/v1"; - -function baseFor(config: ProviderConfig): string { - if (config.baseUrl?.trim()) return config.baseUrl.trim(); - if (config.providerType === "openai") return OPENAI_DEFAULT_BASE; - throw new AiProviderError("This provider needs a base URL"); -} - -function toOpenAiMessages(request: ChatRequest): unknown[] { - const messages: unknown[] = [{ role: "system", content: request.system }]; - - for (const message of request.messages) { - if (message.role === "tool") { - messages.push({ - role: "tool", - tool_call_id: message.toolCallId, - content: message.content, - }); - continue; - } - - if (message.role === "assistant" && message.toolCalls?.length) { - messages.push({ - role: "assistant", - content: message.content || null, - tool_calls: message.toolCalls.map((call) => ({ - id: call.id, - type: "function", - function: { - name: call.name, - arguments: JSON.stringify(call.arguments), - }, - })), - }); - continue; - } - - messages.push({ role: message.role, content: message.content }); - } - - return messages; -} - -/** - * Tool call arguments arrive as JSON fragments spread across many deltas, so - * they are accumulated per index and only parsed once the stream ends. - */ -interface PartialCall { - id: string; - name: string; - args: string; -} - -export const openAiAdapter: ProviderAdapter = { - async *streamChat( - config: ProviderConfig, - request: ChatRequest, - ): AsyncIterable { - const url = joinUrl(baseFor(config), "chat/completions"); - - const headers: Record = { - "Content-Type": "application/json", - }; - if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`; - - const response = await config.fetch(url, { - method: "POST", - headers, - signal: request.signal, - body: JSON.stringify({ - model: request.model, - messages: toOpenAiMessages(request), - stream: true, - ...(request.tools.length - ? { - tools: request.tools.map((tool) => ({ - type: "function", - function: { - name: tool.name, - description: tool.description, - parameters: tool.parameters, - }, - })), - } - : {}), - }), - }); - - await assertOk(response, "OpenAI"); - - const partial = new Map(); - let stopReason: string | undefined; - - for await (const data of readSseLines(response)) { - if (data === "[DONE]") break; - - let frame: any; - try { - frame = JSON.parse(data); - } catch { - continue; - } - - const choice = frame.choices?.[0]; - if (!choice) continue; - - if (choice.finish_reason) stopReason = choice.finish_reason; - - const delta = choice.delta; - if (!delta) continue; - - if (typeof delta.content === "string" && delta.content) { - yield { type: "text", text: delta.content }; - } - - for (const call of delta.tool_calls ?? []) { - const index = call.index ?? 0; - const existing = partial.get(index) ?? { id: "", name: "", args: "" }; - if (call.id) existing.id = call.id; - if (call.function?.name) existing.name = call.function.name; - if (call.function?.arguments) existing.args += call.function.arguments; - partial.set(index, existing); - } - } - - for (const call of partial.values()) { - if (!call.name) continue; - yield { type: "tool_call", call: finalizeCall(call) }; - } - - yield { type: "done", stopReason }; - }, - - async listModels(config: ProviderConfig): Promise { - const headers: Record = {}; - if (config.apiKey) headers.Authorization = `Bearer ${config.apiKey}`; - - const response = await config.fetch(joinUrl(baseFor(config), "models"), { - method: "GET", - headers, - }); - await assertOk(response, "OpenAI"); - - const body: any = await response.json(); - return (body.data ?? []) - .map((entry: any) => entry.id) - .filter((id: unknown): id is string => typeof id === "string") - .sort(); - }, -}; - -export function finalizeCall(call: PartialCall): ToolCall { - let args: Record = {}; - if (call.args.trim()) { - try { - const parsed = JSON.parse(call.args); - if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) { - args = parsed as Record; - } - } catch { - // A model that emitted malformed arguments gets an empty object; the - // tool's own schema validation reports the problem back to it. - args = {}; - } - } - return { - id: - call.id || `call_${call.name}_${Math.random().toString(36).slice(2, 10)}`, - name: call.name, - arguments: args, - }; -} diff --git a/plugins/ai/src/backend/providers/registry.ts b/plugins/ai/src/backend/providers/registry.ts deleted file mode 100644 index 0bbc98752..000000000 --- a/plugins/ai/src/backend/providers/registry.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { anthropicAdapter } from "./anthropic.js"; -import { geminiAdapter } from "./gemini.js"; -import { ollamaAdapter } from "./ollama.js"; -import { openAiAdapter } from "./openai.js"; -import { - AiProviderError, - type AiProviderType, - type ProviderAdapter, -} from "./types.js"; - -/** - * openai_compatible reuses the OpenAI adapter: OpenRouter, Groq, Mistral, - * DeepSeek, together.ai, LM Studio and vLLM all speak the same wire format, - * they differ only in base URL. - */ -const ADAPTERS: Record = { - ollama: ollamaAdapter, - anthropic: anthropicAdapter, - openai: openAiAdapter, - gemini: geminiAdapter, - openai_compatible: openAiAdapter, -}; - -export function getAdapter(providerType: string): ProviderAdapter { - const adapter = ADAPTERS[providerType as AiProviderType]; - if (!adapter) { - throw new AiProviderError(`Unknown provider type: ${providerType}`); - } - return adapter; -} - -/** Provider types that cannot work without a base URL. */ -export const REQUIRES_BASE_URL: AiProviderType[] = [ - "ollama", - "openai_compatible", -]; - -/** Provider types that cannot work without an API key. */ -export const REQUIRES_API_KEY: AiProviderType[] = [ - "anthropic", - "openai", - "gemini", -]; - -/** - * Shown when a provider's model list cannot be fetched: no key entered yet, - * the endpoint is unreachable, or the vendor has no list endpoint. Users can - * always type a model id the list does not contain, so this is a starting - * point rather than a restriction. - */ -export const FALLBACK_MODELS: Record = { - ollama: [ - "llama3.2", - "llama3.1", - "qwen2.5-coder", - "mistral", - "phi4", - "gemma2", - ], - anthropic: ["claude-opus-5", "claude-sonnet-5", "claude-haiku-4-5"], - openai: ["gpt-5", "gpt-5-mini", "o4-mini", "gpt-4.1"], - gemini: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"], - openai_compatible: [], -}; diff --git a/plugins/ai/src/backend/providers/types.ts b/plugins/ai/src/backend/providers/types.ts deleted file mode 100644 index 2b75bfb28..000000000 --- a/plugins/ai/src/backend/providers/types.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * The shape every provider adapter normalizes to. Adding a provider means - * translating its wire format into these events; nothing downstream (the - * engine, the tool dispatcher, the SSE route) knows which vendor is in use. - */ - -export type AiProviderType = - "ollama" | "anthropic" | "openai" | "gemini" | "openai_compatible"; - -export const AI_PROVIDER_TYPES: AiProviderType[] = [ - "ollama", - "anthropic", - "openai", - "gemini", - "openai_compatible", -]; - -export function isAiProviderType(value: unknown): value is AiProviderType { - return ( - typeof value === "string" && (AI_PROVIDER_TYPES as string[]).includes(value) - ); -} - -export interface ChatMessage { - role: "system" | "user" | "assistant" | "tool"; - content: string; - /** Set on assistant turns that requested tools. */ - toolCalls?: ToolCall[]; - /** Set on tool turns, matching the id of the call being answered. */ - toolCallId?: string; - toolName?: string; -} - -export interface ToolCall { - id: string; - name: string; - arguments: Record; - /** - * Opaque provider state that has to be echoed back verbatim on the next - * turn. Gemini 2.5+ rejects a follow-up whose functionCall parts have lost - * their thoughtSignature, so this rides along rather than being dropped. - */ - providerSignature?: string; -} - -export interface ToolDefinition { - name: string; - description: string; - parameters: Record; -} - -export interface ChatRequest { - model: string; - system: string; - messages: ChatMessage[]; - tools: ToolDefinition[]; - signal?: AbortSignal; -} - -export type ChatChunk = - | { type: "text"; text: string } - | { type: "tool_call"; call: ToolCall } - | { type: "done"; stopReason?: string } - | { type: "error"; message: string }; - -/** - * How an adapter reaches its provider. Always the egress-checked fetch from - * http.ts, never the global one. - */ -export type ProviderFetch = ( - url: string, - init: RequestInit, -) => Promise; - -export interface ProviderConfig { - providerType: AiProviderType; - baseUrl?: string | null; - apiKey?: string | null; - fetch: ProviderFetch; -} - -export interface ProviderAdapter { - /** Streams a single assistant turn. Tool execution happens in the engine. */ - streamChat( - config: ProviderConfig, - request: ChatRequest, - ): AsyncIterable; - /** Model ids to offer in the picker, best effort. */ - listModels(config: ProviderConfig): Promise; -} - -export class AiProviderError extends Error { - constructor( - message: string, - readonly status?: number, - ) { - super(message); - this.name = "AiProviderError"; - } -} diff --git a/plugins/ai/src/backend/redaction.ts b/plugins/ai/src/backend/redaction.ts deleted file mode 100644 index 487618228..000000000 --- a/plugins/ai/src/backend/redaction.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Defense in depth for anything about to leave the server. - * - * Read tools already select explicit field allowlists rather than spreading - * rows, so nothing secret should reach here. This exists because "should" is - * not a guarantee: a future tool that forgets to project its fields would - * otherwise ship credentials to a third-party model provider. - */ - -const SECRET_KEY_PATTERN = - /^(password|passwd|pass|secret|token|api_?key|apikey|private_?key|privatekey|key_?password|keypassword|passphrase|client_?secret|authorization|auth_?token|access_?token|refresh_?token|totp_?secret|backup_?codes|session_?token|cookie|credential|ssh_?cert|data_?key|dek)$/i; - -/** Substring markers for keys that are not exact matches but still sensitive. */ -const SECRET_KEY_SUBSTRINGS = [ - "password", - "secret", - "privatekey", - "private_key", - "apikey", - "api_key", - "passphrase", -]; - -const VALUE_PATTERNS: Array<{ pattern: RegExp; label: string }> = [ - { - pattern: - /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*PRIVATE KEY-----/g, - label: "[redacted private key]", - }, - { pattern: /\bsk-[A-Za-z0-9_-]{16,}\b/g, label: "[redacted api key]" }, - { pattern: /\bsk-ant-[A-Za-z0-9_-]{16,}\b/g, label: "[redacted api key]" }, - { pattern: /\bghp_[A-Za-z0-9]{20,}\b/g, label: "[redacted token]" }, - { pattern: /\btmx_[A-Za-z0-9_-]{16,}\b/g, label: "[redacted token]" }, - { - pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]+\b/g, - label: "[redacted token]", - }, - { - pattern: /\bBearer\s+[A-Za-z0-9._-]{16,}/gi, - label: "Bearer [redacted]", - }, -]; - -export const REDACTED = "[redacted]"; - -function isSecretKey(key: string): boolean { - if (SECRET_KEY_PATTERN.test(key)) return true; - const lower = key.toLowerCase(); - return SECRET_KEY_SUBSTRINGS.some((marker) => lower.includes(marker)); -} - -export function redactString(value: string): string { - let output = value; - for (const { pattern, label } of VALUE_PATTERNS) { - output = output.replace(pattern, label); - } - return output; -} - -/** - * Recursively drops secret-named fields and masks secret-shaped values. - * Depth is bounded so a cyclic or pathological structure cannot hang the loop. - */ -export function redact(value: unknown, depth = 0): unknown { - if (depth > 12) return REDACTED; - - if (typeof value === "string") return redactString(value); - if (value === null || typeof value !== "object") return value; - - if (Array.isArray(value)) { - return value.map((entry) => redact(entry, depth + 1)); - } - - const output: Record = {}; - for (const [key, entry] of Object.entries(value as Record)) { - if (isSecretKey(key)) { - // Preserve the shape so the model can still reason about presence, - // without ever seeing the value. - output[key] = entry === null || entry === undefined ? null : REDACTED; - continue; - } - output[key] = redact(entry, depth + 1); - } - return output; -} - -/** Convenience wrapper for serializing a tool result. */ -export function redactToJson(value: unknown): string { - return JSON.stringify(redact(value)); -} diff --git a/plugins/ai/src/backend/repository.ts b/plugins/ai/src/backend/repository.ts deleted file mode 100644 index da82d1de7..000000000 --- a/plugins/ai/src/backend/repository.ts +++ /dev/null @@ -1,421 +0,0 @@ -import { and, desc, eq } from "drizzle-orm"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { - conversations as conversationsDef, - messages as messagesDef, - proposals as proposalsDef, - providers as providersDef, -} from "./tables.js"; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -// Tables come from ctx.db.define, which the SDK hands back untyped. -type Table = any; -type Drizzle = any; -/* eslint-enable @typescript-eslint/no-explicit-any */ - -export interface AiProviderRecord { - id: number; - userId: string; - providerType: string; - label: string; - baseUrl: string | null; - apiKeyPrefix: string | null; - defaultModel: string | null; - enabled: boolean; - createdAt: string; - updatedAt: string; -} - -/** A provider with its key, for the one path that makes outbound calls. */ -export interface AiProviderWithSecret extends AiProviderRecord { - apiKey: string | null; -} - -export interface AiConversationRecord { - id: number; - userId: string; - title: string | null; - providerId: number | null; - model: string | null; - createdAt: string; - updatedAt: string; -} - -export interface AiMessageRecord { - id: number; - conversationId: number; - role: string; - content: string; - toolCalls: string | null; - createdAt: string; -} - -export interface AiProposalRecord { - id: number; - conversationId: number; - userId: string; - kind: string; - summary: string | null; - payload: string; - status: string; - appliedAt: string | null; - resultSummary: string | null; - createdAt: string; -} - -export interface AiProviderInput { - providerType: string; - label: string; - baseUrl?: string | null; - apiKey?: string | null; - defaultModel?: string | null; - enabled?: boolean; -} - -/** Where a provider's key lives in ctx.secrets. */ -export function providerSecretKey(providerId: number): string { - return `provider:${providerId}`; -} - -/** - * Keeps the first few characters so the UI can tell two keys apart without - * ever receiving the key itself. - */ -export function apiKeyPrefix(apiKey: string | null | undefined): string | null { - if (!apiKey) return null; - return apiKey.slice(0, 6); -} - -const now = () => new Date().toISOString(); - -export type AiRepository = Awaited>; - -/** - * The assistant's rows. Every method takes the owning user and scopes to - * them. Keys go through ctx.secrets, which is per acting user, so key reads - * and writes run inside the request (or ctx.asUser) of that same user. - */ -export async function createAiRepository( - ctx: Pick, -) { - const { db, secrets } = ctx; - const providers: Table = await db.define(providersDef); - const conversations: Table = await db.define(conversationsDef); - const messages: Table = await db.define(messagesDef); - const proposals: Table = await db.define(proposalsDef); - - const client = () => db.client(); - - /** Inserts one row and returns its id. MySQL has no RETURNING. */ - async function insertId( - table: Table, - values: Record, - ): Promise { - const drizzle = await client(); - if (db.dialect === "mysql") { - const result = await drizzle.insert(table).values(values); - const header = Array.isArray(result) ? result[0] : result; - return Number(header?.insertId); - } - const rows = await drizzle - .insert(table) - .values(values) - .returning({ id: table.id }); - return rows[0].id as number; - } - - async function first(table: Table, where: unknown): Promise { - const rows = await ( - await client() - ) - .select() - .from(table) - .where(where) - .limit(1); - return (rows[0] as T) ?? null; - } - - const repo = { - // --- providers --- - - /** Never includes key material; callers get the masked prefix only. */ - async listProviders(userId: string): Promise { - return (await client()) - .select() - .from(providers) - .where(eq(providers.userId, userId)) - .orderBy(providers.id); - }, - - async findProvider( - id: number, - userId: string, - ): Promise { - return first( - providers, - and(eq(providers.id, id), eq(providers.userId, userId)), - ); - }, - - /** - * The one path that returns usable key material, right before an - * outbound request. Must run as `userId`. - */ - async findProviderWithSecret( - id: number, - userId: string, - ): Promise { - const row = await repo.findProvider(id, userId); - if (!row) return null; - return { ...row, apiKey: await secrets.get(providerSecretKey(id)) }; - }, - - async createProvider( - userId: string, - input: AiProviderInput, - ): Promise { - const stamp = now(); - const id = await insertId(providers, { - userId, - providerType: input.providerType, - label: input.label, - baseUrl: input.baseUrl ?? null, - apiKeyPrefix: apiKeyPrefix(input.apiKey), - defaultModel: input.defaultModel ?? null, - enabled: input.enabled ?? true, - createdAt: stamp, - updatedAt: stamp, - }); - if (input.apiKey) { - await secrets.set(providerSecretKey(id), input.apiKey); - } - await db.persist(); - const created = await repo.findProvider(id, userId); - if (!created) throw new Error("Provider could not be read back"); - return created; - }, - - async updateProvider( - id: number, - userId: string, - input: Partial, - ): Promise { - const existing = await repo.findProvider(id, userId); - if (!existing) return null; - - const updates: Record = { updatedAt: now() }; - if (input.providerType !== undefined) - updates.providerType = input.providerType; - if (input.label !== undefined) updates.label = input.label; - if (input.baseUrl !== undefined) updates.baseUrl = input.baseUrl; - if (input.defaultModel !== undefined) - updates.defaultModel = input.defaultModel; - if (input.enabled !== undefined) updates.enabled = input.enabled; - - // An empty string clears the key; undefined leaves it untouched. - if (input.apiKey !== undefined) { - updates.apiKeyPrefix = apiKeyPrefix(input.apiKey); - await secrets.set(providerSecretKey(id), input.apiKey || null); - } - - await ( - await client() - ) - .update(providers) - .set(updates) - .where(and(eq(providers.id, id), eq(providers.userId, userId))); - await db.persist(); - return repo.findProvider(id, userId); - }, - - async deleteProvider(id: number, userId: string): Promise { - const existing = await repo.findProvider(id, userId); - if (!existing) return false; - await ( - await client() - ) - .delete(providers) - .where(and(eq(providers.id, id), eq(providers.userId, userId))); - await secrets.delete(providerSecretKey(id)); - await db.persist(); - return true; - }, - - // --- conversations --- - - async listConversations( - userId: string, - limit = 50, - ): Promise { - return (await client()) - .select() - .from(conversations) - .where(eq(conversations.userId, userId)) - .orderBy(desc(conversations.updatedAt)) - .limit(limit); - }, - - async findConversation( - id: number, - userId: string, - ): Promise { - return first( - conversations, - and(eq(conversations.id, id), eq(conversations.userId, userId)), - ); - }, - - async createConversation(input: { - userId: string; - title?: string | null; - providerId?: number | null; - model?: string | null; - }): Promise { - const stamp = now(); - const id = await insertId(conversations, { - userId: input.userId, - title: input.title ?? null, - providerId: input.providerId ?? null, - model: input.model ?? null, - createdAt: stamp, - updatedAt: stamp, - }); - await db.persist(); - const created = await repo.findConversation(id, input.userId); - if (!created) throw new Error("Conversation could not be read back"); - return created; - }, - - async touchConversation(id: number): Promise { - await ( - await client() - ) - .update(conversations) - .set({ updatedAt: now() }) - .where(eq(conversations.id, id)); - await db.persist(); - }, - - async deleteConversation(id: number, userId: string): Promise { - const existing = await repo.findConversation(id, userId); - if (!existing) return false; - // Deleted explicitly rather than trusting the cascade, which a - // connection without foreign keys on would skip. - const drizzle = await client(); - await drizzle.delete(messages).where(eq(messages.conversationId, id)); - await drizzle.delete(proposals).where(eq(proposals.conversationId, id)); - await drizzle - .delete(conversations) - .where(and(eq(conversations.id, id), eq(conversations.userId, userId))); - await db.persist(); - return true; - }, - - // --- messages --- - - async listMessages(conversationId: number): Promise { - return (await client()) - .select() - .from(messages) - .where(eq(messages.conversationId, conversationId)) - .orderBy(messages.id); - }, - - async appendMessage(input: { - conversationId: number; - role: string; - content: string; - toolCalls?: string | null; - }): Promise { - await insertId(messages, { - conversationId: input.conversationId, - role: input.role, - content: input.content, - toolCalls: input.toolCalls ?? null, - createdAt: now(), - }); - await db.persist(); - }, - - // --- proposals --- - - async listProposals( - userId: string, - conversationId: number, - ): Promise { - return (await client()) - .select() - .from(proposals) - .where( - and( - eq(proposals.userId, userId), - eq(proposals.conversationId, conversationId), - ), - ) - .orderBy(desc(proposals.id)); - }, - - async findProposal( - id: number, - userId: string, - ): Promise { - return first( - proposals, - and(eq(proposals.id, id), eq(proposals.userId, userId)), - ); - }, - - async createProposal(input: { - conversationId: number; - userId: string; - kind: string; - summary?: string | null; - payload: string; - }): Promise { - const id = await insertId(proposals, { - conversationId: input.conversationId, - userId: input.userId, - kind: input.kind, - summary: input.summary ?? null, - payload: input.payload, - status: "pending", - createdAt: now(), - }); - await db.persist(); - const created = await repo.findProposal(id, input.userId); - if (!created) throw new Error("Proposal could not be read back"); - return created; - }, - - /** Only moves a pending proposal. False when it was not pending. */ - async setProposalStatus( - id: number, - userId: string, - status: "applied" | "rejected" | "expired", - resultSummary?: string | null, - ): Promise { - const existing = await repo.findProposal(id, userId); - if (!existing || existing.status !== "pending") return false; - await ( - await client() - ) - .update(proposals) - .set({ - status, - appliedAt: status === "applied" ? now() : null, - resultSummary: resultSummary ?? null, - }) - .where( - and( - eq(proposals.id, id), - eq(proposals.userId, userId), - eq(proposals.status, "pending"), - ), - ); - await db.persist(); - return true; - }, - }; - - return repo; -} diff --git a/plugins/ai/src/backend/routes.ts b/plugins/ai/src/backend/routes.ts deleted file mode 100644 index a38067f9e..000000000 --- a/plugins/ai/src/backend/routes.ts +++ /dev/null @@ -1,1106 +0,0 @@ -import type { Request, Response, Router } from "express"; -import type { PluginContext } from "@termix/plugin-sdk/backend"; -import { buildSystemPrompt } from "./context.js"; -import { runAgent } from "./engine.js"; -import { getErrorMessage } from "./errors.js"; -import { - createAiGate, - isAiGloballyEnabled, - readPrivateAllowlist, - resolveAiAccess, -} from "./gating.js"; -import { toChatHistory, toStoredMessage } from "./history.js"; -import { - createProviderFetch, - describeProviderError, -} from "./providers/http.js"; -import { - FALLBACK_MODELS, - getAdapter, - REQUIRES_API_KEY, - REQUIRES_BASE_URL, -} from "./providers/registry.js"; -import type { AiProviderType, ProviderConfig } from "./providers/types.js"; -import { isAiProviderType } from "./providers/types.js"; -import type { AiRepository } from "./repository.js"; -import { serviceAvailable } from "./services.js"; -import { availableTools } from "./tools/catalog.js"; -import { applyProposal } from "./tools/executor.js"; -import type { ToolDeps } from "./tools/types.js"; - -function parseId(raw: unknown): number | null { - const id = typeof raw === "string" ? parseInt(raw, 10) : Number(raw); - return Number.isInteger(id) && id > 0 ? id : null; -} - -function actor(ctx: PluginContext): string { - // Core's plugin router authenticates every request and runs it as that user. - return ctx.currentActor() as string; -} - -/** - * Mounts every assistant route on the plugin's router, served at - * /plugin-api/ai. Everything but /status sits behind the AI gate: the admin - * switch and the user's own opt-in. - */ -export function registerAiRoutes( - router: Router, - repository: AiRepository, - ctx: PluginContext, -): void { - const aiGate = createAiGate(ctx.settings, () => ctx.currentActor()); - const gate = aiGate as never; - const toolDeps: ToolDeps = { - hosts: ctx.hosts, - services: ctx.services, - notify: ctx.notify, - rbac: ctx.rbac, - ssh: ctx.ssh, - audit: ctx.audit, - }; - - const logError = (message: string, error: unknown) => - ctx.log.error( - message, - error instanceof Error ? error : new Error(String(error)), - ); - - const audit = (entry: Parameters[0]) => - ctx.audit.record(entry).catch(() => undefined); - - /** The provider config for an outbound call, with the egress-checked fetch. */ - async function providerConfig(input: { - providerType: string; - baseUrl: string | null; - apiKey: string | null; - }): Promise { - const allowlist = await readPrivateAllowlist(ctx.settings); - return { - providerType: input.providerType as ProviderConfig["providerType"], - baseUrl: input.baseUrl, - apiKey: input.apiKey, - fetch: createProviderFetch(ctx.fetch, allowlist), - }; - } - - /** - * @openapi - * /plugin-api/ai/status: - * get: - * summary: Whether the AI assistant is available to this user - * description: > - * Deliberately not behind the AI gate: the frontend calls this to decide - * whether to render any AI surface at all, and needs a plain answer rather - * than a 403 when the feature is off. - * tags: - * - AI - * responses: - * 200: - * description: The effective enablement state. - */ - router.get("/status", async (_req: Request, res: Response) => { - const userId = actor(ctx); - try { - const [globallyEnabled, access] = await Promise.all([ - isAiGloballyEnabled(ctx.settings), - resolveAiAccess(ctx.settings, userId), - ]); - res.json({ - globallyEnabled, - enabled: access.enabled, - allowReadOnlyCommands: access.allowReadOnlyCommands, - }); - } catch (err) { - logError("Failed to read AI status", err); - res.status(500).json({ error: "Failed to read AI status" }); - } - }); - - /** - * @openapi - * /plugin-api/ai/opt-in: - * put: - * summary: Turn the assistant on or off for the current user - * description: > - * The same value as the user setting "enabled", for the onboarding - * step and the panel, which set it outside the settings form. Refused - * while the assistant is off for the whole server. - * tags: - * - AI - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * enabled: - * type: boolean - * responses: - * 200: - * description: The user's new choice. - * 400: - * description: enabled is not a boolean. - * 403: - * description: The assistant is off for the whole server. - */ - router.put( - "/opt-in", - ctx.rbac.require("use") as never, - async (req: Request, res: Response) => { - const { enabled } = req.body ?? {}; - if (typeof enabled !== "boolean") { - return res.status(400).json({ error: "enabled must be a boolean" }); - } - try { - // Turning it off is always allowed; turning it on needs the switch. - if (enabled && !(await isAiGloballyEnabled(ctx.settings))) { - return res - .status(403) - .json({ error: "The AI assistant is not enabled" }); - } - await ctx.settings.setUser(actor(ctx), "enabled", enabled); - res.json({ enabled }); - } catch (err) { - logError("Failed to save the AI opt-in", err); - res.status(500).json({ error: "Failed to save the setting" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/providers: - * get: - * summary: List the user's configured AI providers - * tags: - * - AI - * responses: - * 200: - * description: Providers, with API keys masked. - * 403: - * description: The AI assistant is not enabled. - */ - router.get( - "/providers", - ctx.rbac.require("use") as never, - gate, - async (_req: Request, res: Response) => { - try { - const providers = await repository.listProviders(actor(ctx)); - res.json({ providers }); - } catch (err) { - logError("Failed to list AI providers", err); - res.status(500).json({ error: "Failed to list providers" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/providers: - * post: - * summary: Add an AI provider - * tags: - * - AI - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * providerType: - * type: string - * label: - * type: string - * baseUrl: - * type: string - * apiKey: - * type: string - * defaultModel: - * type: string - * responses: - * 201: - * description: Provider created. - * 400: - * description: Invalid request body. - */ - router.post( - "/providers", - ctx.rbac.require("manage_providers") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const { providerType, label, baseUrl, apiKey, defaultModel } = - req.body ?? {}; - - if (!isAiProviderType(providerType)) { - return res.status(400).json({ error: "Unknown provider type" }); - } - if (typeof label !== "string" || !label.trim()) { - return res.status(400).json({ error: "label is required" }); - } - if (REQUIRES_BASE_URL.includes(providerType) && !baseUrl?.trim()) { - return res - .status(400) - .json({ error: "This provider needs a base URL" }); - } - if (REQUIRES_API_KEY.includes(providerType) && !apiKey?.trim()) { - return res - .status(400) - .json({ error: "This provider needs an API key" }); - } - - try { - const created = await repository.createProvider(userId, { - providerType, - label: label.trim(), - baseUrl: typeof baseUrl === "string" ? baseUrl.trim() : null, - apiKey: typeof apiKey === "string" ? apiKey.trim() : null, - defaultModel: - typeof defaultModel === "string" ? defaultModel.trim() : null, - }); - - await audit({ - action: "create_ai_provider", - resourceType: "ai_provider", - resourceId: String(created.id), - resourceName: created.label, - success: true, - }); - - res.status(201).json({ provider: created }); - } catch (err) { - logError("Failed to create AI provider", err); - res.status(500).json({ error: "Failed to create provider" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/providers/{id}: - * patch: - * summary: Update an AI provider - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Provider updated. - * 404: - * description: Provider not found. - */ - router.patch( - "/providers/:id", - ctx.rbac.require("manage_providers") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid provider id" }); - - const body = (req.body ?? {}) as Record; - if ( - body.providerType !== undefined && - !isAiProviderType(body.providerType) - ) { - return res.status(400).json({ error: "Unknown provider type" }); - } - - const text = (value: unknown) => - typeof value === "string" ? value.trim() : undefined; - - try { - const updated = await repository.updateProvider(id, userId, { - providerType: body.providerType as string | undefined, - label: text(body.label) || undefined, - baseUrl: - body.baseUrl === null ? null : (text(body.baseUrl) ?? undefined), - apiKey: body.apiKey === null ? "" : text(body.apiKey), - defaultModel: - body.defaultModel === null - ? null - : (text(body.defaultModel) ?? undefined), - enabled: typeof body.enabled === "boolean" ? body.enabled : undefined, - }); - if (!updated) - return res.status(404).json({ error: "Provider not found" }); - - await audit({ - action: "update_ai_provider", - resourceType: "ai_provider", - resourceId: String(id), - resourceName: updated.label, - success: true, - }); - - res.json({ provider: updated }); - } catch (err) { - logError("Failed to update AI provider", err); - res.status(500).json({ error: "Failed to update provider" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/providers/{id}: - * delete: - * summary: Delete an AI provider - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Provider deleted. - * 404: - * description: Provider not found. - */ - router.delete( - "/providers/:id", - ctx.rbac.require("manage_providers") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid provider id" }); - - try { - const deleted = await repository.deleteProvider(id, userId); - if (!deleted) - return res.status(404).json({ error: "Provider not found" }); - - await audit({ - action: "delete_ai_provider", - resourceType: "ai_provider", - resourceId: String(id), - success: true, - }); - - res.json({ success: true }); - } catch (err) { - logError("Failed to delete AI provider", err); - res.status(500).json({ error: "Failed to delete provider" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/probe-models: - * post: - * summary: List models for a provider that has not been saved yet - * description: > - * Lets the add-provider form fill its model picker before the provider - * exists, so nobody has to go and look up model names by hand. - * tags: - * - AI - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * providerType: - * type: string - * baseUrl: - * type: string - * apiKey: - * type: string - * providerId: - * type: integer - * responses: - * 200: - * description: Model ids, possibly a curated fallback list. - * 400: - * description: Unknown provider type. - */ - router.post( - "/probe-models", - ctx.rbac.require("manage_providers") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const { providerType, baseUrl, apiKey, providerId } = req.body ?? {}; - - if (!isAiProviderType(providerType)) { - return res.status(400).json({ error: "Unknown provider type" }); - } - - try { - // Editing an existing provider sends no key, so fall back to the - // stored one rather than making the user retype it. - let resolvedKey = - typeof apiKey === "string" && apiKey.trim() ? apiKey.trim() : null; - const storedId = parseId(providerId); - if (!resolvedKey && storedId) { - const stored = await repository.findProviderWithSecret( - storedId, - userId, - ); - resolvedKey = stored?.apiKey ?? null; - } - - const models = await getAdapter(providerType).listModels( - await providerConfig({ - providerType, - baseUrl: typeof baseUrl === "string" ? baseUrl.trim() : null, - apiKey: resolvedKey, - }), - ); - - res.json({ models, source: "live" }); - } catch (err) { - const warning = describeProviderError( - err, - "Could not reach the provider", - ); - ctx.log.warn(`Model detection failed: ${warning}`); - // A provider that cannot be reached yet still gets a usable list, so - // the form is never a blank text box the user has to guess into. - const fallback = FALLBACK_MODELS[providerType as AiProviderType] ?? []; - res.json({ - models: fallback, - source: fallback.length ? "fallback" : "none", - warning, - }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/providers/{id}/models: - * get: - * summary: List models available from a provider - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Model ids. - * 502: - * description: The provider could not be reached. - */ - router.get( - "/providers/:id/models", - ctx.rbac.require("use") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid provider id" }); - - try { - const provider = await repository.findProviderWithSecret(id, userId); - if (!provider) - return res.status(404).json({ error: "Provider not found" }); - - const models = await getAdapter(provider.providerType).listModels( - await providerConfig(provider), - ); - res.json({ models }); - } catch (err) { - // The message can carry the allowlist hint, which the user needs. - const message = describeProviderError( - err, - "Could not reach the provider", - ); - ctx.log.warn(`Failed to list provider models: ${message}`); - res.status(502).json({ error: message }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/conversations: - * get: - * summary: List the user's AI conversations - * tags: - * - AI - * responses: - * 200: - * description: Conversations, newest first. - */ - router.get( - "/conversations", - ctx.rbac.require("use") as never, - gate, - async (_req: Request, res: Response) => { - try { - const conversations = await repository.listConversations(actor(ctx)); - res.json({ conversations }); - } catch (err) { - logError("Failed to list AI conversations", err); - res.status(500).json({ error: "Failed to list conversations" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/conversations/{id}: - * get: - * summary: Get one conversation with its messages and proposals - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: The conversation. - * 404: - * description: Conversation not found. - */ - router.get( - "/conversations/:id", - ctx.rbac.require("use") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) - return res.status(400).json({ error: "Invalid conversation id" }); - - try { - const conversation = await repository.findConversation(id, userId); - if (!conversation) { - return res.status(404).json({ error: "Conversation not found" }); - } - - const [messages, proposals] = await Promise.all([ - repository.listMessages(id), - repository.listProposals(userId, id), - ]); - - res.json({ conversation, messages, proposals }); - } catch (err) { - logError("Failed to load AI conversation", err); - res.status(500).json({ error: "Failed to load conversation" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/conversations/{id}: - * delete: - * summary: Delete a conversation - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Conversation deleted. - */ - router.delete( - "/conversations/:id", - ctx.rbac.require("use") as never, - gate, - async (req: Request, res: Response) => { - const id = parseId(req.params.id); - if (!id) - return res.status(400).json({ error: "Invalid conversation id" }); - - try { - const deleted = await repository.deleteConversation(id, actor(ctx)); - if (!deleted) { - return res.status(404).json({ error: "Conversation not found" }); - } - res.json({ success: true }); - } catch (err) { - logError("Failed to delete AI conversation", err); - res.status(500).json({ error: "Failed to delete conversation" }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/chat/stream: - * post: - * summary: Send a message and stream the assistant's reply - * description: > - * Server-sent events. Emits conversation, token, tool_call, - * tool_result, proposal, done and error frames. - * tags: - * - AI - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * conversationId: - * type: integer - * providerId: - * type: integer - * model: - * type: string - * message: - * type: string - * activeTab: - * type: string - * responses: - * 200: - * description: An event stream. - * 400: - * description: Invalid request body. - */ - router.post( - "/chat/stream", - ctx.rbac.require("use") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const { conversationId, providerId, model, message, activeTab } = - req.body ?? {}; - - if (typeof message !== "string" || !message.trim()) { - return res.status(400).json({ error: "message is required" }); - } - - const resolvedProviderId = parseId(providerId); - if (!resolvedProviderId) { - return res.status(400).json({ error: "providerId is required" }); - } - - try { - const provider = await repository.findProviderWithSecret( - resolvedProviderId, - userId, - ); - if (!provider) { - return res.status(404).json({ error: "Provider not found" }); - } - - const chosenModel = - (typeof model === "string" && model.trim()) || - provider.defaultModel || - ""; - if (!chosenModel) { - return res.status(400).json({ error: "No model selected" }); - } - - // Resolve or create the conversation before the stream opens, so a - // failure here is still a normal JSON error the client can render. - let conversation = conversationId - ? await repository.findConversation(Number(conversationId), userId) - : null; - if (!conversation) { - conversation = await repository.createConversation({ - userId, - title: message.trim().slice(0, 60), - providerId: resolvedProviderId, - model: chosenModel, - }); - } - - const history = await repository.listMessages(conversation.id); - await repository.appendMessage({ - conversationId: conversation.id, - role: "user", - content: message.trim(), - }); - - const access = ( - req as Request & { aiAccess?: { allowReadOnlyCommands: boolean } } - ).aiAccess ?? { allowReadOnlyCommands: false }; - const hosts = await ctx.hosts.list(); - const config = await providerConfig(provider); - const tools = availableTools( - (service) => serviceAvailable(ctx.services, service), - { allowReadOnlyCommands: access.allowReadOnlyCommands }, - ); - - // no-transform keeps core's compression from buffering the stream, - // and X-Accel-Buffering does the same for nginx. - res.writeHead(200, { - "Content-Type": "text/event-stream", - "Cache-Control": "no-store, no-transform", - Connection: "keep-alive", - "X-Accel-Buffering": "no", - }); - res.flushHeaders?.(); - - const heartbeat = setInterval(() => { - try { - res.write(": keepalive\n\n"); - } catch { - clearInterval(heartbeat); - } - }, 30000); - - // req's own "close" already fired once express read the body, so the - // response is what tells us the client went away. - const abort = new AbortController(); - res.on("close", () => { - clearInterval(heartbeat); - if (!res.writableFinished) abort.abort(); - }); - - const send = (event: unknown) => { - if (res.writableEnded || res.destroyed) return; - res.write(`data: ${JSON.stringify(event)}\n\n`); - }; - - send({ type: "conversation", conversationId: conversation.id }); - - const chatHistory = toChatHistory(history); - chatHistory.push({ role: "user", content: message.trim() }); - - try { - for await (const event of runAgent({ - config, - model: chosenModel, - system: buildSystemPrompt({ - hostCount: hosts.length, - activeTab: typeof activeTab === "string" ? activeTab : null, - allowReadOnlyCommands: access.allowReadOnlyCommands, - }), - history: chatHistory, - context: { - userId, - conversationId: conversation.id, - allowReadOnlyCommands: access.allowReadOnlyCommands, - deps: toolDeps, - }, - tools, - signal: abort.signal, - })) { - if (event.type === "message") { - // Every step is kept, tool results included, so the next message - // replays the run verbatim. Gemini rejects a turn whose - // functionCall parts lost their thoughtSignature. - await repository.appendMessage({ - conversationId: conversation.id, - ...toStoredMessage(event.message), - }); - continue; - } - - if (event.type === "proposal") { - const stored = await repository.createProposal({ - conversationId: conversation.id, - userId, - kind: event.draft.kind, - summary: event.draft.summary, - payload: JSON.stringify(event.draft.payload), - }); - - await audit({ - action: "ai_proposal_created", - resourceType: "ai_proposal", - resourceId: String(stored.id), - resourceName: event.draft.kind, - success: true, - }); - - send({ type: "proposal", proposal: stored }); - continue; - } - - send(event); - } - } finally { - clearInterval(heartbeat); - } - await repository.touchConversation(conversation.id); - - send({ type: "done" }); - res.end(); - } catch (err) { - logError("AI chat stream failed", err); - if (res.writableEnded || res.destroyed) return; - if (res.headersSent) { - res.write( - `data: ${JSON.stringify({ type: "error", message: "The assistant stopped unexpectedly" })}\n\n`, - ); - res.end(); - } else { - res.status(500).json({ error: "Failed to start the assistant" }); - } - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/proposals/{id}/apply: - * post: - * summary: Apply a pending proposal - * description: > - * Re-validates the stored payload and applies it as the approving user, - * through the same core APIs and plugin services a manual action uses. - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Proposal applied. - * 400: - * description: The proposal could not be applied. - * 404: - * description: Proposal not found. - */ - router.post( - "/proposals/:id/apply", - ctx.rbac.require("apply_proposals") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid proposal id" }); - - try { - const stored = await repository.findProposal(id, userId); - if (!stored) - return res.status(404).json({ error: "Proposal not found" }); - if (stored.status !== "pending") { - return res - .status(400) - .json({ error: `This proposal was already ${stored.status}` }); - } - - let payload: Record; - try { - payload = JSON.parse(stored.payload) as Record; - } catch { - return res - .status(400) - .json({ error: "The proposal payload is invalid" }); - } - - const result = await applyProposal(stored.kind, payload, toolDeps); - await repository.setProposalStatus( - id, - userId, - "applied", - result.summary, - ); - - await audit({ - action: "ai_proposal_applied", - resourceType: "ai_proposal", - resourceId: String(id), - resourceName: stored.kind, - success: true, - }); - - res.json({ success: result.ok, summary: result.summary }); - } catch (err) { - const message = getErrorMessage(err, "Failed to apply the proposal"); - logError("Failed to apply AI proposal", err); - - await audit({ - action: "ai_proposal_applied", - resourceType: "ai_proposal", - resourceId: String(id), - success: false, - errorMessage: message, - }); - - res.status(400).json({ error: message }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/proposals/{id}/mark-run-in-terminal: - * post: - * summary: Record that a run_command proposal was executed in an open terminal - * description: > - * For the terminal-docked assistant only. The command is typed into the - * user's already-open SSH session client-side, not re-run over a pooled - * connection here; this just marks the proposal applied with the output - * the client captured, so the card and the /apply path stay in sync - * without running the command twice. - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * requestBody: - * required: true - * content: - * application/json: - * schema: - * type: object - * properties: - * hostId: - * type: integer - * summary: - * type: string - * responses: - * 200: - * description: Proposal marked applied. - * 400: - * description: The proposal is not a run_command proposal, or the hostId does not match. - * 404: - * description: Proposal not found. - */ - router.post( - "/proposals/:id/mark-run-in-terminal", - ctx.rbac.require("apply_proposals") as never, - gate, - async (req: Request, res: Response) => { - const userId = actor(ctx); - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid proposal id" }); - - const { hostId, summary } = req.body ?? {}; - const resolvedHostId = parseId(hostId); - if (!resolvedHostId) { - return res.status(400).json({ error: "hostId is required" }); - } - - try { - const stored = await repository.findProposal(id, userId); - if (!stored) - return res.status(404).json({ error: "Proposal not found" }); - if (stored.kind !== "propose_run_command") { - return res.status(400).json({ - error: "Only run_command proposals can be marked this way", - }); - } - if (stored.status !== "pending") { - return res - .status(400) - .json({ error: `This proposal was already ${stored.status}` }); - } - - let payload: Record; - try { - payload = JSON.parse(stored.payload) as Record; - } catch { - return res - .status(400) - .json({ error: "The proposal payload is invalid" }); - } - if (parseId(payload.hostId) !== resolvedHostId) { - return res - .status(400) - .json({ error: "This proposal is for a different host" }); - } - - const resultSummary = - typeof summary === "string" && summary.trim() - ? summary.trim().slice(0, 2000) - : "Run in the open terminal session"; - await repository.setProposalStatus( - id, - userId, - "applied", - resultSummary, - ); - - await audit({ - action: "ai_proposal_applied", - resourceType: "ai_proposal", - resourceId: String(id), - resourceName: stored.kind, - success: true, - }); - - res.json({ success: true, summary: resultSummary }); - } catch (err) { - logError("Failed to mark AI proposal applied in terminal", err); - res.status(400).json({ - error: getErrorMessage(err, "Failed to mark the proposal applied"), - }); - } - }, - ); - - /** - * @openapi - * /plugin-api/ai/proposals/{id}/reject: - * post: - * summary: Reject a pending proposal - * tags: - * - AI - * parameters: - * - in: path - * name: id - * required: true - * schema: - * type: integer - * responses: - * 200: - * description: Proposal rejected. - * 404: - * description: Proposal not found. - */ - router.post( - "/proposals/:id/reject", - ctx.rbac.require("use") as never, - gate, - async (req: Request, res: Response) => { - const id = parseId(req.params.id); - if (!id) return res.status(400).json({ error: "Invalid proposal id" }); - - try { - const updated = await repository.setProposalStatus( - id, - actor(ctx), - "rejected", - ); - if (!updated) { - return res - .status(404) - .json({ error: "Proposal not found or already resolved" }); - } - - await audit({ - action: "ai_proposal_rejected", - resourceType: "ai_proposal", - resourceId: String(id), - success: true, - }); - - res.json({ success: true }); - } catch (err) { - logError("Failed to reject AI proposal", err); - res.status(500).json({ error: "Failed to reject the proposal" }); - } - }, - ); -} diff --git a/plugins/ai/src/backend/services.ts b/plugins/ai/src/backend/services.ts deleted file mode 100644 index bbf4abb11..000000000 --- a/plugins/ai/src/backend/services.ts +++ /dev/null @@ -1,163 +0,0 @@ -import type { PluginServices } from "@termix/plugin-sdk/backend"; - -/** - * Other plugins the assistant reads and changes through, each optional. A - * service call runs as the acting user and the provider checks that user's - * permissions, so nothing here takes a user id. - */ - -export const SERVICE = { - snippets: "snippets.access", - fleets: "fleets.access", - automations: "automations.access", - workspaces: "workspaces.saved", - topology: "network-topology.graph", - history: "terminal.history", - homepage: "homepage.items", -} as const; - -export type ServiceName = (typeof SERVICE)[keyof typeof SERVICE]; - -/** Whether a compatible provider is running right now. */ -export function serviceAvailable( - services: Pick, - service: string, -): boolean { - try { - return services.providers(service).length > 0; - } catch { - return false; - } -} - -export interface SnippetSummary { - id: number; - name: string; - content: string; - description: string | null; - isNote: boolean; - folder: string | null; -} - -export interface SnippetsAccess { - list: () => Promise; - get: (id: number) => Promise<{ - id: number; - name: string; - content: string; - isNote: boolean; - } | null>; - create: (input: { - name: string; - content: string; - description?: string | null; - folder?: string | null; - }) => Promise<{ id: number; name: string }>; - update: ( - id: number, - changes: { - name?: string; - content?: string; - description?: string | null; - folder?: string | null; - }, - ) => Promise; - remove: (id: number) => Promise; -} - -export interface FleetsAccess { - list: () => Promise< - Array<{ id: number; name: string; color: string | null }> - >; - create: (input: { - name: string; - description?: string | null; - }) => Promise<{ id: number; name: string }>; - addMember: (fleetId: number, hostId: number) => Promise; -} - -export interface AutomationsAccess { - list: () => Promise< - Array<{ - id: number; - name: string; - description: string | null; - enabled: boolean; - triggerKind: string | null; - lastRunAt: string | null; - lastRunStatus: string | null; - missingPlugins: string[]; - }> - >; - get: (id: number) => Promise<{ - id: number; - name: string; - enabled: boolean; - definition: unknown; - } | null>; - create: (input: { - name: string; - description?: string | null; - definition: unknown; - enabled?: boolean; - }) => Promise<{ id: number; name: string }>; -} - -export interface SavedWorkspaces { - list: () => Promise>; -} - -export interface NetworkTopologyGraph { - get: () => Promise; -} - -export interface TerminalHistory { - list: ( - hostId: number, - limit?: number, - ) => Promise>; -} - -/** homepage.items v1, which the homepage plugin provides from B19. */ -export interface HomepageItems { - list: () => Promise< - Array<{ id: number; typeId: string; title: string | null }> - >; -} - -interface ServiceTypes { - "snippets.access": SnippetsAccess; - "fleets.access": FleetsAccess; - "automations.access": AutomationsAccess; - "workspaces.saved": SavedWorkspaces; - "network-topology.graph": NetworkTopologyGraph; - "terminal.history": TerminalHistory; - "homepage.items": HomepageItems; -} - -/** - * The service handle. Throws when the provider is missing or the user may - * not use it; a proposal apply wants that error, a read tool catches it. - */ -export function requireService( - services: Pick, - service: S, -): ServiceTypes[S] { - if (!serviceAvailable(services, service)) { - throw new Error(`The ${service.split(".")[0]} plugin is not available`); - } - return services.get(service); -} - -/** A read through a service, or null when it is missing or refused. */ -export async function readService( - services: Pick, - service: S, - read: (handle: ServiceTypes[S]) => Promise, -): Promise { - try { - return await read(requireService(services, service)); - } catch { - return null; - } -} diff --git a/plugins/ai/src/backend/sync.ts b/plugins/ai/src/backend/sync.ts deleted file mode 100644 index 17af496fc..000000000 --- a/plugins/ai/src/backend/sync.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { PluginContext, SyncRow } from "@termix/plugin-sdk/backend"; -import { - apiKeyPrefix, - providerSecretKey, - type AiRepository, -} from "./repository.js"; -import { providers } from "./tables.js"; - -function keyFromWire(row: SyncRow): string | null { - if (typeof row.apiKey === "string") return row.apiKey || null; - if (row.apiKey === null) return null; - throw new Error("Invalid synced provider key"); -} - -export async function registerProviderSync( - ctx: PluginContext, - repo: AiRepository, -) { - ctx.sync.registerEntity({ - type: "aiProviders", - table: await ctx.db.define(providers), - permissions: { - create: "ai.manage_providers", - update: "ai.manage_providers", - delete: "ai.manage_providers", - }, - serialize: async (row) => { - const apiKey = await ctx.asUser(String(row.userId), () => - ctx.secrets.get(providerSecretKey(Number(row.id))), - ); - return { ...row, apiKey, apiKeyPrefix: apiKeyPrefix(apiKey) }; - }, - deserialize: async (row) => { - const apiKey = keyFromWire(row); - const { apiKey: _key, ...fields } = row; - return { ...fields, apiKeyPrefix: apiKeyPrefix(apiKey) }; - }, - afterWrite: async ({ id, userId, wire }) => { - if (id === null) throw new Error("Synced provider has no local id"); - await ctx.asUser(userId, () => - ctx.secrets.set(providerSecretKey(id), keyFromWire(wire)), - ); - }, - remove: async (row, userId) => { - await ctx.asUser(userId, () => - repo.deleteProvider(Number(row.id), userId), - ); - }, - }); -} diff --git a/plugins/ai/src/backend/tables.ts b/plugins/ai/src/backend/tables.ts deleted file mode 100644 index 6a7a79247..000000000 --- a/plugins/ai/src/backend/tables.ts +++ /dev/null @@ -1,222 +0,0 @@ -import { - adoptLegacyTable, - boolean, - defineTable, - id, - integer, - refUser, - text, - timestamp, - varchar, -} from "@termix/plugin-sdk/db"; - -/* - * Adopted from core's ai_* tables, so column and index names are the legacy - * ones and the rename carries every row across. - * - * Messages and proposals point at their conversation with a plain integer - * here, since the SDK only references users and ssh_data. The adoption - * migrations keep that foreign key by hand, so deleting a conversation still - * cascades. - * - * Provider API keys are not in these tables: they live in ctx.secrets under - * "provider:". A 2.8 database still has an api_key column, which the - * boot migration empties after moving the key. - */ - -export const providers = adoptLegacyTable( - "ai_providers", - defineTable( - "providers", - { - id: id(), - syncId: varchar().unique(), - userId: refUser(), - // ollama | anthropic | openai | gemini | openai_compatible - providerType: text().notNull(), - label: varchar().notNull(), - // Required for ollama and openai_compatible, optional elsewhere. - baseUrl: text(), - // First few characters, kept in the clear so the UI can tell keys apart. - apiKeyPrefix: text(), - defaultModel: text(), - enabled: boolean().notNull().default(true), - createdAt: timestamp().notNull().defaultNow(), - updatedAt: timestamp().notNull().defaultNow(), - }, - { - uniques: [ - { name: "idx_ai_providers_user_label", columns: ["userId", "label"] }, - ], - }, - ), -); - -export const conversations = adoptLegacyTable( - "ai_conversations", - defineTable( - "conversations", - { - id: id(), - userId: refUser(), - title: text(), - providerId: integer(), - model: text(), - createdAt: timestamp().notNull().defaultNow(), - updatedAt: timestamp().notNull().defaultNow(), - }, - { - indexes: [ - { - name: "idx_ai_conversations_user", - columns: ["userId", "updatedAt"], - }, - ], - }, - ), -); - -export const messages = adoptLegacyTable( - "ai_messages", - defineTable( - "messages", - { - id: id(), - conversationId: integer().notNull(), - // user | assistant | tool - role: text().notNull(), - content: text().notNull().default(""), - // Serialized tool calls for this turn. - toolCalls: text(), - createdAt: timestamp().notNull().defaultNow(), - }, - { - indexes: [ - { - name: "idx_ai_messages_conversation", - columns: ["conversationId", "createdAt"], - }, - ], - }, - ), -); - -/** - * A change the assistant wants to make. Nothing here has been applied: the - * payload is re-validated at apply time and only then dispatched. - */ -export const proposals = adoptLegacyTable( - "ai_proposals", - defineTable( - "proposals", - { - id: id(), - conversationId: integer().notNull(), - userId: refUser(), - // The propose_* tool name that produced this. - kind: varchar().notNull(), - summary: text(), - payload: text().notNull().default("{}"), - // pending | applied | rejected | expired - status: varchar(32).notNull().default("pending"), - appliedAt: text(), - resultSummary: text(), - createdAt: timestamp().notNull().defaultNow(), - }, - { - indexes: [ - { name: "idx_ai_proposals_user", columns: ["userId", "status"] }, - { - name: "idx_ai_proposals_conversation", - columns: ["conversationId"], - }, - ], - }, - ), -); - -/* - * Coding agent sessions. Events and queued prompts are rows of their own, so a - * running agent appends instead of rewriting its session and no single value - * grows with the conversation. Text is TEXT on MySQL (64 KB), which is why - * prompts, drafts and event text are capped in UTF-8 bytes. - */ -export const agentSessions = defineTable( - "agent_sessions", - { - id: id(), - sessionId: varchar(36).notNull().unique(), - userId: refUser(), - hostId: integer().notNull(), - providerId: integer().notNull(), - agent: varchar(32).notNull(), - model: text().notNull(), - cwd: text().notNull(), - executable: text().notNull(), - nativeId: text(), - title: text(), - archived: boolean().notNull().default(false), - queuePaused: boolean().notNull().default(false), - draft: text(), - // At most 100 small records. - attachments: text().notNull().default("[]"), - updatedAt: text().notNull(), - }, - { - indexes: [{ name: "idx_ai_agent_sessions_user", columns: ["userId"] }], - }, -); - -export const agentEvents = defineTable( - "agent_events", - { - id: id(), - sessionId: varchar(36).notNull(), - userId: refUser(), - seq: integer().notNull(), - // text | tool | permission | status | error | user | state - kind: varchar(16).notNull(), - text: text().notNull(), - requestId: text(), - choices: text(), - }, - { - uniques: [ - { - name: "idx_ai_agent_events_session_seq", - columns: ["sessionId", "seq"], - }, - ], - }, -); - -export const agentQueue = defineTable( - "agent_queue", - { - id: id(), - sessionId: varchar(36).notNull(), - userId: refUser(), - promptId: varchar(36).notNull(), - position: integer().notNull(), - text: text().notNull(), - attachmentIds: text().notNull().default("[]"), - }, - { - indexes: [ - { - name: "idx_ai_agent_queue_session", - columns: ["sessionId", "position"], - }, - ], - }, -); - -export const tables = [ - providers, - conversations, - messages, - proposals, - agentSessions, - agentEvents, - agentQueue, -]; diff --git a/plugins/ai/src/backend/tools/catalog.ts b/plugins/ai/src/backend/tools/catalog.ts deleted file mode 100644 index 2f58d48fc..000000000 --- a/plugins/ai/src/backend/tools/catalog.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { proposeTools } from "./propose-tools.js"; -import { readTools } from "./read-tools.js"; -import type { AiTool, ToolDefinitionShape } from "./types.js"; - -/** - * The allowlist, and the security boundary for the whole feature. - * - * A model can only ever invoke what appears here. Route-level RBAC gates the - * HTTP API, but tools run in-process with the calling user's identity and never - * pass through a router, so "the assistant cannot reach credentials or user - * administration" is a property of this list, not of the permission system. - * - * Anything touching credentials, vaults, RBAC, users, identity, certificates, - * SSO or instance settings is deliberately absent and must stay absent. - */ -export const AI_TOOLS: AiTool[] = [...readTools, ...proposeTools]; - -const BY_NAME = new Map(AI_TOOLS.map((tool) => [tool.name, tool])); - -export function getTool(name: string): AiTool | undefined { - return BY_NAME.get(name); -} - -export function listToolNames(): string[] { - return AI_TOOLS.map((tool) => tool.name); -} - -/** - * Domains the assistant must never be able to reach, in any tool, ever. - * The catalog test asserts no tool name references these. - */ -export const FORBIDDEN_DOMAINS = [ - "credential", - "vault", - "rbac", - "role", - "permission", - "user_admin", - "password", - "totp", - "webauthn", - "passkey", - "api_key", - "session", - "oidc", - "sso", - "ldap", - "termix_id", - "identity", - "certificate", - "opkssh", - "stepca", - "step_ca", - "acme", - "ssl", - "audit", - "sync", - "settings", -]; - -/** - * The tools offered right now. A tool whose service has no running provider - * is left out, so the model never sees something it cannot use. - */ -export function availableTools( - isAvailable: (service: string) => boolean, - options: { allowReadOnlyCommands?: boolean } = {}, -): AiTool[] { - return AI_TOOLS.filter( - (tool) => - (!tool.service || isAvailable(tool.service)) && - (!tool.requiresReadOnlyCommands || - options.allowReadOnlyCommands === true), - ); -} - -/** Tool definitions in the shape the provider adapters expect. */ -export function toolDefinitions( - tools: AiTool[] = AI_TOOLS, -): ToolDefinitionShape[] { - return tools.map((tool) => ({ - name: tool.name, - description: tool.description, - parameters: tool.parameters, - })); -} diff --git a/plugins/ai/src/backend/tools/command-allowlist.ts b/plugins/ai/src/backend/tools/command-allowlist.ts deleted file mode 100644 index 16b15c68b..000000000 --- a/plugins/ai/src/backend/tools/command-allowlist.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Which commands the assistant may run without a per-command approval click, - * for users who opted into read-only execution. - * - * The check parses the command into arguments and matches the resolved binary - * against the allowlist. Substring matching would be trivially defeated - * ("df; rm -rf /" contains "df"), so any shell metacharacter that could chain, - * redirect or substitute a second command rejects the whole string outright. - */ - -export const READ_ONLY_COMMANDS = new Set([ - "df", - "du", - "free", - "uptime", - "uname", - "whoami", - "hostname", - "id", - "ps", - "top", - "systemctl", - "journalctl", - "docker", - "ip", - "ss", - "netstat", - "lsblk", - "cat", - "ls", - "stat", - "which", - "date", - "lscpu", - "vmstat", - "iostat", -]); - -/** Characters that let one command become several. */ -const SHELL_METACHARACTERS = /[;&|`$><\n\r\\]/; - -/** Subcommands that are safe for otherwise-powerful binaries. */ -const SUBCOMMAND_ALLOWLIST: Record> = { - systemctl: new Set([ - "status", - "list-units", - "list-unit-files", - "is-active", - "is-enabled", - "show", - ]), - docker: new Set([ - "ps", - "stats", - "images", - "logs", - "inspect", - "version", - "info", - ]), - ip: new Set(["a", "addr", "link", "route", "neigh"]), -}; - -/** Paths `cat` may read. Anything else could disclose credentials. */ -const CAT_ALLOWED_PREFIXES = ["/proc/", "/sys/", "/etc/os-release"]; - -export interface CommandCheck { - allowed: boolean; - reason?: string; -} - -export function isReadOnlyCommand(raw: string): CommandCheck { - const command = raw.trim(); - if (!command) return { allowed: false, reason: "Empty command" }; - - if (SHELL_METACHARACTERS.test(command)) { - return { - allowed: false, - reason: "Command chaining, redirection and substitution are not allowed", - }; - } - - const parts = command.split(/\s+/).filter(Boolean); - if (!parts.length) return { allowed: false, reason: "Empty command" }; - - // Reject env-prefixed and privilege-escalating forms outright. - const head = parts[0]; - if (head === "sudo" || head === "su" || head === "doas" || head === "env") { - return { allowed: false, reason: `${head} is not allowed` }; - } - - // A path like /usr/bin/df resolves to its basename. - const binary = head.includes("/") - ? head.slice(head.lastIndexOf("/") + 1) - : head; - - if (!READ_ONLY_COMMANDS.has(binary)) { - return { - allowed: false, - reason: `${binary} is not on the read-only allowlist`, - }; - } - - const allowedSubcommands = SUBCOMMAND_ALLOWLIST[binary]; - if (allowedSubcommands) { - const subcommand = parts.slice(1).find((part) => !part.startsWith("-")); - if (!subcommand || !allowedSubcommands.has(subcommand)) { - return { - allowed: false, - reason: - `${binary} ${subcommand ?? ""}`.trim() + - " is not on the read-only allowlist", - }; - } - } - - if (binary === "cat") { - const targets = parts.slice(1).filter((part) => !part.startsWith("-")); - if (!targets.length) { - return { allowed: false, reason: "cat needs a file path" }; - } - for (const target of targets) { - const permitted = CAT_ALLOWED_PREFIXES.some((prefix) => - prefix.endsWith("/") ? target.startsWith(prefix) : target === prefix, - ); - if (!permitted) { - return { - allowed: false, - reason: `cat is limited to ${CAT_ALLOWED_PREFIXES.join(", ")}`, - }; - } - } - } - - return { allowed: true }; -} diff --git a/plugins/ai/src/backend/tools/executor.ts b/plugins/ai/src/backend/tools/executor.ts deleted file mode 100644 index 17cce89a0..000000000 --- a/plugins/ai/src/backend/tools/executor.ts +++ /dev/null @@ -1,280 +0,0 @@ -import type { Client } from "ssh2"; -import type { PluginHostUpdateInput } from "@termix/plugin-sdk/backend"; -import { execCommand } from "@termix/plugin-sdk/host-commands"; -import { getTool } from "./catalog.js"; -import type { ToolDeps } from "./types.js"; -import { requireService, SERVICE } from "../services.js"; - -/** Approved commands get a bounded window rather than hanging the request. */ -const COMMAND_TIMEOUT_MS = 60_000; - -/** - * Applies an approved proposal. - * - * The stored payload is treated as untrusted input even though the server wrote - * it: the proposal could have sat in the table across a release, and defending - * the apply path rather than the create path means one place to get right. - * Everything goes through the same core APIs and plugin services a human - * action uses, and runs as the approving user: the apply request's actor. - */ - -export interface ApplyResult { - ok: boolean; - summary: string; -} - -function requireNumber(value: unknown, field: string): number { - const parsed = Number(value); - if (!Number.isInteger(parsed) || parsed <= 0) { - throw new Error(`${field} must be a positive integer`); - } - return parsed; -} - -function requireString(value: unknown, field: string): string { - if (typeof value !== "string" || !value.trim()) { - throw new Error(`${field} is required`); - } - return value.trim(); -} - -function optionalString(value: unknown): string | null { - if (typeof value !== "string") return null; - const trimmed = value.trim(); - return trimmed ? trimmed : null; -} - -/** Tags as ssh_data stores them, comma-separated. */ -function joinTags(value: unknown): string { - if (!Array.isArray(value)) return ""; - return value - .filter((tag): tag is string => typeof tag === "string") - .map((tag) => tag.trim().replace(/,/g, "")) - .filter(Boolean) - .join(","); -} - -async function requirePermission( - deps: ToolDeps, - permission: string, -): Promise { - if (!(await deps.rbac.has(permission))) { - throw new Error("You do not have permission to do this"); - } -} - -export async function applyProposal( - kind: string, - payload: Record, - deps: ToolDeps, -): Promise { - // A payload whose tool no longer exists is refused rather than guessed at. - if (!getTool(kind)) { - throw new Error(`Unknown proposal kind: ${kind}`); - } - - switch (kind) { - case "propose_create_host": { - await requirePermission(deps, "hosts.create"); - const created = await deps.hosts.create({ - name: requireString(payload.name, "name"), - ip: requireString(payload.ip, "ip"), - port: Number(payload.port) || 22, - username: optionalString(payload.username) ?? "", - // Credentials are attached by the user after approving. - authType: "password", - folder: optionalString(payload.folder), - tags: joinTags(payload.tags), - }); - return { ok: true, summary: `Created host ${created.name ?? ""}`.trim() }; - } - - case "propose_update_host": { - await requirePermission(deps, "hosts.edit"); - const hostId = requireNumber(payload.hostId, "hostId"); - const changes = (payload.changes ?? {}) as Record; - - const updates: PluginHostUpdateInput = {}; - if (changes.name !== undefined) - updates.name = requireString(changes.name, "name"); - if (changes.ip !== undefined) - updates.ip = requireString(changes.ip, "ip"); - if (changes.port !== undefined) updates.port = Number(changes.port); - if (changes.username !== undefined) - updates.username = optionalString(changes.username) ?? ""; - if (changes.folder !== undefined) - updates.folder = optionalString(changes.folder); - if (changes.tags !== undefined) updates.tags = joinTags(changes.tags); - - if (!Object.keys(updates).length) { - return { ok: false, summary: "Nothing to change" }; - } - - // update() refuses a host the approving user does not own. - const updated = await deps.hosts.update(hostId, updates); - if (!updated) throw new Error("Host not found"); - return { ok: true, summary: `Updated host ${hostId}` }; - } - - case "propose_delete_host": { - const hostId = requireNumber(payload.hostId, "hostId"); - // delete() checks hosts.delete and ownership itself. - const deleted = await deps.hosts.delete(hostId); - if (!deleted) throw new Error("Host not found"); - return { ok: true, summary: `Deleted host ${hostId}` }; - } - - case "propose_create_snippet": { - const created = await requireService( - deps.services, - SERVICE.snippets, - ).create({ - name: requireString(payload.name, "name"), - content: requireString(payload.content, "content"), - description: optionalString(payload.description), - folder: optionalString(payload.folder), - }); - return { - ok: true, - summary: `Created snippet ${created.name}`.trim(), - }; - } - - case "propose_update_snippet": { - const snippetId = requireNumber(payload.snippetId, "snippetId"); - const changes = (payload.changes ?? {}) as Record; - - const snippets = requireService(deps.services, SERVICE.snippets); - const existing = await snippets.get(snippetId); - if (!existing) throw new Error("Snippet not found"); - - const updates: Record = {}; - if (changes.name !== undefined) - updates.name = requireString(changes.name, "name"); - if (changes.content !== undefined) - updates.content = requireString(changes.content, "content"); - if (changes.description !== undefined) - updates.description = optionalString(changes.description); - if (changes.folder !== undefined) - updates.folder = optionalString(changes.folder); - - if (!Object.keys(updates).length) { - return { ok: false, summary: "Nothing to change" }; - } - - await snippets.update(snippetId, updates); - return { ok: true, summary: `Updated snippet ${snippetId}` }; - } - - case "propose_delete_snippet": { - const snippetId = requireNumber(payload.snippetId, "snippetId"); - const deleted = await requireService( - deps.services, - SERVICE.snippets, - ).remove(snippetId); - if (!deleted) throw new Error("Snippet not found"); - return { ok: true, summary: `Deleted snippet ${snippetId}` }; - } - - case "propose_create_fleet": { - const fleets = requireService(deps.services, SERVICE.fleets); - const fleet = await fleets.create({ - name: requireString(payload.name, "name"), - description: optionalString(payload.description), - }); - - const hostIds = Array.isArray(payload.hostIds) ? payload.hostIds : []; - let added = 0; - for (const raw of hostIds) { - const hostId = Number(raw); - if (!Number.isInteger(hostId) || hostId <= 0) continue; - // Only hosts the approving user owns can join their fleet. - const access = await deps.hosts.checkAccess(hostId, "view"); - if (!access.isOwner) continue; - await fleets.addMember(fleet.id, hostId); - added += 1; - } - - return { - ok: true, - summary: `Created fleet ${fleet.name} with ${added} host${added === 1 ? "" : "s"}`, - }; - } - - case "propose_create_automation": { - // The automations plugin runs the same validator its own route does, - // so an LLM-authored definition is held to exactly the human standard. - const created = await requireService( - deps.services, - SERVICE.automations, - ).create({ - name: requireString(payload.name, "name"), - description: optionalString(payload.description), - definition: payload.definition, - // Starts disabled: an automation the user has not watched run once - // should not begin firing against their servers on approval. - enabled: false, - }); - - return { - ok: true, - summary: `Created automation ${created.name} (disabled until you enable it)`, - }; - } - - case "propose_run_command": { - const hostId = requireNumber(payload.hostId, "hostId"); - const command = requireString(payload.command, "command"); - - // ctx.ssh resolves the host for the approving user, with the same - // connect-level access check, owner-key decryption and jump chain a - // terminal gets. - const result = await runCommandOnHost(deps, hostId, command); - if (result.error) { - throw new Error(result.error); - } - return { - ok: true, - summary: result.output?.slice(0, 2000) ?? "(no output)", - }; - } - - default: - throw new Error(`Proposal kind ${kind} cannot be applied automatically`); - } -} - -/** - * Runs one approved command over this plugin's SSH pool, mirroring how an - * automation run_command step executes. - */ -export async function runCommandOnHost( - deps: ToolDeps, - hostId: number, - command: string, -): Promise<{ output?: string; error?: string }> { - try { - const result = await deps.ssh.withConnection< - Awaited>, - Client - >(hostId, { pool: "ai", purpose: "fleet" }, (client) => - execCommand(client, command, COMMAND_TIMEOUT_MS), - ); - - const output = [result.stdout, result.stderr].filter(Boolean).join("\n"); - if (result.code === 0 || result.code === null) { - return { output: output || "(no output)" }; - } - return { error: `Exited with code ${result.code}: ${output}`.trim() }; - } catch (error) { - return { - error: error instanceof Error ? error.message : String(error), - }; - } -} - -/** - * Kinds the route handles itself rather than through applyProposal. - * Empty: everything the assistant can propose can now be applied. - */ -export const ROUTE_APPLIED_KINDS = new Set(); diff --git a/plugins/ai/src/backend/tools/propose-tools.ts b/plugins/ai/src/backend/tools/propose-tools.ts deleted file mode 100644 index 94952261f..000000000 --- a/plugins/ai/src/backend/tools/propose-tools.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { num, objectSchema, proposal, str, type AiTool } from "./types.js"; -import { SERVICE } from "../services.js"; - -/** - * Propose tools never mutate anything. They return a draft that is stored as a - * pending proposal and rendered as a card; the change happens only when the - * user approves it, and the payload is re-validated at that point. - */ - -export const proposeTools: AiTool[] = [ - { - name: "propose_create_host", - description: - "Propose adding a new SSH host. Never include credentials: the user attaches those themselves after approving.", - category: "propose", - parameters: objectSchema( - { - name: str("Display name for the host"), - ip: str("Hostname or IP address"), - port: num("SSH port (defaults to 22)"), - username: str("SSH username"), - folder: str("Folder to file the host under"), - tags: { - type: "array", - items: { type: "string" }, - description: "Tags to apply", - }, - }, - ["name", "ip"], - ), - handler: async (args) => - proposal("propose_create_host", `Add host ${String(args.name)}`, { - name: args.name, - ip: args.ip, - port: args.port ?? 22, - username: args.username ?? null, - folder: args.folder ?? null, - tags: args.tags ?? [], - }), - }, - { - name: "propose_update_host", - description: - "Propose changing an existing host's non-secret settings. Only include the fields that should change.", - category: "propose", - parameters: objectSchema( - { - hostId: num("The host id to update"), - name: str("New display name"), - ip: str("New hostname or IP address"), - port: num("New SSH port"), - username: str("New SSH username"), - folder: str("New folder"), - tags: { - type: "array", - items: { type: "string" }, - description: "Replacement tag list", - }, - }, - ["hostId"], - ), - handler: async (args) => { - const changes: Record = {}; - for (const field of [ - "name", - "ip", - "port", - "username", - "folder", - "tags", - ]) { - if (args[field] !== undefined) changes[field] = args[field]; - } - return proposal( - "propose_update_host", - `Update host ${String(args.hostId)}`, - { hostId: args.hostId, changes }, - ); - }, - }, - { - name: "propose_delete_host", - description: - "Propose removing a host. Use sparingly and explain why in the reason.", - category: "propose", - parameters: objectSchema( - { - hostId: num("The host id to delete"), - reason: str("Why this host should be removed"), - }, - ["hostId", "reason"], - ), - handler: async (args) => - proposal("propose_delete_host", `Delete host ${String(args.hostId)}`, { - hostId: args.hostId, - reason: args.reason, - }), - }, - { - name: "propose_create_snippet", - description: - "Propose saving a new command snippet the user can run against their hosts.", - category: "propose", - service: SERVICE.snippets, - parameters: objectSchema( - { - name: str("Snippet name"), - content: str("The command text"), - description: str("What the snippet does"), - folder: str("Folder to file it under"), - }, - ["name", "content"], - ), - handler: async (args) => - proposal( - "propose_create_snippet", - `Create snippet ${String(args.name)}`, - { - name: args.name, - content: args.content, - description: args.description ?? null, - folder: args.folder ?? null, - }, - ), - }, - { - name: "propose_update_snippet", - description: "Propose editing an existing snippet.", - category: "propose", - service: SERVICE.snippets, - parameters: objectSchema( - { - snippetId: num("The snippet id to update"), - name: str("New name"), - content: str("New command text"), - description: str("New description"), - folder: str("New folder"), - }, - ["snippetId"], - ), - handler: async (args) => { - const changes: Record = {}; - for (const field of ["name", "content", "description", "folder"]) { - if (args[field] !== undefined) changes[field] = args[field]; - } - return proposal( - "propose_update_snippet", - `Update snippet ${String(args.snippetId)}`, - { snippetId: args.snippetId, changes }, - ); - }, - }, - { - name: "propose_delete_snippet", - description: "Propose deleting a snippet.", - category: "propose", - service: SERVICE.snippets, - parameters: objectSchema( - { - snippetId: num("The snippet id to delete"), - reason: str("Why this snippet should be removed"), - }, - ["snippetId", "reason"], - ), - handler: async (args) => - proposal( - "propose_delete_snippet", - `Delete snippet ${String(args.snippetId)}`, - { snippetId: args.snippetId, reason: args.reason }, - ), - }, - { - name: "propose_create_automation", - description: - "Propose a new automation. The definition must be a valid AutomationDefinition object with a trigger and an ordered list of steps. It is validated server-side and previewed with a dry run before anything happens.", - category: "propose", - service: SERVICE.automations, - parameters: objectSchema( - { - name: str("Automation name"), - description: str("What the automation does"), - definition: { - type: "object", - description: - "The AutomationDefinition: { trigger: {...}, steps: [...] }", - }, - }, - ["name", "definition"], - ), - handler: async (args) => - proposal( - "propose_create_automation", - `Create automation ${String(args.name)}`, - { - name: args.name, - description: args.description ?? null, - definition: args.definition, - }, - ), - }, - { - name: "propose_create_fleet", - description: "Propose grouping hosts into a new fleet.", - category: "propose", - service: SERVICE.fleets, - parameters: objectSchema( - { - name: str("Fleet name"), - description: str("What this fleet is for"), - hostIds: { - type: "array", - items: { type: "number" }, - description: "Host ids to add as members", - }, - }, - ["name"], - ), - handler: async (args) => - proposal("propose_create_fleet", `Create fleet ${String(args.name)}`, { - name: args.name, - description: args.description ?? null, - hostIds: args.hostIds ?? [], - }), - }, - { - name: "propose_run_command", - description: - "Propose running a command on a host. The user reviews and approves it before it runs. Use this for anything that changes state; read-only diagnostics may run directly if the user has enabled that.", - category: "propose", - parameters: objectSchema( - { - hostId: num("The host id to run on"), - command: str("The exact command to run"), - explanation: str("What the command does and why it is needed"), - }, - ["hostId", "command", "explanation"], - ), - handler: async (args) => - proposal( - "propose_run_command", - // Deliberately short: the card renders the command in its own block, - // so repeating it here made every card twice as tall as it needed. - `Run a command on host ${String(args.hostId)}`, - { - hostId: args.hostId, - command: args.command, - explanation: args.explanation, - }, - ), - }, -]; diff --git a/plugins/ai/src/backend/tools/read-tools.ts b/plugins/ai/src/backend/tools/read-tools.ts deleted file mode 100644 index 9ce577795..000000000 --- a/plugins/ai/src/backend/tools/read-tools.ts +++ /dev/null @@ -1,313 +0,0 @@ -import type { PluginHostSummary } from "@termix/plugin-sdk/backend"; -import { num, objectSchema, str, type AiTool } from "./types.js"; -import { readService, SERVICE } from "../services.js"; -import { isReadOnlyCommand } from "./command-allowlist.js"; -import { runCommandOnHost } from "./executor.js"; - -/** Output handed back to the model is capped; a log can be huge. */ -const MAX_COMMAND_OUTPUT = 8000; - -/** - * Read tools project explicit fields rather than spreading rows. Redaction runs - * afterwards as a second line of defense, but the projection here is the - * primary control: a field that is never selected cannot leak. - */ - -function splitTags(tags: string | null): string[] { - if (!tags) return []; - return tags - .split(",") - .map((tag) => tag.trim()) - .filter(Boolean); -} - -function toHostSummary(host: PluginHostSummary) { - return { - id: host.id, - name: host.name ?? null, - ip: host.ip, - port: host.port, - username: host.username, - folder: host.folder ?? null, - tags: splitTags(host.tags), - authType: host.authType, - }; -} - -export const readTools: AiTool[] = [ - { - name: "run_readonly_command", - description: - "Run a read-only diagnostic command on a host and get its output, for example df -h, uptime, free -m or systemctl status nginx. Only commands on a fixed read-only allowlist run; anything else must be proposed with propose_run_command.", - category: "read", - requiresReadOnlyCommands: true, - parameters: objectSchema( - { - hostId: num("The host id, as returned by list_hosts"), - command: str("The command to run"), - }, - ["hostId", "command"], - ), - handler: async (args, context) => { - if (!context.allowReadOnlyCommands) { - return { error: "The user has not allowed read-only commands" }; - } - const hostId = Number(args.hostId); - const command = typeof args.command === "string" ? args.command : ""; - const check = isReadOnlyCommand(command); - if (!check.allowed) { - return { error: check.reason ?? "Command is not read-only" }; - } - const access = await context.deps.hosts.checkAccess(hostId, "connect"); - if (!access.hasAccess) return { error: "Host not found" }; - - const result = await runCommandOnHost(context.deps, hostId, command); - await context.deps.audit - .record({ - action: "ai_readonly_command", - resourceType: "host", - resourceId: String(hostId), - details: JSON.stringify({ command }), - success: !result.error, - errorMessage: result.error, - }) - .catch(() => undefined); - if (result.error) return { error: result.error }; - return { output: (result.output ?? "").slice(0, MAX_COMMAND_OUTPUT) }; - }, - }, - { - name: "list_hosts", - description: - "List the user's SSH hosts with their names, addresses, folders and tags. Never returns passwords or keys. Call this before proposing anything that references a host.", - category: "read", - parameters: objectSchema({}), - handler: async (_args, context) => { - const hosts = await context.deps.hosts.list(); - return { hosts: hosts.map(toHostSummary) }; - }, - }, - { - name: "get_host", - description: - "Get one host's non-secret configuration by id. Use it to inspect settings before proposing an update.", - category: "read", - parameters: objectSchema( - { hostId: num("The host id, as returned by list_hosts") }, - ["hostId"], - ), - handler: async (args, context) => { - const host = await context.deps.hosts.get(Number(args.hostId)); - if (!host) return { error: "Host not found" }; - return { host: toHostSummary(host) }; - }, - }, - { - name: "list_fleets", - description: - "List the user's fleets. Fleets group hosts for bulk operations and inventory.", - category: "read", - service: SERVICE.fleets, - parameters: objectSchema({}), - handler: async (_args, context) => { - const fleets = await readService( - context.deps.services, - SERVICE.fleets, - (fleetsAccess) => fleetsAccess.list(), - ); - if (fleets === null) return { unavailable: true, fleets: [] }; - return { - fleets: fleets.map((fleet) => ({ id: fleet.id, name: fleet.name })), - }; - }, - }, - { - name: "list_snippets", - description: - "List the user's saved command snippets, including their folder and the command text.", - category: "read", - service: SERVICE.snippets, - parameters: objectSchema({}), - handler: async (_args, context) => { - const snippets = await readService( - context.deps.services, - SERVICE.snippets, - (snippetsAccess) => snippetsAccess.list(), - ); - if (!snippets) return { unavailable: true, snippets: [] }; - return { - snippets: snippets.map((snippet) => ({ - id: snippet.id, - name: snippet.name, - content: snippet.content, - folder: snippet.folder ?? null, - description: snippet.description ?? null, - })), - }; - }, - }, - { - name: "list_automations", - description: - "List the user's automations with their trigger kind and enabled state. Read this before proposing a change to an existing automation.", - category: "read", - service: SERVICE.automations, - parameters: objectSchema({}), - handler: async (_args, context) => { - const automations = await readService( - context.deps.services, - SERVICE.automations, - (automationsAccess) => automationsAccess.list(), - ); - if (!automations) return { automations: [], unavailable: true }; - return { - automations: automations.map((automation) => ({ - id: automation.id, - name: automation.name, - description: automation.description, - enabled: automation.enabled, - triggerKind: automation.triggerKind, - lastRunAt: automation.lastRunAt, - lastRunStatus: automation.lastRunStatus, - needsPlugins: automation.missingPlugins, - })), - }; - }, - }, - { - name: "get_automation", - description: - "Get one automation's full definition (trigger and steps) by id.", - category: "read", - service: SERVICE.automations, - parameters: objectSchema( - { - automationId: num("The automation id, as returned by list_automations"), - }, - ["automationId"], - ), - handler: async (args, context) => { - const result = await readService( - context.deps.services, - SERVICE.automations, - async (automationsAccess) => ({ - automation: await automationsAccess.get(Number(args.automationId)), - }), - ); - if (result === null) return { unavailable: true }; - const { automation } = result; - if (!automation) return { error: "Automation not found" }; - return { - automation: { - id: automation.id, - name: automation.name, - enabled: automation.enabled, - definition: automation.definition, - }, - }; - }, - }, - { - name: "list_workspaces", - description: - "List the user's saved workspace layouts (named sets of open tabs and splits).", - category: "read", - service: SERVICE.workspaces, - parameters: objectSchema({}), - handler: async (_args, context) => { - const workspaces = await readService( - context.deps.services, - SERVICE.workspaces, - (saved) => saved.list(), - ); - if (workspaces === null) return { workspaces: [], unavailable: true }; - return { workspaces }; - }, - }, - { - name: "list_notification_channels", - description: - "List the user's notification channels by id, name and type. Channel configuration is never returned because it holds tokens.", - category: "read", - parameters: objectSchema({}), - handler: async (_args, context) => { - const channels = await context.deps.notify.channels(); - return { - channels: channels.map((channel) => ({ - id: channel.id, - name: channel.name, - type: channel.type, - enabled: channel.enabled, - })), - }; - }, - }, - { - name: "list_homepage_items", - description: "List the user's homepage service-link tiles.", - category: "read", - service: SERVICE.homepage, - parameters: objectSchema({}), - handler: async (_args, context) => { - const items = await readService( - context.deps.services, - SERVICE.homepage, - (homepage) => homepage.list(), - ); - if (items === null) return { items: [], unavailable: true }; - return { - items: items.map((item) => ({ - id: item.id, - typeId: item.typeId, - title: item.title ?? null, - })), - }; - }, - }, - { - name: "get_command_history", - description: - "Recent commands the user has run on one host, newest first. Useful for understanding what they have been working on.", - category: "read", - service: SERVICE.history, - parameters: objectSchema( - { - hostId: num("The host id, as returned by list_hosts"), - limit: num("How many entries to return (default 25, max 100)"), - }, - ["hostId"], - ), - handler: async (args, context) => { - const limit = Math.min(Math.max(Number(args.limit) || 25, 1), 100); - const hostId = Number(args.hostId); - - // Access is checked here rather than trusted from the model. - const host = await context.deps.hosts.get(hostId); - if (!host) return { error: "Host not found" }; - - const history = await readService( - context.deps.services, - SERVICE.history, - (terminalHistory) => terminalHistory.list(hostId, limit), - ); - if (history === null) return { commands: [], unavailable: true }; - return { commands: history.map((entry) => entry.command) }; - }, - }, - { - name: "get_network_topology", - description: "The user's saved network topology graph, if they have one.", - category: "read", - service: SERVICE.topology, - parameters: objectSchema({}), - handler: async (_args, context) => { - const result = await readService( - context.deps.services, - SERVICE.topology, - async (graph) => ({ topology: await graph.get() }), - ); - if (result === null) return { topology: null, unavailable: true }; - return { topology: result.topology }; - }, - }, -]; diff --git a/plugins/ai/src/backend/tools/types.ts b/plugins/ai/src/backend/tools/types.ts deleted file mode 100644 index 303af7506..000000000 --- a/plugins/ai/src/backend/tools/types.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { PluginContext } from "@termix/plugin-sdk/backend"; - -export type ToolCategory = "read" | "propose"; - -/** What tools and the proposal executor reach core and other plugins through. */ -export type ToolDeps = Pick< - PluginContext, - "hosts" | "services" | "notify" | "rbac" | "ssh" | "audit" ->; - -export interface ToolContext { - /** Always the request's authenticated user, never from model input. */ - userId: string; - conversationId: number; - /** Per-user opt-in for running allowlisted read-only commands. */ - allowReadOnlyCommands: boolean; - deps: ToolDeps; -} - -export interface AiTool { - name: string; - description: string; - category: ToolCategory; - /** - * The service this tool needs. While no plugin provides it, the tool is - * not offered to the model at all. - */ - service?: string; - /** Offered only to a user who turned on read-only commands. */ - requiresReadOnlyCommands?: boolean; - /** JSON Schema for the arguments, sent to the provider verbatim. */ - parameters: Record; - /** - * Read tools return data to feed back to the model. Propose tools return a - * ProposalDraft and must not mutate anything. - */ - handler: ( - args: Record, - context: ToolContext, - ) => Promise; -} - -/** What a provider adapter needs to describe a tool to its model. */ -export interface ToolDefinitionShape { - name: string; - description: string; - parameters: Record; -} - -export interface ProposalDraft { - __proposal: true; - kind: string; - summary: string; - payload: Record; -} - -export function isProposalDraft(value: unknown): value is ProposalDraft { - return ( - typeof value === "object" && - value !== null && - (value as ProposalDraft).__proposal === true - ); -} - -export function proposal( - kind: string, - summary: string, - payload: Record, -): ProposalDraft { - return { __proposal: true, kind, summary, payload }; -} - -/** Small helper so tool schemas stay readable. */ -export function objectSchema( - properties: Record, - required: string[] = [], -): Record { - return { - type: "object", - properties, - required, - additionalProperties: false, - }; -} - -export const str = (description: string) => ({ type: "string", description }); -export const num = (description: string) => ({ type: "number", description }); -export const bool = (description: string) => ({ type: "boolean", description }); diff --git a/plugins/ai/src/frontend/AiAssistantStep.tsx b/plugins/ai/src/frontend/AiAssistantStep.tsx deleted file mode 100644 index 7004e2638..000000000 --- a/plugins/ai/src/frontend/AiAssistantStep.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useEffect, useState } from "react"; -import { Check } from "lucide-react"; -import { useTranslation } from "@termix/plugin-sdk/frontend"; -import { setAiOptIn } from "./ai-api"; - -/** - * Asks once, plainly. - * - * "Leave it off" is preselected and applied on mount, so someone who clicks - * straight through ends up without the assistant rather than with it. Both - * options are described in the same flat register and neither is marked - * recommended: this is a choice, not a pitch. - */ -export function AiAssistantStep() { - const { t } = useTranslation(); - const [enabled, setEnabled] = useState(false); - - function apply(next: boolean) { - setEnabled(next); - // The local choice still shows if the write fails. - setAiOptIn(next).catch(() => undefined); - } - - // Skipping the step entirely still has to mean off, not unset. - useEffect(() => { - apply(false); - }, []); - - const options = [ - { - value: false, - title: t("onboarding.aiSkipTitle"), - description: t("onboarding.aiSkipDesc"), - }, - { - value: true, - title: t("onboarding.aiEnableTitle"), - description: t("onboarding.aiEnableDesc"), - }, - ]; - - return ( -
-

{t("onboarding.aiIntro")}

- -
- {options.map((option) => ( - - ))} -
- -

- {t("onboarding.aiNote")} -

-
- ); -} diff --git a/plugins/ai/src/frontend/AiMessage.tsx b/plugins/ai/src/frontend/AiMessage.tsx deleted file mode 100644 index 71b2ad1c5..000000000 --- a/plugins/ai/src/frontend/AiMessage.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import ReactMarkdown from "react-markdown"; -import remarkGfm from "remark-gfm"; - -interface AiMessageProps { - role: "user" | "assistant"; - content: string; -} - -export function AiMessage({ role, content }: AiMessageProps) { - if (role === "user") { - return ( -
- {content} -
- ); - } - - return ( -
- {content} -
- ); -} diff --git a/plugins/ai/src/frontend/AiPanel.tsx b/plugins/ai/src/frontend/AiPanel.tsx deleted file mode 100644 index fd0b9ff94..000000000 --- a/plugins/ai/src/frontend/AiPanel.tsx +++ /dev/null @@ -1,442 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import { useTranslation } from "@termix/plugin-sdk/frontend"; -import { Loader2, Send, Settings2, Sparkles, Square } from "lucide-react"; -import { - Button, - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, - Textarea, -} from "@termix/plugin-sdk/ui"; -import { - getAiProviders, - getAiStatus, - setAiOptIn, - type AiProposal, - type AiProvider, -} from "./ai-api"; -import { AiMessage } from "./AiMessage"; -import { AiProviderSettings } from "./AiProviderSettings"; -import { AiToolCall } from "./AiToolCall"; -import { ProposalCard } from "./ProposalCard"; -import { - buildTimeline, - finishedRunEntries, - userEntry, - type HistoryEntry, -} from "./transcript"; -import { useAiStream, type ToolActivity } from "./use-ai-stream"; -import { - activeMentionQuery, - useMentions, - type MentionItem, -} from "./useMentions"; -import { mentionLabel } from "./labels"; - -export function AiPanel({ activeTab }: { activeTab?: string | null }) { - const { t } = useTranslation(); - const { state, send, stop } = useAiStream(); - - const [providers, setProviders] = useState([]); - const [providerId, setProviderId] = useState(null); - const [loading, setLoading] = useState(true); - const [showSettings, setShowSettings] = useState(false); - const [input, setInput] = useState(""); - const [history, setHistory] = useState([]); - const [resolvedProposals, setResolvedProposals] = useState< - Record - >({}); - const runCountRef = useRef(0); - const scrollRef = useRef(null); - const textareaRef = useRef(null); - - const [mention, setMention] = useState<{ - query: string; - start: number; - } | null>(null); - const [mentionIndex, setMentionIndex] = useState(0); - const { search: searchMentions } = useMentions(Boolean(providerId)); - - const mentionMatches = mention ? searchMentions(mention.query) : []; - - const updateMentionQuery = (value: string, caret: number) => { - const next = activeMentionQuery(value, caret); - setMention(next); - setMentionIndex(0); - }; - - /** - * Replaces the "@partial" under the caret with the chosen name. The mention - * is plain text: the assistant still has to call a read tool to look the - * item up, so nothing is attached to the prompt behind the user's back. - */ - const insertMention = (item: MentionItem) => { - if (!mention) return; - const caret = textareaRef.current?.selectionStart ?? input.length; - const before = input.slice(0, mention.start); - const after = input.slice(caret); - const inserted = `@${item.label} `; - - setInput(`${before}${inserted}${after}`); - setMention(null); - - requestAnimationFrame(() => { - const position = before.length + inserted.length; - textareaRef.current?.focus(); - textareaRef.current?.setSelectionRange(position, position); - }); - }; - - const loadProviders = useCallback(async (selectId?: number) => { - try { - const list = await getAiProviders(); - setProviders(list); - setProviderId((current) => { - // A provider that was just added becomes the active one, so there is - // no second step between creating it and being able to use it. - if (selectId && list.some((entry) => entry.id === selectId)) { - return selectId; - } - // Fall back to the first if the current selection has gone away. - if (current && list.some((entry) => entry.id === current)) { - return current; - } - return list[0]?.id ?? null; - }); - return list; - } catch { - setProviders([]); - return []; - } - }, []); - - useEffect(() => { - let cancelled = false; - (async () => { - try { - const status = await getAiStatus(); - if (cancelled) return; - - // Opening the panel is consent to use it. Without this every write - // 403s through the gate, which surfaces as a bare permission error - // rather than anything the user can act on. - if (status.globallyEnabled && !status.enabled) { - await setAiOptIn(true); - } - if (!status.globallyEnabled) return; - - const list = await loadProviders(); - // With nothing configured there is nothing to chat with, so go - // straight to the form instead of showing a card that just vanishes. - if (!cancelled && list.length === 0) setShowSettings(true); - } finally { - if (!cancelled) setLoading(false); - } - })(); - return () => { - cancelled = true; - }; - }, [loadProviders]); - - useEffect(() => { - scrollRef.current?.scrollTo({ - top: scrollRef.current.scrollHeight, - behavior: "smooth", - }); - }, [state.assistantText, state.tools.length, history.length]); - - // The run's steps stay in the transcript, so the next message does not wipe - // what the assistant already looked at. - const keepRun = useCallback( - (_conversationId: number | null, reply: string, tools: ToolActivity[]) => { - const runId = runCountRef.current++; - setHistory((prev) => [ - ...prev, - ...finishedRunEntries(runId, tools, reply), - ]); - }, - [], - ); - - async function handleSend() { - const message = input.trim(); - if (!message || !providerId || state.streaming) return; - - setInput(""); - setHistory((prev) => [...prev, userEntry(message)]); - - await send({ - message, - providerId, - conversationId: state.conversationId, - activeTab, - onComplete: keepRun, - }); - } - - /** - * Records the outcome on the card and tells the assistant what happened. - * - * Without the follow-up the run just stops: the assistant proposed a - * command, the user approved it, and the model never learns it produced - * output, so it sits there as if the tool were still running. - */ - function handleProposalResolved( - id: number, - status: "applied" | "rejected", - resultSummary?: string, - ) { - setResolvedProposals((prev) => ({ - ...prev, - [id]: { status, resultSummary }, - })); - - if (!providerId || state.streaming) return; - - const proposal = state.proposals.find((entry) => entry.id === id); - const label = proposal?.summary ?? proposal?.kind ?? `proposal ${id}`; - - const followUp = - status === "applied" - ? resultSummary - ? `I approved "${label}". Result:\n\n${resultSummary}` - : `I approved "${label}". It completed.` - : `I rejected "${label}". Do not retry it unless I ask.`; - - setHistory((prev) => [...prev, userEntry(followUp)]); - - void send({ - message: followUp, - providerId, - conversationId: state.conversationId, - activeTab, - onComplete: keepRun, - }); - } - - const proposals: AiProposal[] = state.proposals.map((proposal) => { - const resolved = resolvedProposals[proposal.id]; - if (!resolved) return proposal; - return { - ...proposal, - status: resolved.status, - resultSummary: resolved.resultSummary ?? proposal.resultSummary, - }; - }); - - const timeline = buildTimeline(history, state, proposals); - - if (loading) { - return ( -
- -
- ); - } - - return ( -
- {/* - The panel also lives in the narrow sidebar, so the title and the - provider picker get their own rows rather than competing for one line. - */} -
-
- - {t("ai.title")} - - {t("hosts.docsLink")} - - -
- - {providers.length > 0 && ( -
- -
- )} -
- - {showSettings && ( -
- setShowSettings(false)} - /> -
- )} - -
- {timeline.map((item) => { - if (item.kind === "message") { - return ( - - ); - } - if (item.kind === "tool") { - return ( - - ); - } - return ( - - ); - })} - - {state.error && ( -
- {state.error} -
- )} -
- -
- {mentionMatches.length > 0 && ( -
- {mentionMatches.map((item, index) => ( - - ))} -
- )} - -