Nightly Build / build (push) Successful in 6m47s
- GET /api/file returns ETag (mtime+size) + X-Writable on disk files; PUT /api/file accepts optional if_match -> 409 Conflict on stale version (last-write-wins preserved when omitted), echoes the new ETag - FileViewerBase: View | Edit tabs for .md when the caller can write; source textarea with Save/Cancel, live preview while editing - Watcher no longer clobbers the buffer mid-edit: while editing with unsaved changes it probes the server ETag and only raises a conflict when the remote actually moved on (own-save echo is ignored) - Conflict banner: Reload remote | Copy mine, then reload | Overwrite - i18n (en/it/fr) + CSS; docs/projects.md updated
1207 lines
66 KiB
JavaScript
1207 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.plugin_catalog': 'Plugin Catalog',
|
||
'nav.catalog': 'Connectors Catalog',
|
||
'nav.config': 'Settings',
|
||
'nav.llm_requests': 'LLM Requests',
|
||
'nav.tic': 'TIC Sessions',
|
||
|
||
// ── 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.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',
|
||
|
||
// ── Config ──────────────────────────────────────────────────────────────────
|
||
'config.title': 'Config',
|
||
'config.loading': 'Loading…',
|
||
'config.developer': 'Developer',
|
||
'config.error_save':'Error saving "{name}": {msg}',
|
||
'config.enabled': 'Enabled',
|
||
'config.disabled': 'Disabled',
|
||
|
||
'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 monitors all async events generated by connected MCP servers (new emails, calendar updates, WhatsApp messages, etc.). It reads your notification rules from data/notifications.md and your memory to decide — via an LLM call — which events are worth surfacing. Relevant notifications are forwarded to the home agent set via /sethome.',
|
||
|
||
'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. When disabled, no MCP events are processed.',
|
||
'config.prop.tic__security_group.name': 'Security Group',
|
||
'config.prop.tic__security_group.desc': 'Tool permission group applied to each TIC agent session. Leave empty to use the default group.',
|
||
'config.prop.tic__interval_minutes.name': 'Check Interval (minutes)',
|
||
'config.prop.tic__interval_minutes.desc': 'How often TIC runs, in minutes. Leave empty to use the value from config.yml (tic.interval_secs).',
|
||
|
||
// ── 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.scope': 'Scope',
|
||
'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.scope': 'Scope',
|
||
'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.table.scope': 'Scope',
|
||
|
||
'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.btn.catalog': 'Catalog',
|
||
'connectors.btn.marketplace': 'Marketplace',
|
||
'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.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.detail.access.title': 'Who can use it',
|
||
'connectors.detail.access.desc': 'Ticking a box grants this connector\'s tools to that person\'s agent. Saving replaces the whole list.',
|
||
'connectors.detail.access.empty': 'No users.',
|
||
'connectors.detail.access.save': 'Save access',
|
||
|
||
'connectors.error.no_connector': 'No connector named "{name}" is available to you.',
|
||
|
||
// ── Plugins ─────────────────────────────────────────────────────────────────
|
||
'plugins.title': 'Plugins',
|
||
'plugins.loading': 'Loading…',
|
||
'plugins.section.mine': 'My plugins',
|
||
'plugins.section.manage': 'Manage plugins',
|
||
'plugins.empty.mine': 'No plugins available to you yet.',
|
||
'plugins.empty.ask_admin': 'Ask an admin to grant you access.',
|
||
'plugins.empty.manage': 'No plugins registered.',
|
||
'plugins.status.active': 'active',
|
||
'plugins.status.running': 'running',
|
||
'plugins.status.enabled': 'enabled',
|
||
'plugins.status.off': 'off',
|
||
'plugins.enabled': 'Enabled',
|
||
'plugins.save': 'Save',
|
||
'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.yes': 'yes',
|
||
'plugins.no': 'no',
|
||
'plugins.badge.user_config': 'per-user settings',
|
||
'plugins.access.btn': 'User access',
|
||
'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.title': 'Plugin Catalog',
|
||
'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 catalog',
|
||
'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.',
|
||
|
||
// ── TIC Sessions ────────────────────────────────────────────────────────────
|
||
'tic.title': 'TIC Sessions',
|
||
'tic.loading': 'Loading…',
|
||
'tic.empty': 'No TIC sessions found.',
|
||
'tic.total': '{n} total',
|
||
'tic.refresh': 'Refresh',
|
||
|
||
'tic.table.agent': 'Agent',
|
||
'tic.table.started': 'Started',
|
||
'tic.table.messages': 'Messages',
|
||
'tic.table.last_activity':'Last activity',
|
||
|
||
'tic.pagination': 'Page {cur} of {pages} — {total} sessions',
|
||
|
||
// ── 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.catalog': 'Catalog',
|
||
'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.reset_pw': 'Reset password',
|
||
'users.action.edit': 'Edit',
|
||
'users.action.connectors': 'Connectors',
|
||
'users.action.delete': 'Delete',
|
||
|
||
'users.modal.connectors_title': 'Connectors — {username}',
|
||
'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.edit_title': 'Edit {username}',
|
||
'users.modal.reset_title': 'Reset password — {username}',
|
||
'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.',
|
||
|
||
// ── Catalog ─────────────────────────────────────────────────────────────────
|
||
'catalog.title': 'Connector Catalog',
|
||
'catalog.loading': 'Loading…',
|
||
'catalog.not_admin': 'The catalog is managed by the admin.',
|
||
'catalog.not_admin_link': 'What you can activate is on the <a href="#connectors">Connectors</a> page.',
|
||
'catalog.desc': 'What this box offers. Nothing here is running — a global entry still needs enabling, a per-user one still needs each user to activate it, both on the <a href="#connectors">Connectors</a> page.',
|
||
'catalog.empty.title': 'The catalog is empty.',
|
||
'catalog.empty.hint': 'Add a connector from the marketplace to get started.',
|
||
'catalog.empty.action': 'Browse the marketplace',
|
||
|
||
'catalog.btn.add': 'Add connector',
|
||
'catalog.dropdown.marketplace': 'From the marketplace',
|
||
'catalog.dropdown.marketplace_desc': 'Vetted connectors, files verified by SHA-256.',
|
||
'catalog.dropdown.manual': 'Manually',
|
||
'catalog.dropdown.manual_desc': 'You supply the config, and vouch for it yourself.',
|
||
|
||
'catalog.table.connector': 'Connector',
|
||
'catalog.table.scope': 'Scope',
|
||
'catalog.table.type': 'Type',
|
||
'catalog.table.auth': 'Auth',
|
||
|
||
'catalog.badge.global': 'global',
|
||
'catalog.badge.per_user': 'per-user',
|
||
'catalog.badge.local_script':'local script',
|
||
'catalog.badge.remote': 'remote',
|
||
|
||
'catalog.action.remove': 'Remove from catalog',
|
||
|
||
'catalog.new.back': 'Back',
|
||
'catalog.new.title': 'Add connector manually',
|
||
'catalog.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.',
|
||
'catalog.new.name': 'Name',
|
||
'catalog.new.name_hint': 'slug',
|
||
'catalog.new.scope': 'Scope',
|
||
'catalog.new.type': 'Type',
|
||
'catalog.new.transport': 'Transport',
|
||
'catalog.new.command': 'Command',
|
||
'catalog.new.command_ph': 'python3',
|
||
'catalog.new.script_path': 'Script path',
|
||
'catalog.new.script_path_hint': 'as <connector>/<file>, under ./connectors',
|
||
'catalog.new.url': 'URL',
|
||
'catalog.new.args': 'Args',
|
||
'catalog.new.args_hint': 'one per line',
|
||
'catalog.new.config_schema': 'Required secret/env keys',
|
||
'catalog.new.config_schema_hint': 'comma/newline',
|
||
'catalog.new.auth': 'Auth',
|
||
'catalog.new.friendly': 'Friendly name',
|
||
'catalog.new.desc': 'Description',
|
||
'catalog.new.desc_hint': 'the LLM reads this when deciding to activate the connector',
|
||
'catalog.new.cancel': 'Cancel',
|
||
'catalog.new.save': 'Add to catalog',
|
||
|
||
'catalog.error.name': 'Name is required.',
|
||
'catalog.confirm.delete': 'Remove "{name}" from the catalog?\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',
|
||
};
|