Nightly Build / build (push) Successful in 7m1s
The generic per-user #plugins page is gone: a plugin with per-user settings hosts them in its own web_pages() sidebar page instead (Telegram's pairing page is new; Honcho's opt-in page already existed). The admin catalog moves from #plugin-catalog to #plugins (old hash redirected), and user_config_schema is removed from the Plugin trait, the API DTOs and both plugins — the my-config endpoint, the plugin_user_configs store and the update_user_config hook stay, now driven by each plugin's own page fragment.
1190 lines
66 KiB
JavaScript
1190 lines
66 KiB
JavaScript
export default {
|
||
// ── Navigation ─────────────────────────────────────────────────────────────
|
||
'nav.section.workspace': 'Your space',
|
||
'nav.section.extensions': 'Extensions',
|
||
'nav.section.config': 'Configuration',
|
||
'nav.section.dev': 'Development',
|
||
'nav.chat': 'Chat',
|
||
'nav.inbox': 'Inbox',
|
||
'nav.dashboard': 'Dashboard',
|
||
'nav.projects': 'Projects',
|
||
'nav.tasks': 'Task Manager',
|
||
'nav.tasks.running': 'Running Tasks',
|
||
'nav.tasks.cron': 'Cron Jobs',
|
||
'nav.tasks.scheduled': 'Scheduled Tasks',
|
||
'nav.tasks.history': 'History',
|
||
'nav.models': 'Models',
|
||
'nav.providers': 'Providers',
|
||
'nav.security': 'Security',
|
||
'nav.agents': 'Agents',
|
||
'nav.users': 'Users',
|
||
'nav.roles': 'Roles',
|
||
'nav.connectors': 'Connectors',
|
||
'nav.plugins': 'Plugins',
|
||
'nav.config': 'Settings',
|
||
'nav.llm_requests': 'LLM Requests',
|
||
'nav.system_agents': 'System agents',
|
||
|
||
// ── Top bar ────────────────────────────────────────────────────────────────
|
||
'topbar.profile': 'Profile',
|
||
'topbar.logout': 'Log out',
|
||
'topbar.account': 'Account',
|
||
'topbar.open_chat': 'Open chat',
|
||
'topbar.brand': 'Skald',
|
||
'topbar.to_light': 'Switch to light mode',
|
||
'topbar.to_dark': 'Switch to dark mode',
|
||
|
||
// ── Chat ───────────────────────────────────────────────────────────────────
|
||
'chat.title': 'Skald',
|
||
'chat.tab.general': 'General',
|
||
'chat.hello': 'Hello! How can I help you today?',
|
||
'chat.greeting': 'Hi there!',
|
||
'chat.greeting.named': 'Hi, {name}!',
|
||
'chat.greeting.sub': 'What can I do for you today?',
|
||
'chat.placeholder': 'Message… (Enter to send, Shift+Enter for a new line)',
|
||
'chat.mobile.placeholder': 'Type a message…',
|
||
'chat.mobile.ask': 'Ask me anything',
|
||
'chat.mobile.back_general': 'Back to General',
|
||
'chat.mobile.project': 'Project',
|
||
'chat.mobile.chat': 'Chat',
|
||
'chat.mobile.record_voice': 'Record voice',
|
||
'chat.mobile.stop_record': 'Stop recording',
|
||
|
||
'mobile.coming_soon': 'Coming soon',
|
||
'mobile.nav.inbox': 'Inbox',
|
||
'mobile.nav.projects': 'Projects',
|
||
'mobile.nav.chat': 'Chat',
|
||
'mobile.nav.alerts': 'Alerts',
|
||
'mobile.nav.settings': 'Settings',
|
||
'mobile.settings.theme': 'Theme',
|
||
'mobile.settings.light': 'Light',
|
||
'mobile.settings.dark': 'Dark',
|
||
'mobile.settings.language': 'Language',
|
||
'chat.send': 'Send',
|
||
'chat.stop': 'Stop',
|
||
'chat.thinking': 'Thinking…',
|
||
'chat.reasoning': 'Reasoning…',
|
||
'chat.attach': 'Attach files',
|
||
'chat.new_session': 'New conversation',
|
||
'chat.scroll_to_latest': 'Scroll to latest',
|
||
'chat.security_group': 'Security group',
|
||
'chat.collapse': 'Hide chat',
|
||
'chat.close_tab': 'Close tab',
|
||
'chat.privacy': 'Private to you',
|
||
'chat.privacy.hint': 'Only you can see this conversation. Sharing anything with the group always asks for approval first.',
|
||
'chat.suggest.1': 'What can you do?',
|
||
'chat.suggest.2': 'Help me plan my day',
|
||
'chat.suggest.3': 'Tell me a story',
|
||
'chat.suggest.4': 'What do you remember about me?',
|
||
'chat.rejected': 'Denied.',
|
||
'chat.rejected_by_user': 'Denied by user.',
|
||
'chat.truncated': 'Response truncated by the token limit (↓{tokens} tok).',
|
||
|
||
// ── Copilot render ─────────────────────────────────────────────────────────
|
||
'copilot.open_in_viewer': 'Open in viewer',
|
||
'copilot.unchanged_lines': '⋯ {n} unchanged lines ⋯',
|
||
'copilot.cancel': 'Cancel',
|
||
'copilot.clarification_ph': 'Type your answer…',
|
||
'copilot.send': 'Send',
|
||
'copilot.result': 'result',
|
||
'copilot.error_label': 'error',
|
||
'copilot.not_sent_to_llm': 'This message is not sent to the LLM',
|
||
'copilot.remove': 'Remove',
|
||
'copilot.result_json': 'result · json',
|
||
'copilot.changes': 'changes',
|
||
'copilot.view_details': 'View details',
|
||
'tool_detail.title': 'Tool call',
|
||
'tool_detail.target': 'target',
|
||
'tool_detail.input': 'input',
|
||
'copilot.agent_done': 'done',
|
||
'copilot.agent_running': 'running…',
|
||
'copilot.agent_finished': 'finished',
|
||
'copilot.status_awaiting': 'Awaiting approval',
|
||
'copilot.status_cancelled': 'Cancelled by user',
|
||
'copilot.status_denied': 'Denied by policy',
|
||
'copilot.bypass_session': 'Session',
|
||
'copilot.bypass_15min': '15 min',
|
||
|
||
// ── Copilot slash commands ─────────────────────────────────────────────────
|
||
'copilot.cmd.help': 'Show available commands',
|
||
'copilot.cmd.clear': 'Start a new conversation',
|
||
'copilot.cmd.new': 'Alias for /clear',
|
||
'copilot.cmd.models': 'List available LLM models',
|
||
'copilot.cmd.model': 'Select the model for this chat',
|
||
'copilot.cmd.context': 'Last turn\'s token usage',
|
||
'copilot.cmd.cost': 'Session spend (USD)',
|
||
'copilot.cmd.compact': 'Force context compaction',
|
||
'copilot.cmd.resettools': 'Remove activated tool groups',
|
||
'copilot.cmd.sethome': 'Set web as notification home',
|
||
|
||
// ── LLM Requests ────────────────────────────────────────────────────────────
|
||
'llmr.title': 'LLM Requests',
|
||
'llmr.loading': 'Loading…',
|
||
'llmr.empty': 'No requests found.',
|
||
'llmr.total': '{n} rows',
|
||
'llmr.filter.agent_id': 'Agent ID',
|
||
'llmr.filter.agent_ph': 'e.g. assistant',
|
||
'llmr.filter.source': 'Source',
|
||
'llmr.filter.source_ph': 'e.g. web, tic, cron',
|
||
'llmr.filter.from': 'From',
|
||
'llmr.filter.to': 'To',
|
||
'llmr.filter.apply': 'Apply',
|
||
'llmr.filter.reset': 'Reset',
|
||
'llmr.table.agent': 'Agent',
|
||
'llmr.table.source': 'Source',
|
||
'llmr.table.model': 'Model',
|
||
'llmr.table.date': 'Date',
|
||
'llmr.table.in_tokens': 'In tokens',
|
||
'llmr.table.out_tokens': 'Out tokens',
|
||
'llmr.table.cache_hit': 'Cache hit',
|
||
'llmr.cache_read': 'read: {n} tk',
|
||
'llmr.cache_write': 'write: {n} tk',
|
||
'llmr.pagination': 'Page {cur} of {pages} — {total} results',
|
||
|
||
// ── LLM Request Detail ──────────────────────────────────────────────────────
|
||
'llmr.detail.back': 'Back',
|
||
'llmr.detail.loading': 'Loading…',
|
||
'llmr.detail.request': 'Request',
|
||
'llmr.detail.no_agent': 'no agent',
|
||
'llmr.detail.error_badge': 'error',
|
||
'llmr.detail.purged': 'Payload not available — this request has been purged by the retention policy.',
|
||
'llmr.detail.reasoning_label': 'reasoning',
|
||
'llmr.detail.tool_params': 'Parameters',
|
||
'llmr.detail.tool_result': 'Result',
|
||
'llmr.detail.system_role': 'system',
|
||
'llmr.detail.cache_label': 'cache {pct}',
|
||
'llmr.detail.stat_input': 'Input tokens',
|
||
'llmr.detail.stat_output': 'Output tokens',
|
||
|
||
'llmr.detail.section_req_headers': 'Request Headers',
|
||
'llmr.detail.section_resp_headers':'Response Headers',
|
||
'llmr.detail.section_params': 'Parameters',
|
||
'llmr.detail.section_system': 'System Prompt',
|
||
'llmr.detail.section_conversation':'Conversation',
|
||
'llmr.detail.section_tools': 'Tools Defined',
|
||
'llmr.detail.section_response': 'Response',
|
||
'llmr.detail.tools_activated': 'Tools activated (DTL)',
|
||
'llmr.detail.tool_reference_loads': 'loads {n}',
|
||
'llmr.detail.flag_deferred': 'deferred',
|
||
'llmr.detail.flag_cached': 'cached',
|
||
|
||
// ── Config ──────────────────────────────────────────────────────────────────
|
||
'config.title': 'Config',
|
||
'config.loading': 'Loading…',
|
||
'config.developer': 'Developer',
|
||
'config.error_save':'Error saving "{name}": {msg}',
|
||
'config.enabled': 'Enabled',
|
||
'config.disabled': 'Disabled',
|
||
'config.llm_model.auto': 'auto',
|
||
|
||
'config.set.interface.name': 'Interface',
|
||
'config.set.interface.desc': 'Look and feel of the web interface.',
|
||
'config.set.tic_agent.name': 'TIC Agent',
|
||
'config.set.tic_agent.desc': 'TIC is a background agent that runs for every user, one at a time. For each user it reads the events their own connectors have pushed since the last run (new mail, calendar changes, incoming messages), decides — via an LLM call — which of them are worth surfacing, and sends those to that user as notifications. It reads only that user\'s events and writes only to their own conversation; a user who has not logged in since the last restart is skipped, because their database is still encrypted. Each run is recorded on the System agents page, visible to the user it ran for.',
|
||
'config.set.compaction.name': 'Compaction',
|
||
'config.set.compaction.desc': 'When a conversation grows too large, older messages are summarised by an LLM to keep the context within limits.',
|
||
|
||
'config.prop.ui_locale.name': 'Language',
|
||
'config.prop.ui_locale.desc': 'Default interface language for the whole instance. Each user can override it on their profile.',
|
||
'config.prop.tic__enabled.name': 'Enabled',
|
||
'config.prop.tic__enabled.desc': 'Enable or disable the TIC agent for the whole instance. When disabled, no events are processed for anyone.',
|
||
'config.prop.tic__security_group.name': 'Security Group',
|
||
'config.prop.tic__security_group.desc': 'Tool permission group applied to each TIC run. It is re-checked against each user\'s own role: a user whose role does not allow this group runs under their role\'s default group instead. Leave empty to always use the role default.',
|
||
'config.prop.tic__interval_minutes.name': 'Check Interval (minutes)',
|
||
'config.prop.tic__interval_minutes.desc': 'How often TIC starts a pass over all users, in minutes. Leave empty to use the value from config.yml (tic.interval_secs).',
|
||
'config.prop.compaction_model.name': 'Compaction model',
|
||
'config.prop.compaction_model.desc': 'Model used to summarise compacted conversations, for the whole instance. A cheap model is usually enough. Leave empty for automatic selection.',
|
||
|
||
// ── Projects ────────────────────────────────────────────────────────────────
|
||
'projects.title': 'Projects',
|
||
'projects.btn.new': 'New Project',
|
||
'projects.empty': 'No projects yet. Create one to get started.',
|
||
'projects.action.edit': 'Edit',
|
||
'projects.action.delete': 'Delete',
|
||
'projects.card.updated': 'Updated',
|
||
'projects.confirm.delete': 'Delete project "{name}"? This removes it for everyone it is shared with.',
|
||
'projects.badge.owned': 'Owned',
|
||
'projects.badge.shared_by': 'Shared by {name}',
|
||
'projects.badge.readonly': 'Read-only',
|
||
|
||
'projects.modal.title_edit': 'Edit Project',
|
||
'projects.modal.title_new': 'New Project',
|
||
'projects.modal.name': 'Name',
|
||
'projects.modal.name_ph': 'My Project',
|
||
'projects.modal.desc': 'Description',
|
||
'projects.modal.desc_ph': 'What this project is about',
|
||
'projects.modal.cancel': 'Cancel',
|
||
'projects.modal.saving': 'Saving…',
|
||
'projects.modal.save': 'Save',
|
||
'projects.modal.create': 'Create',
|
||
|
||
// ── Project sharing ─────────────────────────────────────────────────────────
|
||
'projects.share.title': 'Sharing',
|
||
'projects.share.owner': 'owner',
|
||
'projects.share.choose_user': 'Choose a person…',
|
||
'projects.share.add': 'Add',
|
||
'projects.share.all_added': 'Everyone already has access.',
|
||
'projects.share.remove': 'Remove',
|
||
'projects.share.access.read': 'Read',
|
||
'projects.share.access.write': 'Write',
|
||
'projects.share.access.readonly': 'Read-only',
|
||
'projects.share.access.readwrite':'Read & write',
|
||
'projects.tabs.files': 'Files',
|
||
'projects.tabs.sharing': 'Sharing',
|
||
'projects.files.col.name': 'Name',
|
||
'projects.files.col.created': 'Created',
|
||
'projects.files.col.modified': 'Modified',
|
||
'projects.files.col.size': 'Size',
|
||
'projects.files.empty': 'This folder is empty.',
|
||
'projects.files.refresh': 'Refresh',
|
||
'projects.files.drop': 'Drop files here to upload',
|
||
'projects.files.btn.new_folder': 'New folder',
|
||
'projects.files.btn.upload': 'Upload',
|
||
'projects.files.uploading': 'Uploading…',
|
||
'projects.files.action.rename': 'Rename',
|
||
'projects.files.action.delete': 'Delete',
|
||
'projects.files.confirm.delete_file': 'Delete "{name}"?',
|
||
'projects.files.confirm.delete_dir': 'Delete the folder "{name}" and everything inside it?',
|
||
'projects.files.modal.mkdir': 'New folder',
|
||
'projects.files.modal.rename': 'Rename "{name}"',
|
||
'projects.files.modal.name': 'Name',
|
||
'projects.files.error.name': 'Enter a valid name (no slashes).',
|
||
|
||
// ── Project detail ──────────────────────────────────────────────────────────
|
||
'project_board.back': 'Projects',
|
||
'project_board.open_chat': 'Open Chat',
|
||
|
||
// ── Session detail ──────────────────────────────────────────────────────────
|
||
'session.back': 'Back',
|
||
'session.loading': 'Loading session…',
|
||
'session.no_session': 'No session loaded.',
|
||
'session.no_session_hint': 'Navigate to <code>#session/{id}</code> to view a session.',
|
||
'session.empty': 'No messages in this session.',
|
||
'session.user_role': 'User',
|
||
'session.assistant_role': 'Assistant',
|
||
'session.reasoning_label': 'Reasoning',
|
||
'session.thinking_role': 'Thinking',
|
||
'session.failed': 'failed',
|
||
'session.ephemeral': 'ephemeral',
|
||
'session.automated': 'automated',
|
||
'session.live': 'live',
|
||
'session.agent': 'agent:',
|
||
'session.id': 'id:',
|
||
'session.sub_agent': 'Sub-agent:',
|
||
'session.end_of': 'end of',
|
||
'session.depth': 'depth {n}',
|
||
'session.synthetic': 'synthetic',
|
||
'session.tool_args': 'Arguments',
|
||
'session.tool_result': 'Result',
|
||
'session.tool_error': 'Error',
|
||
|
||
// ── Inbox ──────────────────────────────────────────────────────────────────
|
||
'inbox.empty': 'No pending requests',
|
||
'inbox.reject_prompt': 'Rejection reason (optional):',
|
||
|
||
// ── Approvals ──────────────────────────────────────────────────────────────
|
||
'approval.pending': 'Waiting for your OK',
|
||
'approval.approved': 'Allowed',
|
||
'approval.rejected': 'Denied',
|
||
'approval.approve': 'Allow',
|
||
'approval.reject': 'Deny',
|
||
'approval.confirm_reject': 'Confirm deny',
|
||
'approval.reject_hint': 'Optional: say why (the assistant will read it)',
|
||
'approval.bypass_15': 'Allow and skip similar requests for 15 minutes',
|
||
'approval.bypass_all': 'Allow and skip all requests for this session',
|
||
|
||
// ── Login ──────────────────────────────────────────────────────────────────
|
||
'login.title': 'Welcome back',
|
||
'login.subtitle': 'Sign in to your account.',
|
||
'login.username': 'Username',
|
||
'login.password': 'Password',
|
||
'login.submit': 'Sign in',
|
||
'login.signing': 'Signing in…',
|
||
'login.missing': 'Enter your username and password.',
|
||
'login.error': 'Invalid username or password.',
|
||
'login.network': 'Network error — please try again.',
|
||
|
||
// ── Profile ────────────────────────────────────────────────────────────────
|
||
'profile.title': 'Profile',
|
||
'profile.account': 'Account',
|
||
'profile.username': 'Username',
|
||
'profile.role': 'Role',
|
||
'profile.name': 'Display name',
|
||
'profile.name.ph': 'Your name',
|
||
'profile.language': 'Language',
|
||
'profile.language.default': 'Group default ({locale})',
|
||
'profile.saved': 'Saved.',
|
||
'profile.pw': 'Change password',
|
||
'profile.pw.current': 'Current password',
|
||
'profile.pw.new': 'New password',
|
||
'profile.pw.confirm': 'Confirm new password',
|
||
'profile.pw.short': 'Password must be at least 4 characters.',
|
||
'profile.pw.mismatch': 'Passwords do not match.',
|
||
'profile.pw.changed': 'Password changed.',
|
||
'profile.pw.submit': 'Change password',
|
||
|
||
// ── Settings (config page extras) ──────────────────────────────────────────
|
||
'config.debug': 'Debug mode',
|
||
'config.debug.desc': 'Show developer pages (LLM requests, TIC sessions) in the sidebar.',
|
||
|
||
// ── First-run setup ────────────────────────────────────────────────────────
|
||
'setup.title': 'Welcome to Skald',
|
||
'setup.subtitle': 'Create the admin account to get started.',
|
||
'setup.username': 'Choose a username.',
|
||
'setup.pw.short': 'Password must be at least 4 characters.',
|
||
'setup.pw.mismatch': 'The two passwords do not match.',
|
||
'setup.confirm': 'Confirm password',
|
||
'setup.language': 'Interface language',
|
||
'setup.profile': 'Instance type',
|
||
'setup.encrypt': 'Encrypt my conversation history',
|
||
'setup.warn': 'your password derives the encryption key. If you forget it, your entire conversation history will be permanently lost — there is no recovery.',
|
||
'setup.warn.strong': 'Warning:',
|
||
'setup.submit': 'Create account',
|
||
'setup.creating': 'Creating…',
|
||
'setup.network': 'Network error — please try again.',
|
||
|
||
// ── Dashboard ───────────────────────────────────────────────────────────────
|
||
'dashboard.status.loading': 'Loading…',
|
||
'dashboard.status.no_models': 'No LLM models',
|
||
'dashboard.status.online': 'Online & ready',
|
||
'dashboard.status.degraded': 'Degraded',
|
||
'dashboard.status.offline': 'All models offline',
|
||
|
||
'dashboard.stats.loading': 'Loading stats…',
|
||
'dashboard.stats.empty': 'No LLM requests in the selected range.',
|
||
'dashboard.stats.requests': 'Requests {per}',
|
||
'dashboard.stats.tokens': 'Tokens {per}',
|
||
'dashboard.stats.latency': 'Avg latency (ms)',
|
||
'dashboard.stats.models': 'Models',
|
||
'dashboard.stats.per_min': '/ min',
|
||
'dashboard.stats.per_hour': '/ hour',
|
||
'dashboard.stats.per_day': '/ day',
|
||
|
||
'dashboard.stats.range.hour': '1h',
|
||
'dashboard.stats.range.day': '24h',
|
||
'dashboard.stats.range.week': '7d',
|
||
'dashboard.stats.range.month': '30d',
|
||
|
||
'dashboard.stats.chart.input': 'Input',
|
||
'dashboard.stats.chart.output': 'Output',
|
||
'dashboard.stats.chart.cached': 'Cached',
|
||
'dashboard.stats.chart.non_cached': 'Non-cached',
|
||
'dashboard.stats.chart.cache_hit': 'Cache hit: {pct}%',
|
||
|
||
'dashboard.hero.subtitle': 'Your AI command centre — research, code, plan, and orchestrate. All in one place.',
|
||
|
||
'dashboard.banner.no_models.title': 'No LLM models configured.',
|
||
'dashboard.banner.no_models.desc': 'Start by adding a provider (Anthropic, OpenAI, OpenRouter…), then add at least one model in the Models section.',
|
||
'dashboard.banner.no_models.action': 'Add a provider',
|
||
|
||
'dashboard.section.stats': 'LLM Stats',
|
||
'dashboard.section.pending': 'Pending',
|
||
'dashboard.section.guide': 'Quick guide',
|
||
|
||
'dashboard.tip.honcho.title': 'Enable Honcho',
|
||
'dashboard.tip.honcho.desc': 'Persistent long-term memory — the agent learns your preferences over time. Ask the Copilot to enable it.',
|
||
|
||
'dashboard.refresh': 'Refresh',
|
||
|
||
'dashboard.guide.chat.title': 'Chat',
|
||
'dashboard.guide.chat.desc': 'Your home page is a conversation — it knows everything: ask it to run agents, enable plugins, write code, or search the web.',
|
||
'dashboard.guide.inbox.title': 'Inbox',
|
||
'dashboard.guide.inbox.desc': 'Pending approvals and agent questions that need your input before background tasks can continue.',
|
||
'dashboard.guide.agents.title': 'Agents',
|
||
'dashboard.guide.agents.desc': 'Specialized sub-agents (engineer, architect, QA…). Each has a focused system prompt, tool set, and model selection.',
|
||
'dashboard.guide.cron.title': 'Cron',
|
||
'dashboard.guide.cron.desc': 'Scheduled tasks that run automatically at set intervals, even when the Copilot is idle.',
|
||
'dashboard.guide.models.title': 'Models',
|
||
'dashboard.guide.models.desc': 'Manage LLM, transcription, and image generation models. Drag to reorder priority.',
|
||
'dashboard.guide.providers.title': 'Providers',
|
||
'dashboard.guide.providers.desc': 'Add API keys for LLM providers (Anthropic, OpenAI, OpenRouter, Ollama…).',
|
||
'dashboard.guide.security.title': 'Security',
|
||
'dashboard.guide.security.desc': 'Define rules to auto-approve or auto-reject tool calls — skip repetitive confirmation prompts.',
|
||
|
||
// ── Models (hub + sections) ─────────────────────────────────────────────────
|
||
'models.hub.title': 'Models',
|
||
'models.hub.subtitle': 'Configure LLM, transcription, and image generation providers.',
|
||
'models.hub.count.none': 'No models',
|
||
'models.hub.count.one': '1 model',
|
||
'models.hub.count.many': '{n} models',
|
||
|
||
'models.hub.card.llm.title': 'LLM',
|
||
'models.hub.card.llm.desc': 'Chat & completion models for agents and tools',
|
||
'models.hub.card.transcribe.title': 'Transcription',
|
||
'models.hub.card.transcribe.desc': 'Speech-to-text models via cloud or local plugin',
|
||
'models.hub.card.image.title': 'Image Generation',
|
||
'models.hub.card.image.desc': 'Text-to-image models via cloud API',
|
||
'models.hub.card.tts.title': 'Text-to-Speech',
|
||
'models.hub.card.tts.desc': 'Speech synthesis models via cloud or local plugin',
|
||
|
||
'models.back': 'Back to models',
|
||
'models.add': 'Add',
|
||
'models.add_model': 'Add model',
|
||
'models.add_first': 'Add your first model',
|
||
'models.edit': 'Edit',
|
||
'models.delete': 'Delete',
|
||
'models.saving': 'Saving…',
|
||
'models.save_changes': 'Save changes',
|
||
'models.cancel': 'Cancel',
|
||
'models.search': 'Search models…',
|
||
'models.loading': 'Loading models…',
|
||
'models.no_results': 'No models found',
|
||
'models.enter_id': 'Enter model ID manually',
|
||
'models.managed_plugin': 'Managed by plugin',
|
||
'models.readonly_plugin': 'Models with the <strong>Plugin</strong> badge are read-only — managed automatically by the plugin that registered them.',
|
||
'models.readonly_plugin_full': 'Models with the <strong>Plugin</strong> badge are read-only — managed automatically by the plugin that registered them. To add, modify, or remove them, ask the agent directly: it has all the documentation it needs.',
|
||
'models.default': 'default',
|
||
'models.move_up': 'Move up',
|
||
'models.move_down': 'Move down',
|
||
'models.strength.very_high': 'Very High',
|
||
'models.strength.high': 'High',
|
||
'models.strength.average': 'Average',
|
||
'models.strength.low': 'Low',
|
||
'models.strength.very_low': 'Very Low',
|
||
'models.strength.none': '— none —',
|
||
'models.reasoning.off': '— off —',
|
||
'models.reasoning.label': 'Reasoning',
|
||
'models.reasoning.thinking': 'Reasoning (thinking)',
|
||
'models.strength': 'Strength',
|
||
'models.priority': 'Priority',
|
||
'models.default_model': 'Default model',
|
||
'models.extra_params': 'Extra params',
|
||
'models.extra_params_hint': '(JSON, optional)',
|
||
'models.model_id': 'Model ID',
|
||
'models.model_id_hint': '(sent to API)',
|
||
'models.model_id_immutable': 'Model ID cannot be changed after creation.',
|
||
'models.name_alias': 'Name / Alias',
|
||
'models.name_alias_hint': '(optional)',
|
||
'models.name_alias_ph': 'same as model ID',
|
||
'models.edit_title': 'Edit {name}',
|
||
'models.edit_info': 'Model ID and provider cannot be changed. To use a different model, add a new entry.',
|
||
'models.name_help': 'Used to reference this model (e.g. in an agent\'s <code>client</code>). Must be unique.',
|
||
'models.no_providers_llm': 'Add a <strong>Provider</strong> first, then come back here to add models.',
|
||
'models.no_providers_transcribe': 'No provider supports transcription yet. Add an <strong>OpenAI</strong> or <strong>OpenRouter</strong> provider first.',
|
||
'models.no_providers_image': 'No provider supports image generation yet. Add an <strong>OpenRouter</strong> provider first.',
|
||
'models.no_providers_tts': 'No provider supports TTS yet. Add an <strong>OpenAI</strong> provider first.',
|
||
'models.list_empty_llm': 'No models configured yet.',
|
||
'models.list_empty_transcribe': 'No transcription models configured.',
|
||
'models.list_empty_image': 'No image generation models configured.',
|
||
'models.list_empty_tts': 'No TTS models configured.',
|
||
'models.list_empty_add_hint': 'Click <strong>Add</strong> to add a cloud model.',
|
||
'models.list_empty_whisper': 'Activate the <strong>Whisper Local</strong> plugin for on-device transcription.',
|
||
'models.source': 'Source',
|
||
'models.source_plugin': 'Plugin',
|
||
'models.source_cloud': 'Cloud',
|
||
'models.name_col': 'Name',
|
||
'models.provider_col': 'Provider',
|
||
'models.model_id_col': 'Model ID',
|
||
'models.language_col': 'Language',
|
||
'models.language_auto': 'auto',
|
||
'models.choose_provider': 'Choose Provider',
|
||
'models.add_model_title': 'Add Model',
|
||
'models.model_label': 'Model',
|
||
'models.add_model_provider': 'Add {type} Model — Choose Provider',
|
||
'models.add_model_type': 'Add {type} Model',
|
||
'models.priority_hint': 'Lower number = tried first. Default: 100.',
|
||
'models.priority_hint_short': 'Lower number = used first. Default: 100.',
|
||
'models.status_healthy': 'Healthy',
|
||
'models.status_degraded': 'Degraded',
|
||
'models.status_down': 'Down',
|
||
|
||
'models.llm.title': 'LLM Models',
|
||
'models.transcribe.title': 'Transcription Models',
|
||
'models.image.title': 'Image Generation Models',
|
||
'models.tts.title': 'Text-to-Speech Models',
|
||
|
||
'models.confirm_delete': 'Delete {type} model "{name}"?',
|
||
|
||
'models.error.save_order': 'Failed to save order: {msg}',
|
||
'models.error.load_models': 'Failed to load models: {msg}',
|
||
'models.error.invalid_json': 'Extra params: invalid JSON',
|
||
'models.error.select_model': 'Select a model',
|
||
|
||
'models.label.sent_to_api': '(sent to API)',
|
||
'models.label.optional': '(optional)',
|
||
'models.label.bcp47': '(BCP-47, optional)',
|
||
'models.label.required_elevenlabs': '(optional — required for ElevenLabs)',
|
||
'models.label.shown_to_llm': '(optional — shown to LLM)',
|
||
'models.label.response_fmt': 'Response format',
|
||
'models.label.response_fmt_hint': '(optional)',
|
||
'models.label.description': 'Description',
|
||
'models.label.description_hint': '(optional)',
|
||
'models.label.instructions': 'Instructions',
|
||
'models.label.instructions_hint': '(optional — shown to LLM)',
|
||
'models.label.voice_id': 'Voice ID',
|
||
'models.label.voice_id_hint': '(optional — required for ElevenLabs)',
|
||
'models.label.max_output': 'Max output tokens',
|
||
'models.label.max_output_hint': '(optional)',
|
||
|
||
'models.ph.model_id': 'e.g. gpt-4o',
|
||
'models.ph.model_id_transcribe': 'e.g. openai/whisper-1',
|
||
'models.ph.model_id_tts': 'e.g. tts-1-hd',
|
||
'models.ph.model_id_image': 'e.g. x-ai/grok-2-vision',
|
||
'models.ph.name_alias': 'same as model ID',
|
||
'models.ph.language': 'e.g. it, en — leave blank for auto-detect',
|
||
'models.ph.voice_id': 'e.g. alloy, Kore, 21m00Tcm4TlvDq8ikWAM',
|
||
'models.ph.description': 'e.g. High quality, slow — best for long responses',
|
||
'models.ph.instructions': 'e.g. Speak in a calm, neutral tone. Pause slightly between sentences.',
|
||
'models.ph.max_tokens': 'up to {n}',
|
||
|
||
'models.form.model_lock': 'Model ID cannot be changed after creation.',
|
||
'models.form.voice_hint': 'Speaker voice. OpenAI: <code>alloy</code>/<code>echo</code>/<code>nova</code>… (default <code>alloy</code> if empty); Gemini: <code>Kore</code>/<code>Puck</code>/<code>Zephyr</code>…; ElevenLabs: the voice ID.',
|
||
'models.form.instructions_hint': 'Voice/tone guidance injected into the LLM system prompt when this model is active.',
|
||
'models.form.response_hint': 'Audio format requested from the provider. Leave empty unless the model requires a specific one — e.g. Gemini TTS only accepts <code>pcm</code>.',
|
||
'models.form.response_default': 'Provider default (mp3)',
|
||
'models.form.priority_img': 'Lower number = tried first. Default: 100.',
|
||
'models.form.name_as_provider': 'Name / Alias <span class="text-muted fw-normal">(used as provider_id in the LLM tool)</span>',
|
||
|
||
'models.tts.cost_multiplier': 'Cost multiplier relative to base rate',
|
||
'models.llm.price_tooltip': 'Input/Output per 1M tokens',
|
||
|
||
// ── Approval rules ─────────────────────────────────────────────────────────
|
||
'approval.action.require': 'Require',
|
||
'approval.action.allow': 'Allow',
|
||
'approval.action.deny': 'Deny',
|
||
'approval.chip.unset': '—',
|
||
'approval.chip.req': 'Req',
|
||
|
||
'approval.category.filesystem': 'File System',
|
||
'approval.category.shell': 'Shell',
|
||
'approval.category.subagent': 'Agents',
|
||
'approval.category.introspection': 'Introspection',
|
||
'approval.category.config': 'Config',
|
||
'approval.category.dynamic': 'Dynamic',
|
||
|
||
'approval.fs.allow_read': 'Allow read',
|
||
'approval.fs.allow_write': 'Allow write',
|
||
'approval.fs.deny': 'Deny',
|
||
'approval.fs.require': 'Require',
|
||
'approval.fs.default': 'Require (system default)',
|
||
|
||
'approval.error.enter_path': 'Enter a directory path.',
|
||
'approval.error.tool_required': 'Tool pattern is required.',
|
||
'approval.error.override_prio': 'Override rules must have priority < 0.',
|
||
'approval.error.lowprio_range': 'Low priority rules must have priority between 1 and {max}.',
|
||
|
||
'approval.tool.any': 'Any tool',
|
||
'approval.tool.any_mcp': 'Any MCP tool',
|
||
'approval.tool.search': 'Search tools…',
|
||
'approval.tool.no_results': 'No results',
|
||
'approval.tool.group_builtin': 'Built-in',
|
||
'approval.tool.group_glob': 'Glob',
|
||
'approval.tool.group_mcp': 'MCP · {server}',
|
||
|
||
'approval.form.new_override': 'New override rule',
|
||
'approval.form.new_lowprio': 'New low priority rule',
|
||
'approval.form.edit': 'Edit rule',
|
||
'approval.form.tool_pattern': 'Tool pattern',
|
||
'approval.form.tool_pattern_ph': 'e.g. mcp__whatsapp__* or execute_cmd',
|
||
'approval.form.tool_pattern_hint': 'Use <code>*</code> as a trailing wildcard, e.g. <code>mcp__whatsapp__*</code>',
|
||
'approval.form.select_tool': 'Select tool',
|
||
'approval.form.path_pattern': 'Path pattern',
|
||
'approval.form.path_pattern_ph': 'e.g. data/* or data/notes/*',
|
||
'approval.form.path_pattern_hint': 'Filter by file path. Use <code>*</code> as a wildcard.',
|
||
'approval.form.action': 'Action',
|
||
'approval.form.priority': 'Priority',
|
||
'approval.form.priority_override_hint': 'Must be <strong>< 0</strong> (e.g. −10)',
|
||
'approval.form.priority_lowprio_hint': 'Must be <strong>1 – {max}</strong>',
|
||
'approval.form.source': 'Source',
|
||
'approval.form.source_any': 'Any',
|
||
'approval.form.agent_id': 'Agent ID',
|
||
'approval.form.agent_id_ph': 'assistant (empty = any)',
|
||
'approval.form.note': 'Note',
|
||
'approval.form.note_ph': 'Short description…',
|
||
'approval.form.cancel': 'Cancel',
|
||
'approval.form.saving': 'Saving…',
|
||
'approval.form.save': 'Save',
|
||
|
||
'approval.card.priority': 'Priority',
|
||
'approval.card.edit': 'Edit',
|
||
'approval.card.delete': 'Delete',
|
||
'approval.card.remove': 'Remove',
|
||
|
||
'approval.matrix.title': 'Per-tool',
|
||
'approval.matrix.subtitle': 'priority = 0 · exact tool name · no path/source filters',
|
||
'approval.matrix.loading': 'Loading tools…',
|
||
|
||
'approval.fs.title': 'File System',
|
||
'approval.fs.subtitle': 'path-scoped read / write access',
|
||
'approval.fs.empty': 'No path rules yet — add one below.',
|
||
'approval.fs.add_ph': 'Add directory path, e.g. docs',
|
||
'approval.fs.default_label': 'Default',
|
||
'approval.fs.default_hint': 'unmatched paths',
|
||
|
||
'approval.sidebar.overrides': 'Overrides',
|
||
'approval.sidebar.overrides_sub': 'priority < 0 · evaluated first',
|
||
'approval.sidebar.lowprio': 'Low Priority',
|
||
'approval.sidebar.lowprio_sub': 'priority 1–999998 · evaluated after per-tool',
|
||
'approval.sidebar.add': 'Add',
|
||
'approval.sidebar.empty': 'No rules yet.',
|
||
|
||
'approval.default_bar.title': 'Default action',
|
||
'approval.default_bar.hint': 'if no rule matches',
|
||
'approval.default_bar.unset': 'system default: allow',
|
||
|
||
'approval.header.default_badge': 'Default',
|
||
'approval.header.rule_count': '{n} rule',
|
||
'approval.header.rule_count_plural': '{n} rules',
|
||
|
||
'approval.confirm.delete_fs': 'Remove File System rule for "{path}"?',
|
||
'approval.confirm.delete_rule': 'Delete rule for "{pattern}"?',
|
||
'approval.label.optional': '(optional)',
|
||
|
||
// ── Security (groups) ───────────────────────────────────────────────────────
|
||
'security.title': 'Security',
|
||
'security.group_count': '{n} group',
|
||
'security.group_count_plural': '{n} groups',
|
||
'security.new_group': 'New group',
|
||
'security.rename_group': 'Rename group',
|
||
'security.duplicate': 'Duplicate',
|
||
'security.duplicate_title': 'Duplicate {name}',
|
||
'security.duplicating': 'Duplicating…',
|
||
'security.create_first': 'Create first group',
|
||
|
||
'security.form.id': 'ID',
|
||
'security.form.id_ph': 'e.g. cron_strict',
|
||
'security.form.id_hint': 'Lowercase slug, no spaces. Cannot be changed later.',
|
||
'security.form.name': 'Name',
|
||
'security.form.name_ph': 'e.g. Cron strict',
|
||
'security.form.description': 'Description',
|
||
'security.form.description_ph': 'Short description…',
|
||
'security.form.new_name': 'New name',
|
||
'security.form.new_id': 'New ID',
|
||
'security.form.copy_info': 'All <strong>{n}</strong> rule{s} from <em>{name}</em> will be copied.',
|
||
'security.form.cancel': 'Cancel',
|
||
'security.form.saving': 'Saving…',
|
||
'security.form.save': 'Save',
|
||
|
||
'security.card.default_badge': 'Default',
|
||
'security.card.rule_count': '{n} rule',
|
||
'security.card.rule_count_plural': '{n} rules',
|
||
'security.card.duplicate': 'Duplicate',
|
||
'security.card.rename': 'Rename',
|
||
'security.card.delete_disabled': 'Cannot delete the default group',
|
||
'security.card.delete': 'Delete group',
|
||
|
||
'security.confirm.delete': 'Delete group "{name}"?',
|
||
'security.confirm.delete_with_rules': 'Delete group "{name}" and its {n} rule{s}?',
|
||
|
||
'security.banner.text1': '<strong>Permission groups</strong> are named sets of approval rules. A session\'s active <strong>Agent Profile</strong> determines which group applies — that group\'s rules are evaluated first, with the <strong>Default</strong> group as fallback.',
|
||
'security.banner.text2': 'Click a group to view and manage its rules. The <strong>Default</strong> group cannot be deleted, but its rules can be edited freely.',
|
||
|
||
'security.empty.title': 'No groups yet.',
|
||
|
||
'security.error.name_required': 'Name is required.',
|
||
'security.error.id_required': 'ID is required.',
|
||
'security.error.group_name_required': 'Group name is required.',
|
||
'security.error.group_id_required': 'Group ID is required.',
|
||
|
||
// ── Roles ───────────────────────────────────────────────────────────────────
|
||
'roles.title': 'Roles',
|
||
'roles.count': '{n} role',
|
||
'roles.count_plural': '{n} roles',
|
||
'roles.new_role': 'New role',
|
||
'roles.loading': 'Loading…',
|
||
'roles.empty': 'No roles.',
|
||
|
||
'roles.col.id': 'ID',
|
||
'roles.col.label': 'Label',
|
||
'roles.col.group': 'Permission group',
|
||
'roles.col.interface': 'Interface',
|
||
'roles.col.assistant': 'Assistant',
|
||
|
||
'roles.badge.simple': 'Simple',
|
||
'roles.badge.full': 'Full',
|
||
|
||
'roles.form.new': 'New role',
|
||
'roles.form.edit': 'Edit {name}',
|
||
'roles.form.id': 'ID',
|
||
'roles.form.id_hint': '(slug)',
|
||
'roles.form.id_ph': 'e.g. editor',
|
||
'roles.form.id_desc': 'Lowercase, no spaces. Cannot be changed later.',
|
||
'roles.form.label': 'Label',
|
||
'roles.form.group': 'Default security group',
|
||
'roles.form.allowed': 'Additional security groups',
|
||
'roles.form.allowed_hint': 'Groups this role may also switch to at runtime, on top of the default. Users pick from these in chat.',
|
||
'roles.form.interface': 'Interface',
|
||
'roles.form.interface_full': 'Full — all pages',
|
||
'roles.form.interface_simple': 'Simple — chat only',
|
||
'roles.form.interface_hint': 'Members with the simple interface see only chat and inbox. Stored as <code>ui_mode</code> in the attrs JSON below.',
|
||
'roles.form.assistant': 'Default assistant',
|
||
'roles.form.assistant_default': 'Default (Assistant)',
|
||
'roles.form.assistant_hint': 'The agent members of this role chat with by default. Overridable per person later.',
|
||
'roles.form.attrs': 'Attrs',
|
||
'roles.form.attrs_hint': '(JSON, optional)',
|
||
'roles.form.attrs_ph': '{}',
|
||
'roles.form.cancel': 'Cancel',
|
||
'roles.form.create': 'Create',
|
||
'roles.form.save': 'Save',
|
||
|
||
'roles.error.id_label': 'ID and label are required.',
|
||
'roles.error.label': 'Label is required.',
|
||
|
||
'roles.tooltip.locked': 'Built-in role — locked',
|
||
'roles.tooltip.edit': 'Edit',
|
||
'roles.tooltip.delete': 'Delete',
|
||
|
||
'roles.confirm.delete': 'Delete role "{name}"?',
|
||
|
||
// ── Agents ──────────────────────────────────────────────────────────────────
|
||
'agents.title': 'Agents',
|
||
'agents.loading': 'Loading…',
|
||
'agents.empty': 'No agents found.',
|
||
'agents.back': 'Agents',
|
||
|
||
'agents.section.chat': 'Chat',
|
||
'agents.section.task': 'Task Executors',
|
||
'agents.section.system': 'System',
|
||
|
||
'agents.strength.very_high': 'Very High',
|
||
'agents.strength.high': 'High',
|
||
'agents.strength.average': 'Average',
|
||
'agents.strength.low': 'Low',
|
||
'agents.strength.very_low': 'Very Low',
|
||
|
||
'agents.detail.meta': 'Metadata',
|
||
'agents.detail.id': 'ID',
|
||
'agents.detail.strength': 'Strength',
|
||
'agents.detail.pinned_model': 'Pinned model',
|
||
'agents.detail.memory_files': 'Memory files',
|
||
'agents.detail.model_order': 'Model resolution order',
|
||
'agents.detail.model_order_desc': 'Models sorted by how well they match this agent\'s requirements. The system uses the first available model from the top.',
|
||
'agents.detail.no_models': 'No models configured.',
|
||
'agents.detail.prompt': 'System prompt',
|
||
'agents.detail.default': 'default',
|
||
|
||
'agents.table.rank': '#',
|
||
'agents.table.strength': 'Strength',
|
||
'agents.table.name': 'Name',
|
||
'agents.table.model_id': 'Model ID',
|
||
|
||
'agents.banner.title': '<strong>Read-only view.</strong> Agents are defined by files in <code>agents/</code> — to add, remove, or modify an agent, edit the corresponding <code>AGENT.md</code> file in that directory.',
|
||
'agents.banner.text': 'You can also ask <strong>Copilot</strong> (top bar) to create a new agent for you — just describe what it should do and it will set up all the files automatically.',
|
||
|
||
// ── Connectors ──────────────────────────────────────────────────────────────
|
||
'connectors.title': 'Connectors',
|
||
'connectors.loading': 'Loading…',
|
||
'connectors.search': 'Search connectors…',
|
||
'connectors.btn.signin_providers': 'Sign-in providers',
|
||
'connectors.empty.installed': 'No connectors installed yet.',
|
||
'connectors.empty.available': 'Nothing available to you yet.',
|
||
'connectors.empty.install_hint': 'Install one from the Marketplace to get started.',
|
||
'connectors.empty.ask_admin': 'Ask an admin to make one available.',
|
||
'connectors.empty.match': 'No connector matches "{query}".',
|
||
|
||
'connectors.chip.global': 'global',
|
||
'connectors.chip.per_user': 'per-user',
|
||
'connectors.chip.local_script': 'local script',
|
||
'connectors.chip.auth.api_key': 'Requires an API key',
|
||
'connectors.chip.auth.oauth': 'Requires sign-in (OAuth)',
|
||
'connectors.chip.auth.qr': 'Requires QR pairing',
|
||
'connectors.chip.auth.ssh_key': 'Requires an SSH key',
|
||
|
||
'connectors.status.active': 'active',
|
||
'connectors.status.needs_fix': 'needs fix',
|
||
'connectors.status.needs_signin': 'needs sign-in',
|
||
'connectors.status.enabled': 'enabled',
|
||
'connectors.status.off': 'off',
|
||
'connectors.status.available': 'available',
|
||
|
||
'connectors.providers.title': 'Sign-in providers',
|
||
'connectors.providers.desc': 'OAuth apps that per-user connectors sign in through. One app (e.g. Google) covers all of its services. The client secret is stored on this box and never shown again.',
|
||
'connectors.providers.empty': 'No sign-in providers yet.',
|
||
'connectors.providers.secret_set': 'secret set',
|
||
'connectors.providers.no_secret': 'no secret',
|
||
'connectors.providers.no_client_id': '(no client id)',
|
||
'connectors.providers.add_google': 'Add Google',
|
||
'connectors.providers.add_other': 'Add other',
|
||
'connectors.providers.save': 'Save',
|
||
'connectors.providers.cancel': 'Cancel',
|
||
'connectors.providers.delete_confirm': 'Delete the "{name}" sign-in provider?\n\nConnectors that use it will no longer be able to sign in.',
|
||
'connectors.providers.error.name_client': 'Name and client id are required.',
|
||
'connectors.providers.error.secret': 'A client secret is required for a new provider.',
|
||
'connectors.providers.field.name': 'Provider id',
|
||
'connectors.providers.field.name_help': 'The slug a connector references (must match the manifest\'s auth.provider).',
|
||
'connectors.providers.field.display': 'Display name',
|
||
'connectors.providers.field.client_id': 'Client id',
|
||
'connectors.providers.field.client_secret': 'Client secret',
|
||
'connectors.providers.field.secret_help_new': 'Required.',
|
||
'connectors.providers.field.secret_help_edit': 'Leave blank to keep the stored secret.',
|
||
'connectors.providers.field.auth_url': 'Authorization URL',
|
||
'connectors.providers.field.token_url': 'Token URL',
|
||
'connectors.providers.field.redirect': 'Redirect URI',
|
||
'connectors.providers.field.redirect_help': 'The copy-paste page. Must be registered as an authorized redirect in the provider\'s console.',
|
||
'connectors.providers.field.extra': 'Extra params (JSON)',
|
||
'connectors.providers.field.extra_help': 'Merged into the consent URL. Google needs these two to return a refresh token.',
|
||
|
||
'connectors.detail.back': 'Back',
|
||
'connectors.detail.global_note': 'Runs once for the household, on the host. Nobody reaches it until they are granted access.',
|
||
'connectors.detail.managed.title': 'This connector is managed for you.',
|
||
'connectors.detail.managed.desc': 'It is enabled by an admin and granted to you — there is nothing to configure.',
|
||
'connectors.detail.config.title_active': 'Configuration',
|
||
'connectors.detail.config.title_setup': 'Set up',
|
||
'connectors.detail.config.already_global': 'Already enabled. Re-submitting replaces the stored credentials.',
|
||
'connectors.detail.config.already_user': 'Already active. Re-submitting replaces the stored credentials.',
|
||
'connectors.detail.config.api_key': 'API key',
|
||
'connectors.detail.config.btn_test': 'Test credentials',
|
||
'connectors.detail.config.btn_testing': 'Testing…',
|
||
'connectors.detail.config.btn_enable_global': 'Enable globally',
|
||
'connectors.detail.config.btn_save_restart': 'Save & restart',
|
||
'connectors.detail.config.btn_disable': 'Disable',
|
||
'connectors.detail.config.btn_activate': 'Activate',
|
||
'connectors.detail.config.btn_deactivate': 'Deactivate',
|
||
'connectors.detail.detail_scope_global': 'global',
|
||
'connectors.detail.scope_local': 'runs code on this box',
|
||
'connectors.detail.status.active': 'active',
|
||
'connectors.detail.status.needs_fix': 'needs fixing',
|
||
'connectors.detail.status.needs_signin': 'needs sign-in',
|
||
'connectors.detail.confirm.deactivate': 'Deactivate "{name}"?',
|
||
'connectors.detail.confirm.disable_global': 'Disable "{name}"?\n\nIt stops for everyone who can use it.',
|
||
|
||
'connectors.detail.oauth.title': 'Sign in',
|
||
'connectors.detail.oauth.desc': 'Signs in with {provider}. You approve access in a browser tab, then paste back the code the page shows you — nothing is stored on this box until you do.',
|
||
'connectors.detail.oauth.scopes': 'It will request access to:',
|
||
'connectors.detail.oauth.signed_in': 'Signed in and active.',
|
||
'connectors.detail.oauth.btn_signin': 'Sign in with {provider}',
|
||
'connectors.detail.oauth.btn_signin_again':'Sign in again',
|
||
'connectors.detail.oauth.btn_finish': 'Finish sign-in',
|
||
'connectors.detail.oauth.btn_complete': 'Complete sign-in',
|
||
'connectors.detail.oauth.step1': 'A tab opened for {provider}. Approve access there.',
|
||
'connectors.detail.oauth.step1_link': 'Re-open the sign-in page',
|
||
'connectors.detail.oauth.step2': 'Paste the code the page gave you:',
|
||
'connectors.detail.oauth.cancel': 'Cancel',
|
||
'connectors.detail.oauth.deactivate': 'Deactivate',
|
||
|
||
'connectors.detail.qr.title': 'Link your phone',
|
||
'connectors.detail.qr.desc': 'Scan a QR code with your phone to link this device. The session stays on this box — no password is stored.',
|
||
'connectors.detail.qr.connected': 'Connected and active.',
|
||
'connectors.detail.qr.scan': 'Scan this code with your phone:',
|
||
'connectors.detail.qr.hint': 'WhatsApp → Settings → Linked Devices → Link a Device.',
|
||
'connectors.detail.qr.connecting': 'Connecting…',
|
||
'connectors.detail.qr.btn_start': 'Start sign-in',
|
||
'connectors.detail.qr.btn_starting': 'Preparing… (this can take a minute the first time)',
|
||
'connectors.detail.qr.btn_relink': 'Re-link (new QR)',
|
||
|
||
'connectors.detail.test.running': 'Testing credentials…',
|
||
'connectors.detail.test.skipped': 'No verification step for this connector.',
|
||
'connectors.detail.test.ok_label': 'OK',
|
||
'connectors.detail.test.fail_label': 'Failed',
|
||
'connectors.detail.test.error_saved': 'Saved, but the credentials did not check out — fix them and test again.',
|
||
'connectors.detail.test.error_verify': 'Verification failed — the connector stays disabled until the credentials are fixed.',
|
||
|
||
|
||
'connectors.error.no_connector': 'No connector named "{name}" is available to you.',
|
||
|
||
// ── Plugins ─────────────────────────────────────────────────────────────────
|
||
'plugins.loading': 'Loading…',
|
||
'plugins.empty.manage': 'No plugins registered.',
|
||
'plugins.enabled': 'Enabled',
|
||
'plugins.save_config': 'Save config',
|
||
'plugins.saved': 'Saved.',
|
||
'plugin_page.loading': 'Loading…',
|
||
'plugin_page.unavailable': 'This page is not available (plugin disabled or page not granted).',
|
||
'plugins.badge.user_page': 'user page',
|
||
'plugins.access.desc': 'Tick a box to let that person see and configure this plugin. Saving replaces the whole list.',
|
||
'plugins.access.empty': 'No users.',
|
||
'plugins.access.save': 'Save access',
|
||
'plugins.error.required': '"{field}" is required.',
|
||
'plugins.catalog.configure': 'Configure',
|
||
'plugins.health.ok': 'active',
|
||
'plugins.health.off': 'off',
|
||
'plugins.health.needs_config': 'needs configuration',
|
||
'plugins.health.not_running': 'not running',
|
||
'plugins.badge.instance_config': 'instance settings',
|
||
'plugins.detail.back': 'Back to plugins',
|
||
'plugins.detail.config.title': 'Instance configuration',
|
||
'plugins.detail.config.empty': 'This plugin has no instance settings.',
|
||
'plugins.detail.config.custom_page': 'This plugin has its own configuration page.',
|
||
'plugins.detail.config.open': 'Open configuration',
|
||
'plugins.detail.access.title': 'User access',
|
||
'plugins.detail.not_found': 'No plugin named "{id}".',
|
||
|
||
// ── Providers ───────────────────────────────────────────────────────────────
|
||
'providers.title': 'Providers',
|
||
'providers.add': 'Add',
|
||
'providers.add_first': 'Add your first provider',
|
||
'providers.empty': 'No providers configured yet.',
|
||
'providers.count': '{n}',
|
||
|
||
'providers.card.models_title': 'Models using this provider',
|
||
'providers.card.api_key_configured': 'API key configured',
|
||
'providers.card.api_key_missing': 'API key missing',
|
||
'providers.card.edit': 'Edit',
|
||
'providers.card.delete': 'Delete',
|
||
'providers.card.base_url': 'Base URL',
|
||
|
||
'providers.modal.add': 'Add Provider',
|
||
'providers.modal.edit': 'Edit Provider',
|
||
'providers.modal.name': 'Name',
|
||
'providers.modal.name_ph': 'e.g. My Anthropic',
|
||
'providers.modal.type': 'Type',
|
||
'providers.modal.api_key': 'API Key',
|
||
'providers.modal.api_key_ph': 'Leave blank to keep existing key',
|
||
'providers.modal.base_url': 'Base URL',
|
||
'providers.modal.base_url_ollama': 'http://localhost:11434',
|
||
'providers.modal.base_url_oai': 'http://localhost:1234/v1',
|
||
'providers.modal.description': 'Description',
|
||
'providers.modal.description_optional': '(optional)',
|
||
'providers.modal.cancel': 'Cancel',
|
||
'providers.modal.saving': 'Saving…',
|
||
'providers.modal.save_changes': 'Save changes',
|
||
'providers.modal.add_provider': 'Add provider',
|
||
|
||
'providers.confirm.delete': 'Delete provider "{name}"? All associated models will be deleted too.',
|
||
|
||
// ── System agents ───────────────────────────────────────────────────────────
|
||
'system_agents.title': 'System agents',
|
||
'system_agents.subtitle': 'Background agents the assistant runs for you on a schedule. They read the events your connectors receive and notify you when something looks worth your attention.',
|
||
'system_agents.loading': 'Loading…',
|
||
'system_agents.empty': 'No runs yet.',
|
||
'system_agents.empty_hint': 'A run is recorded only when there are new events to look at.',
|
||
'system_agents.total': '{n} total',
|
||
'system_agents.refresh': 'Refresh',
|
||
|
||
'system_agents.table.agent': 'Agent',
|
||
'system_agents.table.started': 'Started',
|
||
'system_agents.table.status': 'Status',
|
||
'system_agents.table.duration': 'Duration',
|
||
'system_agents.table.result': 'Result',
|
||
|
||
'system_agents.status.running': 'Running',
|
||
'system_agents.status.completed': 'Completed',
|
||
'system_agents.status.failed': 'Failed',
|
||
'system_agents.status.cancelled': 'Cancelled',
|
||
|
||
'system_agents.stat.events_processed': 'events',
|
||
'system_agents.stat.notifications_emitted': 'notifications',
|
||
|
||
'system_agents.pagination': 'Page {cur} of {pages} — {total} runs',
|
||
|
||
// ── File viewer ─────────────────────────────────────────────────────────────
|
||
'fv.back': 'Back',
|
||
'fv.download': 'Download',
|
||
'fv.mode_preview': 'Show preview',
|
||
'fv.mode_source': 'Show source',
|
||
'fv.binary_unavailable': 'Preview not available for this file type.',
|
||
'fv.latex_failed': 'LaTeX compilation failed — showing source instead',
|
||
'fv.tab_view': 'View',
|
||
'fv.tab_edit': 'Edit',
|
||
'fv.dirty_badge': 'Unsaved changes',
|
||
'fv.dirty_warn': 'Discard unsaved changes?',
|
||
'fv.save': 'Save',
|
||
'fv.saving': 'Saving…',
|
||
'fv.cancel': 'Cancel',
|
||
'fv.edit_placeholder': 'Write Markdown…',
|
||
'fv.edit_hint': 'Switch to View to preview',
|
||
'fv.conflict_title': 'The file was modified remotely while you were editing. What do you want to do?',
|
||
'fv.conflict_reload': 'Reload remote',
|
||
'fv.conflict_copy': 'Copy mine, then reload',
|
||
'fv.conflict_overwrite': 'Overwrite',
|
||
|
||
// ── Marketplace ─────────────────────────────────────────────────────────────
|
||
'marketplace.title': 'Marketplace',
|
||
'marketplace.btn.connectors': 'Connectors',
|
||
'marketplace.action.refetch': 'Refetch the feed',
|
||
'marketplace.not_admin': 'The marketplace is managed by the admin.',
|
||
'marketplace.not_admin_link': 'Connectors the admin has installed appear on the <a href="#connectors">Connectors</a> page.',
|
||
'marketplace.desc': 'Vetted connectors you can add to this box\'s catalog. Installing does not activate anything — it makes a connector <em>available</em>.',
|
||
'marketplace.feed_unreachable': 'Marketplace unreachable — {error}',
|
||
'marketplace.loading': 'Loading feed…',
|
||
|
||
'marketplace.filter.search': 'Search connectors…',
|
||
'marketplace.filter.scope': 'Scope',
|
||
'marketplace.filter.type': 'Type',
|
||
'marketplace.filter.all': 'All',
|
||
'marketplace.filter.global': 'Global',
|
||
'marketplace.filter.per_user': 'Per-user',
|
||
'marketplace.filter.remote': 'Remote',
|
||
'marketplace.filter.local': 'Local',
|
||
|
||
'marketplace.grid.empty_feed': 'The feed is empty.',
|
||
'marketplace.grid.no_match': 'No connector matches these filters.',
|
||
|
||
'marketplace.card.installed': 'installed',
|
||
'marketplace.card.update_available': 'update available',
|
||
'marketplace.card.installed_version': 'installed v{v}',
|
||
'marketplace.card.update': 'Update',
|
||
'marketplace.card.scope_global': 'global',
|
||
'marketplace.card.scope_per_user': 'per-user',
|
||
'marketplace.card.type_script': 'local script',
|
||
'marketplace.card.type_remote': 'remote',
|
||
'marketplace.card.files_one': '{n} file, SHA-256 verified on install',
|
||
'marketplace.card.files_other': '{n} files, SHA-256 verified on install',
|
||
'marketplace.card.oauth_scopes_one': 'Requests {n} OAuth scope',
|
||
'marketplace.card.oauth_scopes_other': 'Requests {n} OAuth scopes',
|
||
'marketplace.card.installing': 'Installing…',
|
||
'marketplace.card.reinstall': 'Reinstall',
|
||
'marketplace.card.install': 'Install',
|
||
'marketplace.card.homepage': 'Homepage',
|
||
|
||
'marketplace.confirm.install_warn': 'This puts code on this box:\n • {n} file(s), each verified against its SHA-256\n • installed into ./connectors/{id}/',
|
||
'marketplace.confirm.install_body':'Install "{name}" into the catalog?\n\nInstalling does not activate it.',
|
||
|
||
// ── Users ───────────────────────────────────────────────────────────────────
|
||
'users.title': 'Users',
|
||
'users.loading': 'Loading…',
|
||
'users.empty': 'No users.',
|
||
'users.count_one': '{n} user',
|
||
'users.count_other': '{n} users',
|
||
'users.btn.new': 'New user',
|
||
|
||
'users.table.username': 'Username',
|
||
'users.table.display_name': 'Display name',
|
||
'users.table.role': 'Role',
|
||
'users.table.db': 'DB',
|
||
'users.table.status': 'Status',
|
||
|
||
'users.badge.encrypted': 'Encrypted',
|
||
'users.badge.cleartext': 'Cleartext',
|
||
'users.badge.active': 'Active',
|
||
'users.badge.inactive': 'Inactive',
|
||
|
||
'users.action.delete': 'Delete',
|
||
|
||
'users.detail.back': 'Users',
|
||
'users.detail.profile': 'Profile',
|
||
'users.detail.connectors': 'Connectors',
|
||
'users.detail.security': 'Security',
|
||
'users.detail.saved': 'Saved',
|
||
'users.detail.delete_hint': 'Deletes this user, their database and all conversation history.',
|
||
'users.detail.no_such': 'User not found.',
|
||
|
||
'users.conn.globals': 'Shared connectors',
|
||
'users.conn.catalog': 'Personal connectors',
|
||
'users.conn.hint_global': 'Available to this user as soon as you enable it.',
|
||
'users.conn.hint_catalog': 'This user may activate these and sign in themselves from their Connectors page.',
|
||
'users.conn.empty': 'No connectors registered yet.',
|
||
'users.conn.disabled': 'disabled',
|
||
|
||
'users.modal.create_title': 'New user',
|
||
'users.modal.username': 'Username',
|
||
'users.modal.display_name': 'Display name',
|
||
'users.modal.optional': '(optional)',
|
||
'users.modal.role': 'Role',
|
||
'users.modal.birthdate': 'Date of birth',
|
||
'users.modal.sex': 'Sex',
|
||
'users.modal.notes': 'Notes',
|
||
'users.modal.notes_hint': 'Included in the profile shown to agents.',
|
||
'users.modal.password': 'Password',
|
||
'users.modal.new_password': 'New password',
|
||
'users.modal.encrypt': 'Encrypt conversation history',
|
||
'users.modal.encrypt_warn': '<strong>Warning:</strong> if the password is lost, the conversation history is permanently unrecoverable.',
|
||
'users.modal.active': 'Active',
|
||
'users.modal.only_cleartext': 'Only works for cleartext (non-encrypted) users.',
|
||
'users.modal.cancel': 'Cancel',
|
||
'users.modal.create_btn': 'Create',
|
||
'users.modal.save_btn': 'Save',
|
||
'users.modal.reset_btn': 'Reset',
|
||
|
||
'users.error.required_username_pw': 'Username and password are required.',
|
||
'users.error.required_username': 'Username is required.',
|
||
'users.error.password_empty': 'Password must not be empty.',
|
||
|
||
'users.confirm.delete': 'Delete user "{username}"? This permanently erases their database and all conversation history.',
|
||
|
||
// ── Connectors: add / manual entry ────────────────────────────────────────────
|
||
'connectors.add.btn': 'Add connector',
|
||
'connectors.add.marketplace': 'From the marketplace',
|
||
'connectors.add.marketplace_desc': 'Vetted connectors, files verified by SHA-256.',
|
||
'connectors.add.manual': 'Manually',
|
||
'connectors.add.manual_desc': 'You supply the config, and vouch for it yourself.',
|
||
|
||
'connectors.action.remove': 'Remove',
|
||
|
||
'connectors.new.back': 'Back',
|
||
'connectors.new.title': 'Add connector manually',
|
||
'connectors.new.script_warn': '<i class="bi bi-exclamation-triangle me-1"></i>A local script runs code on this box. Nothing verifies it — unlike the marketplace path, there is no digest to check.',
|
||
'connectors.new.name': 'Name',
|
||
'connectors.new.name_hint': 'slug',
|
||
'connectors.new.scope': 'Scope',
|
||
'connectors.new.type': 'Type',
|
||
'connectors.new.transport': 'Transport',
|
||
'connectors.new.command': 'Command',
|
||
'connectors.new.command_ph': 'python3',
|
||
'connectors.new.script_path': 'Script path',
|
||
'connectors.new.script_path_hint': 'as <connector>/<file>, under ./connectors',
|
||
'connectors.new.url': 'URL',
|
||
'connectors.new.args': 'Args',
|
||
'connectors.new.args_hint': 'one per line',
|
||
'connectors.new.config_schema': 'Required secret/env keys',
|
||
'connectors.new.config_schema_hint': 'comma/newline',
|
||
'connectors.new.auth': 'Auth',
|
||
'connectors.new.friendly': 'Friendly name',
|
||
'connectors.new.desc': 'Description',
|
||
'connectors.new.desc_hint': 'the LLM reads this when deciding to activate the connector',
|
||
'connectors.new.cancel': 'Cancel',
|
||
'connectors.new.save': 'Add connector',
|
||
|
||
'connectors.new.error_name': 'Name is required.',
|
||
'connectors.confirm.remove': 'Remove "{name}"?\n\nAnything already activated from it keeps running.',
|
||
|
||
// ── Cron ────────────────────────────────────────────────────────────────────
|
||
'cron.title': 'Cron Jobs',
|
||
'cron.count_one': '{n} job',
|
||
'cron.count_other': '{n} jobs',
|
||
|
||
'cron.empty.title': 'No recurring cron jobs.',
|
||
'cron.empty.hint': 'Ask the agent to create one with <code>execute_task</code>.',
|
||
|
||
'cron.badge.running': 'running',
|
||
'cron.badge.disabled': 'disabled',
|
||
'cron.badge.idle': 'idle',
|
||
|
||
'cron.action.delete': 'Delete',
|
||
|
||
'cron.card.label_agent': 'Agent',
|
||
'cron.card.label_last_run': 'Last run',
|
||
'cron.card.label_next_run': 'Next run',
|
||
'cron.card.enabled': 'Enabled',
|
||
'cron.card.disabled': 'Disabled',
|
||
|
||
'cron.confirm.delete': 'Delete job "{title}"?',
|
||
|
||
// ── Common ─────────────────────────────────────────────────────────────────
|
||
'common.save': 'Save',
|
||
'common.saving': 'Saving…',
|
||
'common.cancel': 'Cancel',
|
||
'common.loading': 'Loading…',
|
||
'common.saved': 'Saved',
|
||
|
||
// ── Shared Folders (blueprint §6) ────────────────────────────────────────────
|
||
'nav.shared_folders': 'Shared Folders',
|
||
'sf.title': 'Shared Folders',
|
||
'sf.count': '{n} folder',
|
||
'sf.count_plural': '{n} folders',
|
||
'sf.new': 'New folder',
|
||
'sf.loading': 'Loading…',
|
||
'sf.empty': 'No shared folders yet.',
|
||
'sf.empty_hint': 'Create one to share files with other members.',
|
||
'sf.note.propagation': 'Adding or removing a member is applied to their workspace right away.',
|
||
'sf.members': 'Members',
|
||
'sf.no_members': 'No members yet — only you (admin) can reach this folder.',
|
||
'sf.no_desc': 'No description yet — add one so the assistant knows what to keep here.',
|
||
'sf.all_added': 'Everyone available is already a member.',
|
||
'sf.choose_user': 'Choose a member…',
|
||
'sf.add': 'Add',
|
||
'sf.remove': 'Remove',
|
||
'sf.edit_desc': 'Edit description',
|
||
'sf.delete': 'Delete',
|
||
'sf.access.label': 'Access',
|
||
'sf.access.read': 'Read',
|
||
'sf.access.write': 'Write',
|
||
'sf.access.readonly': 'Read only',
|
||
'sf.access.readwrite': 'Read & write',
|
||
'sf.confirm.delete': 'Delete the shared folder “{name}”?\n\nMembers lose access. The files on disk are left untouched.',
|
||
'sf.confirm.remove_member': 'Remove {name} from “{folder}”?',
|
||
'sf.error.name': 'A folder name is required.',
|
||
'sf.form.new': 'New shared folder',
|
||
'sf.form.edit': 'Edit “{name}”',
|
||
'sf.form.name': 'Folder name',
|
||
'sf.form.name_hint': '(letters, numbers, dashes — no slashes)',
|
||
'sf.form.name_ph': 'documents',
|
||
'sf.form.name_desc': 'Becomes shared/<name> in every member’s workspace. It can’t be renamed later.',
|
||
'sf.form.desc': 'What’s it for?',
|
||
'sf.form.desc_desc': 'The assistant reads this to decide what to store here and when to look. Write it as instructions to the assistant.',
|
||
'sf.form.desc_ph': 'Household bills, contracts and warranties. Save utility PDFs and deadlines here; check here when asked about an invoice or a warranty.',
|
||
'sf.form.create': 'Create',
|
||
'sf.form.save': 'Save',
|
||
'sf.form.cancel': 'Cancel',
|
||
};
|