Files
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

673 lines
41 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Messages } from '../types';
export const enAiMessages: Messages = {
// AI Settings
'ai.agentSettings': 'Agent Settings',
'ai.chat.preparing': 'Preparing…',
'ai.chat.compactingContext': 'Compacting earlier context…',
'ai.chat.compactingStep': 'Trimming context for next step…',
'ai.chat.compactionRetry': 'Request was too large. Compacting context and retrying…',
'ai.chat.compactionBanner': 'Context compacted: {before}K → {after}K tokens',
'ai.chat.contextUsage': 'Context usage: {used} / {max} tokens',
'ai.chat.activity.title': 'Agent activity',
'ai.chat.activity.plan': 'Plan',
'ai.chat.activity.webSearch': 'Web search',
'ai.chat.activity.fileChanges': 'File changes',
'ai.chat.activity.status.running': 'Running',
'ai.chat.activity.status.completed': 'Completed',
'ai.chat.activity.status.failed': 'Failed',
'ai.chat.activity.file.add': 'Add',
'ai.chat.activity.file.update': 'Update',
'ai.chat.activity.file.delete': 'Delete',
'ai.chat.activity.usage': 'Tokens',
'ai.chat.activity.usage.input': 'in',
'ai.chat.activity.usage.output': 'out',
'ai.chat.activity.usage.cached': 'cached',
'ai.chat.activity.usage.reasoning': 'reasoning',
'ai.title': 'AI',
'ai.description': 'Configure AI providers, agents, and safety settings',
'ai.providers': 'Providers',
'ai.agents': 'Agents',
'ai.providers.empty': 'No providers configured. Add a provider to get started.',
'ai.providers.add': 'Add Provider',
'ai.providers.active': 'Active',
'ai.providers.apiKeyConfigured': 'API key configured',
'ai.providers.noApiKey': 'No API key',
'ai.providers.configure': 'Configure',
'ai.providers.remove': 'Remove',
'ai.providers.name': 'Display Name',
'ai.providers.name.placeholder': 'e.g. My Provider',
'ai.providers.style': 'Protocol style',
'ai.providers.style.anthropic': 'Anthropic-compatible',
'ai.providers.style.openai': 'OpenAI-compatible',
'ai.providers.style.google': 'Google-compatible',
'ai.providers.style.inherited': 'auto',
'ai.providers.style.help': 'Selects which API format requests use. Override when a third-party endpoint speaks a different dialect than its provider type suggests.',
'ai.providers.openaiApi': 'OpenAI API format',
'ai.providers.openaiApi.chat': 'Chat Completions',
'ai.providers.openaiApi.responses': 'Responses',
'ai.providers.openaiApi.help': 'Chat Completions works with most OpenAI-compatible endpoints. Responses can raise cache hit rates on relays that support /v1/responses.',
'ai.providers.icon.change': 'Change icon',
'ai.providers.icon.upload': 'Upload image',
'ai.providers.icon.reset': 'Reset',
'ai.providers.icon.close': 'Close',
'ai.providers.icon.uploadedNote': 'Custom icon (64×64 WebP)',
'ai.providers.icon.errorType': 'Please choose an image file.',
'ai.providers.apiKey': 'API Key',
'ai.providers.apiKey.placeholder': 'Enter API key',
'ai.providers.apiKey.decrypting': 'Decrypting...',
'ai.providers.baseUrl': 'Base URL',
'ai.providers.baseUrl.anthropicHelp': 'Anthropic-compatible: host with or without /v1 (for example https://gateway.example or https://gateway.example/v1). Detection and chat both use /v1/models and /v1/messages.',
'ai.providers.baseUrl.ollamaHelp': 'Local Ollama: http://localhost:11434/v1 (no API key). Ollama Cloud: https://ollama.com/v1 plus your cloud API key.',
'ai.providers.skipTLSVerify': 'Skip TLS certificate verification (for self-signed certs)',
'ai.providers.defaultModel': 'Default Model',
'ai.providers.defaultModel.placeholder': 'e.g. gpt-4o, claude-sonnet-4-20250514',
'ai.providers.contextWindow': 'Context window',
'ai.providers.contextWindow.placeholder': 'e.g. 128000',
'ai.providers.contextWindow.help': 'Leave blank to use the model list value when available, otherwise NetMesh uses a safe default.',
'ai.providers.contextWindow.error': 'Enter a positive whole number, or leave it blank.',
'ai.providers.refreshModels': 'Refresh models',
'ai.providers.test': 'Test',
'ai.providers.test.testing': 'Testing…',
'ai.providers.test.ok': 'Connected ({latency} ms)',
'ai.providers.test.warn': 'Reached endpoint, but response looks incomplete ({latency} ms)',
'ai.providers.test.warnSlow': 'Connected, but slow ({latency} ms)',
'ai.providers.test.error': 'Failed ({detail})',
'ai.providers.test.missingBaseUrl': 'Enter a Base URL first',
'ai.providers.test.missingApiKey': 'Enter an API key first',
'ai.providers.test.unavailable': 'Connection test is unavailable in this environment',
'ai.providers.searchModel': 'Search or type model ID...',
'ai.providers.filterModels': 'Filter models...',
'ai.providers.loadingModels': 'Loading models...',
'ai.providers.noMatchingModels': 'No matching models',
'ai.providers.clickToLoadModels': 'Click to load models',
'ai.providers.showingModels': 'Showing first 100 of {count} models. Type to filter.',
'ai.providers.advancedParams': 'Advanced Parameters',
'ai.providers.advancedParams.hint': 'Leave blank to use provider defaults.',
'ai.providers.advancedParams.maxTokens.placeholder': 'e.g. 4096',
'ai.providers.advancedParams.default': 'Provider default',
// AI Codex
'ai.codex': 'Codex',
'ai.codex.title': 'Codex CLI',
'ai.codex.description': 'Connect OpenAI Codex. Sign in with ChatGPT here, or enable an OpenAI-compatible provider API key and custom endpoint in Settings.',
'ai.codex.appServer.title': 'Use Codex App Server',
'ai.codex.appServer.experimental': 'Experimental',
'ai.codex.appServer.description': 'Use the persistent Codex protocol for native approvals, sandbox controls, live models, and mid-turn questions. SDK remains the default.',
'ai.codex.appServer.checking': 'Checking App Server support…',
'ai.codex.appServer.available': 'App Server is available for this Codex CLI.',
'ai.codex.appServer.modelCatalogWarning': 'The live Codex model catalog is unavailable. Using the built-in model list.',
'ai.codex.appServer.approval.allowSession': 'Allow for session',
'ai.codex.appServer.userInput.title': 'Codex needs your input',
'ai.codex.appServer.userInput.description': 'Answer these questions to continue the current turn.',
'ai.codex.appServer.userInput.other': 'Enter another answer',
'ai.codex.appServer.userInput.autoResolve': 'Codex will continue automatically if no answer is provided in time.',
'ai.codex.appServer.userInput.skip': 'Skip',
'ai.codex.appServer.userInput.submit': 'Continue',
'ai.codex.steer.addInstruction': 'Add instruction',
'ai.codex.steer.sending': 'Adding instruction…',
'ai.codex.steer.placeholder': 'Add an instruction while Codex is working…',
'ai.codex.steer.notSteerableReview': 'This Codex review turn cannot accept additional instructions. Your draft was kept.',
'ai.codex.steer.notSteerableCompact': 'This Codex compaction turn cannot accept additional instructions. Your draft was kept.',
'ai.codex.steer.busy': 'Another instruction is already being sent to Codex.',
'ai.codex.steer.inactive': 'The Codex turn has already ended. Your draft was kept.',
'ai.codex.steer.unsupported': 'In-flight instructions require the Codex App Server runtime.',
'ai.codex.steer.failed': 'Codex could not accept the additional instruction. Your draft was kept.',
'ai.codex.detecting': 'Detecting...',
'ai.codex.notFound': 'Not found',
'ai.codex.awaitingLogin': 'Awaiting login',
'ai.codex.connectedChatGPT': 'Connected via ChatGPT',
'ai.codex.connectedApiKey': 'Connected via API key',
'ai.codex.connectedCustomConfig': 'Connected via ~/.codex/config.toml',
'ai.codex.customConfigIncomplete': 'Custom config detected (env var missing)',
'ai.codex.customConfigHint': 'Using custom provider "{provider}" configured in ~/.codex/config.toml — no ChatGPT login needed.',
'ai.codex.customConfigMissingEnvKey': 'Warning: {envKey} is not set in your shell environment. Export it (or launch NetMesh from a shell that has it) so Codex can authenticate.',
'ai.codex.notConnected': 'Not connected',
'ai.codex.statusUnknown': 'Status unknown',
'ai.codex.path': 'Path:',
'ai.codex.notFoundHint': 'Could not find codex in PATH. Install it or specify the executable path below.',
'ai.codex.customPathPlaceholder': 'e.g. /usr/local/bin/codex',
'ai.codex.check': 'Check',
'ai.codex.resetPath': 'Reset',
'ai.codex.openLogin': 'Open Login',
'ai.codex.logout': 'Logout',
'ai.codex.connectChatGPT': 'Connect ChatGPT',
'ai.codex.refreshStatus': 'Refresh Status',
// AI Claude Code
'ai.claude.title': 'Claude Code',
'ai.claude.description': "Anthropic's agentic coding assistant. Requires the system Claude Code CLI.",
'ai.claude.detecting': 'Detecting...',
'ai.claude.detected': 'Detected',
'ai.claude.notFound': 'Not found',
'ai.claude.path': 'Path:',
'ai.claude.notFoundHint': 'Could not find claude in PATH. Install it or specify the executable path below.',
'ai.claude.customPathPlaceholder': 'e.g. /usr/local/bin/claude',
'ai.claude.configSection': 'Authentication & config (optional)',
'ai.claude.configDir': 'Config directory',
'ai.claude.configDir.placeholder': '~/.claude (leave blank for default)',
'ai.claude.configDir.hint': 'Sets CLAUDE_CONFIG_DIR — point at a folder where you have run `claude` login (contains settings.json + credentials).',
'ai.claude.settings': 'Settings file',
'ai.claude.settings.placeholder': '~/team-settings.json (path, or inline {"model":"..."})',
'ai.claude.settings.hint': 'Optional. A settings.json path or inline JSON, passed to the SDK as `settings`. Additive to — and independent of — the config directory above (merged on top, not a replacement).',
'ai.claude.envVars': 'Environment variables',
'ai.claude.envVars.placeholder': 'ANTHROPIC_BASE_URL=https://...\nANTHROPIC_MODEL=...',
'ai.claude.envVars.hint': 'One KEY=VALUE per line, passed to the Claude agent. Stored locally in plaintext — for API keys / credentials, prefer the config directory above (a `claude` login).',
'ai.claude.check': 'Check',
'ai.claude.resetPath': 'Reset',
// AI GitHub Copilot CLI
'ai.copilot.title': 'GitHub Copilot CLI',
'ai.copilot.description': 'Uses the GitHub Copilot CLI. Once detected, it can be selected as an external coding agent.',
'ai.copilot.detecting': 'Detecting...',
'ai.copilot.detected': 'Detected',
'ai.copilot.notFound': 'Not found',
'ai.copilot.path': 'Path:',
'ai.copilot.notFoundHint': 'Could not find copilot in PATH. Install it or specify the executable path below.',
'ai.copilot.customPathPlaceholder': 'e.g. /usr/local/bin/copilot',
'ai.copilot.check': 'Check',
'ai.copilot.resetPath': 'Reset',
// AI Cursor SDK
'ai.cursor.title': 'Cursor',
'ai.cursor.description': 'Uses the Cursor SDK or local Agent CLI login.',
'ai.cursor.detecting': 'Detecting...',
'ai.cursor.detected': 'Available',
'ai.cursor.notFound': 'Unavailable',
'ai.cursor.path': 'Runtime:',
'ai.cursor.notFoundHint': 'Enter an API key to enable Cursor, or switch to CLI login.',
'ai.cursor.notInstalledHint': 'Cursor SDK / Agent CLI was not detected.',
'ai.cursor.installStatus': 'Cursor runtime',
'ai.cursor.installed': 'Detected',
'ai.cursor.notInstalled': 'Not detected',
'ai.cursor.modeCli': 'CLI login',
'ai.cursor.modeApiKey': 'API Key',
'ai.cursor.modeCliHint': 'Uses your local `cursor-agent login` session and subscription Auto quota. Saved API Key is kept but not used in this mode.',
'ai.cursor.modeApiKeyHint': 'Uses the metered Cursor API. CLI login is ignored while this mode is active.',
'ai.cursor.cliLoginStatus': 'CLI login',
'ai.cursor.cliLoginOk': 'Logged in',
'ai.cursor.cliLoginAs': 'Logged in as {{email}}',
'ai.cursor.cliLoginMissing': 'Not logged in',
'ai.cursor.cliLoginHint': 'Run `cursor-agent login` in a terminal, then click Check.',
'ai.cursor.apiKeyStatus': 'API Key',
'ai.cursor.apiKeyConfigured': 'Configured',
'ai.cursor.apiKeyMissing': 'Missing',
'ai.cursor.apiKeyFromEnv': 'From environment',
'ai.cursor.apiKey': 'API Key',
'ai.cursor.apiKeyPlaceholder': 'Enter Cursor API key',
'ai.cursor.apiKeyPlaceholder.env': 'Using CURSOR_API_KEY; enter a key to override',
'ai.cursor.apiKeyEnvHint': 'Cursor can use CURSOR_API_KEY from your shell. Save a key here only if you want NetMesh to override it.',
'ai.cursor.apiKeyOverrideHint': 'NetMesh will use the saved key here before CURSOR_API_KEY.',
'ai.cursor.saveApiKey': 'Save',
'ai.cursor.saved': 'Saved',
'ai.cursor.showApiKey': 'Show API key',
'ai.cursor.hideApiKey': 'Hide API key',
'ai.cursor.customPathPlaceholder': 'e.g. /usr/local/bin/cursor',
'ai.cursor.check': 'Check',
// AI CodeBuddy Code
'ai.codebuddy.title': 'CodeBuddy Code',
'ai.codebuddy.description': 'Uses CodeBuddy Code via the official Agent SDK (`@tencent-ai/agent-sdk`). Once detected, it can be selected as an external coding agent.',
'ai.codebuddy.detecting': 'Detecting...',
'ai.codebuddy.detected': 'Detected',
'ai.codebuddy.notFound': 'Not found',
'ai.codebuddy.path': 'Path:',
'ai.codebuddy.notFoundHint': 'Could not find codebuddy in PATH. Install it or specify the executable path below.',
'ai.codebuddy.customPathPlaceholder': 'e.g. /usr/local/bin/codebuddy',
'ai.codebuddy.check': 'Check',
'ai.codebuddy.resetPath': 'Reset',
'ai.codebuddy.configSection': 'Authentication & config (optional)',
'ai.codebuddy.internetEnv': 'Internet Environment',
'ai.codebuddy.internetEnv.default': 'Default (overseas)',
'ai.codebuddy.internetEnv.internal': 'Internal',
'ai.codebuddy.internetEnv.ioa': 'IOA',
'ai.codebuddy.internetEnv.hint': 'Sets CODEBUDDY_INTERNET_ENVIRONMENT — choose Internal or IOA for restricted network environments.',
'ai.codebuddy.envVars': 'Environment variables',
'ai.codebuddy.envVars.placeholder': 'CODEBUDDY_API_KEY=...\nCODEBUDDY_AUTH_TOKEN=...\nOTHER_VAR=...',
'ai.codebuddy.envVars.hint': 'One KEY=VALUE per line, passed to the CodeBuddy agent. Set CODEBUDDY_API_KEY or CODEBUDDY_AUTH_TOKEN here for authentication. Stored locally in plaintext.',
'ai.codebuddy.advancedSection': 'Advanced options (SDK 0.3.230)',
'ai.codebuddy.effort': 'Reasoning Effort',
'ai.codebuddy.effort.default': 'Default',
'ai.codebuddy.effort.low': 'Low',
'ai.codebuddy.effort.medium': 'Medium',
'ai.codebuddy.effort.high': 'High',
'ai.codebuddy.effort.xhigh': 'XHigh',
'ai.codebuddy.effort.hint': 'Controls model reasoning depth. Use Low for simple commands to save tokens, High/XHigh for complex diagnostics.',
'ai.codebuddy.maxTurns': 'Max Turns',
'ai.codebuddy.maxTurns.hint': 'Limits the maximum conversation turns per request to prevent runaway loops. Leave empty for default.',
'ai.codebuddy.maxBudget': 'Max Budget (USD)',
'ai.codebuddy.maxBudget.hint': 'Maximum spend (USD) per request. Stops automatically when exceeded. Leave empty for no limit.',
'ai.codebuddy.sandbox': 'Sandbox Mode',
'ai.codebuddy.sandbox.hint': 'Execute tool calls in a sandbox, restricting filesystem and network access.',
'ai.codebuddy.fileCheckpointing': 'File Checkpointing',
'ai.codebuddy.fileCheckpointing.hint': 'Enable file operation checkpoints so AI file modifications can be rolled back.',
'ai.codebuddy.elicitation.title': 'CodeBuddy needs your input',
'ai.codebuddy.elicitation.description': 'Review the request to continue the current turn.',
'ai.codebuddy.elicitation.select': 'Select an option',
'ai.codebuddy.elicitation.yes': 'Yes',
'ai.codebuddy.elicitation.no': 'No',
'ai.codebuddy.elicitation.decline': 'Decline',
'ai.codebuddy.elicitation.accept': 'Continue',
'ai.codebuddy.elicitation.validation.required': '{field} is required.',
'ai.codebuddy.elicitation.validation.invalidType': '{field} has an invalid value.',
'ai.codebuddy.elicitation.validation.integer': '{field} must be an integer.',
'ai.codebuddy.elicitation.validation.notInteger': '{field} must be a whole number.',
'ai.codebuddy.elicitation.validation.minimum': '{field} must be at least {limit}.',
'ai.codebuddy.elicitation.validation.maximum': '{field} must be at most {limit}.',
'ai.codebuddy.elicitation.validation.minLength': '{field} must contain at least {limit} characters.',
'ai.codebuddy.elicitation.validation.maxLength': '{field} must contain at most {limit} characters.',
'ai.codebuddy.elicitation.validation.minItems': 'Select at least {limit} options for {field}.',
'ai.codebuddy.elicitation.validation.maxItems': 'Select at most {limit} options for {field}.',
'ai.codebuddy.elicitation.validation.format': '{field} must match the {format} format.',
'ai.codebuddy.elicitation.validation.option': 'Select a valid option for {field}.',
// AI OpenCode
'ai.opencode.title': 'OpenCode',
'ai.opencode.description': 'Uses OpenCode via the official SDK. Configure providers and keys in OpenCode, then select it as an external coding agent.',
'ai.opencode.detecting': 'Detecting...',
'ai.opencode.detected': 'Detected',
'ai.opencode.notFound': 'Not found',
'ai.opencode.path': 'Path:',
'ai.opencode.notFoundHint': 'Could not find opencode in PATH. Install it or specify the executable path below.',
'ai.opencode.customPathPlaceholder': 'e.g. /usr/local/bin/opencode',
'ai.opencode.check': 'Check',
'ai.opencode.resetPath': 'Reset',
// AI Grok Build (in-app managed agent — distinct from External MCP client install)
'ai.grok.title': 'Grok Build',
'ai.grok.description': "xAI's Grok Build coding agent CLI. Install the Grok CLI, sign in with `grok login` or set XAI_API_KEY, then select it as an external agent.",
'ai.grok.detecting': 'Detecting...',
'ai.grok.detected': 'Detected',
'ai.grok.notFound': 'Not found',
'ai.grok.path': 'Path:',
'ai.grok.notFoundHint': 'Could not find grok in PATH. Install Grok Build CLI or specify the executable path below.',
'ai.grok.customPathPlaceholder': 'e.g. /usr/local/bin/grok',
'ai.grok.check': 'Check',
'ai.grok.resetPath': 'Reset',
'ai.grok.runtime.acp.title': 'Use Grok ACP (agent stdio)',
'ai.grok.runtime.acp.default': 'Default',
'ai.grok.runtime.acp.description':
'Talk to Grok over Agent Client Protocol (grok agent stdio). Injects NetMesh MCP on session/new. Turn off to use the original headless streaming-json CLI path.',
'ai.grok.runtime.streamingJson.hint':
'Using headless streaming-json (grok -p --output-format streaming-json). Project .grok/config.toml is used for MCP injection.',
// AI Default Agent
'ai.defaultAgent': 'Default Agent',
'ai.defaultAgent.description': 'Agent to use when starting a new AI session',
'ai.defaultAgent.catty': 'Catty (Built-in)',
'ai.toolAccess.title': 'Tool Access',
'ai.toolAccess.mode': 'NetMesh Access Mode',
'ai.toolAccess.description': 'Choose how external agents access NetMesh sessions. MCP exposes the built-in server, while Skills + CLI points agents to the local NetMesh skill and CLI commands.',
'ai.toolAccess.mode.mcp': 'MCP',
'ai.toolAccess.mode.skills': 'Skills + CLI',
'ai.toolAccess.mcpPrompt.title': 'Prompt for your AI client',
'ai.toolAccess.mcpPrompt.description': 'Paste this prompt into your AI client (Codex, Claude Code, …) and it will register NetMesh MCP for you.',
'ai.toolAccess.mcpPrompt.enableHint': 'Turn on External MCP below to include the launcher path in this prompt.',
'ai.toolAccess.skills.file': 'Skill file',
'ai.toolAccess.skills.description': 'In Skills + CLI mode, agents are pointed to this local skill file automatically. The NetMesh CLI launcher path is provided to the agent in each session.',
'ai.toolAccess.skills.unavailable': 'Skill file path unavailable',
// External MCP (productized catalog MCP for Codex / Claude Code / Cursor)
'ai.externalMcp.title': 'External MCP',
'ai.externalMcp.description': 'Expose NetMesh as an MCP server for external clients such as Codex, Claude Code, Cursor, and Grok. Uses the same catalog tools as in-app agents (terminal, SFTP, Vault, port forwarding). Keep NetMesh running while clients are connected.',
'ai.externalMcp.sessionsExposed': 'Sessions in scope: {count}',
'ai.externalMcp.mode': 'Availability mode',
'ai.externalMcp.mode.temporary': 'Temporary',
'ai.externalMcp.mode.persistent': 'Always on',
'ai.externalMcp.mode.description': 'Temporary mode auto-disables after idle timeout. Always-on restores External MCP when NetMesh starts.',
'ai.externalMcp.idleTimeout': 'Idle timeout',
'ai.externalMcp.idleTimeout.description': 'In temporary mode, disable External MCP after this many minutes with no MCP operations.',
'ai.externalMcp.idleTimeout.minutes': 'min',
'ai.externalMcp.focusOnHostOpen': 'Focus window on host_open',
'ai.externalMcp.focusOnHostOpen.description': 'When an MCP client opens a host, bring the main window to the foreground. Turn off to keep working without interruption.',
'ai.externalMcp.silentSessions': 'Silent MCP sessions',
'ai.externalMcp.silentSessions.description': 'Sessions opened by AI stay out of your tab bar and are not restored after restart. View them anytime from the tray panel.',
'ai.externalMcp.sessionIdleTimeout': 'Opened session idle timeout',
'ai.externalMcp.sessionIdleTimeout.description': 'Automatically close sessions opened by an AI after this many minutes without terminal or file activity.',
'ai.externalMcp.usage.title': 'How to use',
'ai.externalMcp.usage.keepRunning': '1. Turn on External MCP and keep NetMesh running.',
'ai.externalMcp.usage.localhost': '2. Clients connect via the local launcher (127.0.0.1 only). Discovery is removed when you disable the switch.',
'ai.externalMcp.usage.permissions': '3. Write operations follow Settings → AI → Safety (observer / confirm / auto) and the command blocklist.',
'ai.externalMcp.usage.capabilities': '4. Full catalog tools are available: terminal, SFTP, Vault, and port forwarding. Secrets (passwords / private keys) are never returned.',
'ai.externalMcp.help.ariaLabel': 'External MCP help',
'ai.externalMcp.security': 'Security',
'ai.externalMcp.security.description': 'Listens on 127.0.0.1 with a rotating token, reuses AI Permission Mode for writes, and removes discovery when disabled. No OAuth — this is a local desktop bridge.',
'ai.externalMcp.permissionMode': 'Current permission mode: {mode}',
'ai.externalMcp.permissionMode.label': 'Write permission mode',
'ai.externalMcp.permissionMode.hint': 'Same setting as Settings → AI → Safety. Auto runs NetMesh write tools without NetMesh approval prompts; Confirm asks each time. External clients (Codex / Claude / Grok) may still show their own tool approval UI.',
'ai.externalMcp.permissionMode.unknown': 'Unknown',
'ai.externalMcp.discovery': 'Discovery',
'ai.externalMcp.launcher': 'Launcher',
'ai.externalMcp.unavailable': 'Unavailable',
'ai.externalMcp.bridgeUnavailable': 'External MCP bridge unavailable',
'ai.externalMcp.copy': 'Copy',
'ai.externalMcp.copied': 'Copied',
'ai.externalMcp.copyFailed': 'Copy failed. Try copying manually.',
'ai.externalMcp.refresh': 'Refresh',
'ai.externalMcp.clientConfiguration': 'Client configuration',
'ai.externalMcp.clientConfiguration.description': 'Pick a client to one-click install, or copy CLI / config snippets.',
'ai.externalMcp.client.codex': 'Codex',
'ai.externalMcp.client.claude': 'Claude Code',
'ai.externalMcp.client.grok': 'Grok',
'ai.externalMcp.client.cursor': 'Cursor',
'ai.externalMcp.cliCommand': 'CLI command',
'ai.externalMcp.configSnippet': 'Config snippet',
'ai.externalMcp.addToCodex': 'Add to Codex',
'ai.externalMcp.addToClaude': 'Add to Claude Code',
'ai.externalMcp.addToGrok': 'Add to Grok',
'ai.externalMcp.codexAdded': 'Codex MCP entry added. Restart Codex or open a new Codex session.',
'ai.externalMcp.claudeAdded': 'Claude Code MCP entry added. Restart Claude Code or open a new Claude Code session.',
'ai.externalMcp.grokAdded': 'Grok MCP entry added. Restart Grok or open a new Grok session.',
'ai.externalMcp.installCodex': 'Install Codex separately, then click Refresh.',
'ai.externalMcp.installClaude': 'Install Claude Code separately, then click Refresh.',
'ai.externalMcp.installGrok': 'Install the Grok CLI separately, then click Refresh.',
'ai.externalMcp.conflict.description': 'A NetMesh-external entry already exists and points elsewhere. Remove or edit it manually.',
'ai.externalMcp.enableForLauncher': 'Enable External MCP to get a usable launcher path.',
'ai.externalMcp.cursor.title': 'Cursor / other clients',
'ai.externalMcp.cursor.description': 'Merge this into your MCP config (for example ~/.cursor/mcp.json). Do not replace the whole file if you already have other servers.',
'ai.externalMcp.status.unavailable': 'Unavailable',
'ai.externalMcp.status.disabled': 'Disabled',
'ai.externalMcp.status.running': 'Running',
'ai.externalMcp.status.starting': 'Starting',
'ai.externalMcp.status.error': 'Error',
'ai.externalMcp.status.configured': 'Configured',
'ai.externalMcp.status.notConfigured': 'Not configured',
'ai.externalMcp.status.checking': 'Checking',
'ai.externalMcp.status.codexNotFound': 'Codex not found',
'ai.externalMcp.status.claudeNotFound': 'Claude Code not found',
'ai.externalMcp.status.grokNotFound': 'Grok not found',
'ai.externalMcp.status.conflict': 'Conflict',
'ai.userSkills.title': 'User Skills',
'ai.userSkills.description': 'Open the NetMesh skills folder to add your own skill directories. NetMesh scans these skills automatically and injects only lightweight indexes unless a skill clearly matches the current request.',
'ai.userSkills.openFolder': 'Open Skills Folder',
'ai.userSkills.reload': 'Reload Skills',
'ai.userSkills.location': 'Location',
'ai.userSkills.loading': 'Scanning user skills...',
'ai.userSkills.summary': '{ready} ready, {warnings} warnings',
'ai.userSkills.empty': 'No user skills found yet. Open the folder to add skill directories with a SKILL.md file.',
'ai.userSkills.unavailable': 'User skills are unavailable in this environment.',
'ai.userSkills.status.ready': 'Ready',
'ai.userSkills.status.warning': 'Warning',
// AI Quick Messages
'ai.quickMessages.title': 'Quick Messages',
'ai.quickMessages.description': 'Create reusable prompts you can insert from the AI chat with / or the quick-message button. Unlike user skills, quick messages fill the composer with text.',
'ai.quickMessages.add': 'Add Quick Message',
'ai.quickMessages.createTitle': 'New Quick Message',
'ai.quickMessages.editTitle': 'Edit Quick Message',
'ai.quickMessages.name': 'Name',
'ai.quickMessages.name.placeholder': 'e.g. Check disk space',
'ai.quickMessages.slug': 'Command',
'ai.quickMessages.slug.placeholder': 'disk-check',
'ai.quickMessages.descriptionField': 'Description (optional)',
'ai.quickMessages.descriptionField.placeholder': 'Short hint about what this prompt does',
'ai.quickMessages.content': 'Message content',
'ai.quickMessages.content.placeholder': 'Full prompt text to insert when selected...',
'ai.quickMessages.empty': 'No quick messages yet. Add a few prompts you use often.',
'ai.quickMessages.confirmDelete': 'Delete quick message "{name}"?',
'ai.quickMessages.error.nameRequired': 'Name is required.',
'ai.quickMessages.error.invalidSlug': 'Command may only contain lowercase letters, numbers, and hyphens.',
'ai.quickMessages.error.contentRequired': 'Message content is required.',
'ai.quickMessages.error.slugTaken': 'This command is already used by another quick message.',
'ai.quickMessages.error.slugConflictsWithSkill': 'This command conflicts with user skill "/{slug}". Choose another.',
'ai.quickMessages.error.maxItems': 'You can save at most {max} quick messages.',
// AI Chat
'ai.chat.noProvider': 'No AI provider is configured. Go to **Settings → AI → Providers** to add and enable a provider.',
'ai.chat.toolDenied': 'Action was rejected by the user.',
'ai.chat.toolApproved': 'Approved',
'ai.chat.toolApprovalHint': 'Enter once · Esc reject',
'ai.chat.approve': 'Approve',
'ai.chat.approveOnce': 'Once',
'ai.chat.alwaysAllow': 'Always',
'ai.chat.slashStopDesc': 'Stop the current AI turn and cancel in-flight tools',
'ai.chat.slashCompactDesc': 'Summarize earlier conversation context',
'ai.chat.reject': 'Reject',
'ai.chat.toolLabel': 'Tool',
'ai.chat.targetLabel': 'Target',
'ai.chat.rawCommand': 'Command',
'ai.chat.copyCommand': 'Copy',
'ai.chat.commandCopied': 'Copied',
'ai.chat.approvalSession': 'Session',
'ai.chat.approvalShell': 'Shell',
'ai.chat.approvalCwd': 'Cwd',
'ai.chat.approvalReason': 'Reason',
'ai.chat.approvalInvocation': 'Invocation',
'ai.chat.permissionRequired': 'Permission Required',
'ai.chat.permissionDescription': 'The AI agent wants to execute a tool call that requires your approval.',
'ai.chat.commandBlocked': 'This command is blocked by your security policy and cannot be executed.',
'ai.chat.recommendAllow': 'Allow',
'ai.chat.recommendConfirm': 'Confirm',
'ai.chat.recommendDeny': 'Deny',
'ai.chat.exportConversation': 'Export conversation',
'ai.chat.exportAs': 'Export As',
'ai.chat.exportMarkdown': 'Markdown',
'ai.chat.exportJSON': 'JSON',
'ai.chat.exportPlainText': 'Plain Text',
'ai.chat.thinking': 'Thinking',
'ai.chat.thoughtFor': 'Thought for {duration}',
'ai.chat.thought': 'Thought',
'ai.chat.agents': 'Agents',
'ai.chat.detectedOnMachine': 'Detected on this machine',
'ai.chat.rescan': 'Re-scan',
'ai.chat.permObserver': 'Observer',
'ai.chat.permConfirm': 'Confirm',
'ai.chat.permAuto': 'Auto',
'ai.chat.permObserverDesc': 'Read only',
'ai.chat.permConfirmDesc': 'Ask before writes',
'ai.chat.permAutoDesc': 'Run freely',
'ai.chat.emptyHint': 'Ask about your servers, run commands, or get help with configurations.',
'ai.chat.placeholder': 'Message {agent} — @ to include context, / for commands',
'ai.chat.placeholderDefault': 'Message Catty Agent...',
'ai.chat.noModel': 'No model',
'ai.chat.noProviderModel': 'No default model — set one in Settings → AI → Providers.',
'ai.chat.selectProvider': 'Select provider',
'ai.chat.selectProviderAndModel': 'Select provider and model',
'ai.chat.selectModel': 'Select model',
'ai.chat.searchModels': 'Search models',
'ai.chat.providers': 'Providers',
'ai.chat.models': 'Models',
'ai.chat.pinned': 'Pinned',
'ai.chat.useCustomModel': 'Use "{id}"',
'ai.chat.thinkingLevel': 'Thinking',
'ai.chat.thinkingOff': 'Off',
'ai.chat.pinModel': 'Pin model',
'ai.chat.unpinModel': 'Unpin model',
'ai.chat.loadingModels': 'Loading models...',
'ai.chat.noMatchingModels': 'No matching models',
'ai.chat.recent': 'Recent',
'ai.chat.viewAll': 'View All',
'ai.chat.untitled': 'Untitled',
'ai.chat.justNow': 'Just now',
'ai.chat.minutesAgo': '{n}m ago',
'ai.chat.hoursAgo': '{n}h ago',
'ai.chat.daysAgo': '{n}d ago',
'ai.chat.newChat': 'New Chat',
'ai.chat.allSessions': 'All Sessions',
'ai.chat.loadEarlierMessages': 'Load earlier messages ({n} more)',
'ai.chat.jumpNav': 'Jump to message',
'ai.chat.jumpUntitled': '(empty message)',
'ai.chat.usedTools': 'Tools used: {n}',
'ai.chat.loadMoreSessions': 'Load more sessions ({n} more)',
'ai.chat.noSessions': 'No previous sessions',
'ai.chat.retryHint': 'You can retry by sending your message again.',
'ai.chat.approvalTimeout': 'Tool approval timed out after 5 minutes. You can retry by sending your message again.',
'ai.chat.menuHosts': 'Hosts',
'ai.chat.menuContext': 'Context',
'ai.chat.menuFiles': 'Files',
'ai.chat.menuImage': 'Image',
'ai.chat.menuMentionHost': 'Mention Host',
'ai.chat.menuMentionNote': 'Mention Note',
'ai.chat.mentionNoteSearch': 'Search notes…',
'ai.chat.mentionNoteEmpty': 'No matching notes',
'ai.chat.mentionNoteUnavailable': 'This agent cannot read Vault notes in the current connection mode.',
'ai.chat.mentionNoteTooMany': 'These notes cannot all be referenced together. Please select fewer notes.',
'ai.chat.mentionNoteInvalid': '"{{title}}" could not be attached: the note has an invalid identifier.',
'ai.chat.untitledNote': 'Untitled note',
'ai.chat.menuUserSkills': 'User Skills',
'ai.chat.menuSlashCommands': 'Slash Commands',
'ai.chat.slashCommands': 'Slash commands',
'ai.chat.slashSystemCommands': 'Commands',
'ai.chat.slashQuickMessages': 'Quick messages',
'ai.chat.slashUserSkills': 'User skills',
'ai.chat.quickMessages': 'Slash commands',
'ai.chat.slashNoResults': 'No matching commands',
'ai.chat.slashEmptyHint': 'Add prompts in Settings → AI → Quick Messages.',
// AI Chat Shortcuts
'ai.chatShortcuts.title': 'Chat Shortcuts',
'ai.chatShortcuts.selectionAction': 'Show Add to Conversation when selecting terminal text',
'ai.chatShortcuts.selectionAction.description': 'Show a small AI button next to selected terminal text.',
// AI Error
'ai.codex.bridgeError': 'Codex main-process handlers are not loaded yet. Fully restart NetMesh, or restart the Electron dev process, then try again.',
// AI Web Search
'ai.webSearch.title': 'Web Search',
'ai.webSearch.enable': 'Enable Web Search',
'ai.webSearch.enable.description': 'Allow the AI agent to search the web for current information.',
'ai.webSearch.provider': 'Search Provider',
'ai.webSearch.provider.description': 'Choose a web search API provider.',
'ai.webSearch.apiKey': 'API Key',
'ai.webSearch.apiKey.description': 'API key for the selected search provider.',
'ai.webSearch.apiKey.placeholder': 'Enter API key...',
'ai.webSearch.apiHost': 'API Host',
'ai.webSearch.apiHost.description': 'Custom API endpoint. Leave default unless you use a proxy.',
'ai.webSearch.apiHost.searxngDescription': 'URL of your SearXNG instance (required).',
'ai.webSearch.maxResults': 'Max Results',
'ai.webSearch.maxResults.description': 'Maximum number of search results to return (1-20).',
// AI Safety Settings
'ai.safety.title': 'Safety',
'ai.safety.permissionMode': 'Permission Mode',
'ai.safety.permissionMode.description': 'Controls how the AI interacts with your NetMesh terminal sessions. Observer mode blocks write operations that go through NetMesh. External agent CLIs may still have their own local tools and approval flow.',
'ai.safety.permissionMode.observer': 'Observer - Read only, no actions',
'ai.safety.permissionMode.confirm': 'Confirm - Ask before actions',
'ai.safety.permissionMode.auto': 'Auto - Execute freely',
'ai.safety.commandTimeout': 'Command Timeout',
'ai.safety.commandTimeout.description': 'Maximum seconds a command can run before being terminated through NetMesh execution.',
'ai.safety.commandTimeout.unit': 'sec',
'ai.safety.responseIdleTimeout': 'Built-in AI Response Wait',
'ai.safety.responseIdleTimeout.description': 'Cancel a built-in AI request after this many seconds without a new response. This setting does not control total response time or command execution.',
'ai.safety.responseIdleTimeout.unit': 'sec',
'ai.safety.maxIterations': 'Max Iterations',
'ai.safety.maxIterations.description': 'Maximum number of AI tool-use loops to prevent runaway execution. External agents may have their own internal iteration limits that take precedence.',
'ai.safety.blocklist': 'Command Blocklist',
'ai.safety.blocklist.description': 'Regex patterns to block dangerous commands executed through NetMesh.',
'ai.safety.blocklist.placeholder': 'Regex pattern...',
'ai.safety.blocklist.reset': 'Reset to defaults',
'ai.safety.blocklist.add': 'Add pattern',
'ai.safety.grants.title': 'Permission memory',
'ai.safety.grants.heading': 'Confirm-mode allow rules',
'ai.safety.grants.description': 'Confirm mode asks before running an operation. Saved rules automatically allow matching operations across all terminal sessions/nodes, and can be edited manually.',
'ai.safety.grants.empty': 'No saved rules yet. Approve a tool with “Always allow”, or add one manually.',
'ai.safety.grants.capability': 'Capability',
'ai.safety.grants.sessionPattern': 'Session pattern',
'ai.safety.grants.commandPattern': 'Command pattern (optional)',
'ai.safety.grants.note': 'Note (optional)',
'ai.safety.grants.add': 'Add rule',
'ai.safety.grants.remove': 'Remove',
'ai.safety.grants.export': 'Export JSON',
'ai.safety.grants.import': 'Import JSON',
'ai.safety.note': 'These safety settings are enforced for actions that go through NetMesh. External agent CLIs may also expose local tools that are governed by the agent itself.',
// Unified tooltips for terminal workspace and top tabs (issue #954)
'terminal.layer.addTerminal': 'Add Terminal',
'terminal.layer.switchToSplitView': 'Switch to Split View',
'terminal.layer.sftp': 'SFTP',
'terminal.layer.scripts': 'Scripts',
'terminal.layer.history': 'History',
'terminal.layer.theme': 'Theme',
'terminal.layer.notes': 'Notes',
'terminal.layer.aiChat': 'AI Chat',
'terminal.layer.movePanelLeft': 'Move panel to left',
'terminal.layer.movePanelRight': 'Move panel to right',
'terminal.layer.closePanel': 'Close panel',
'terminal.layer.closePane': 'Close split',
'terminal.layer.resizeSplit': 'Resize split',
'terminal.layer.splitHorizontal': 'Split top and bottom',
'terminal.layer.splitVertical': 'Split left and right',
'terminal.layer.openInNewSplit': 'Open in new split',
'terminal.layer.hostTree.search': 'Search hosts...',
'terminal.layer.hostTree.searchButton': 'Search',
'terminal.layer.hostTree.tagsButton': 'Filter by tags',
'terminal.layer.hostTree.newHost': 'New host',
'terminal.layer.hostTree.newHostInGroup': 'New host in this group',
'terminal.layer.hostTree.editHost': 'Edit host',
'terminal.layer.hostTree.hostSavedNextConnection': 'Host updated. Connection settings will apply the next time you connect.',
'terminal.layer.hostTree.newGroup': 'New group',
'terminal.layer.hostTree.localShell': 'Local shell',
'terminal.layer.hostTree.tagsEmpty': 'No tags available',
'terminal.layer.hostTree.clearTags': 'Clear selection',
'terminal.layer.hostTree.collapse': 'Collapse host list',
'terminal.layer.hostTree.expand': 'Expand host list',
'terminal.layer.hostTree.empty': 'No hosts found',
'terminal.layer.hostTree.details.host': 'Host',
'terminal.layer.hostTree.details.user': 'User',
'terminal.layer.hostTree.details.port': 'Port',
'terminal.layer.hostTree.details.protocol': 'Protocol',
'terminal.layer.hostTree.details.group': 'Group',
'terminal.layer.hostTree.details.tags': 'Tags',
'terminal.layer.hostTree.details.lastConnected': 'Last connected',
'topTabs.openQuickSwitcher': 'Open quick switcher',
'topTabs.moreTabs': 'More tabs',
'topTabs.aiAssistant': 'AI Assistant',
'topTabs.newLocalTerminal': 'New Local Terminal',
'topTabs.controlPanel': 'Quick controls',
'topTabs.controlPanel.externalMcp': 'External MCP',
'topTabs.controlPanel.theme': 'Theme',
'topTabs.controlPanel.theme.light': 'Light',
'topTabs.controlPanel.theme.dark': 'Dark',
'topTabs.controlPanel.theme.system': 'System',
'topTabs.externalMcp.enable': 'Enable External MCP',
'topTabs.externalMcp.disable': 'Disable External MCP',
'topTabs.windowOpacity': 'Window opacity',
'topTabs.openSettings': 'Open Settings',
'ai.chat.sessionHistory': 'Session history',
'ai.chat.resizeInput': 'Drag to resize the message input',
'ai.chat.attach': 'Attach',
'ai.chat.terminalSelectionAttachment': 'Terminal selection',
'ai.chat.terminalSelectionLines': 'lines: {count}',
'ai.chat.collapse': 'Collapse',
'ai.chat.expand': 'Expand',
'ai.chat.enableAgent': 'Enable {name}',
'ai.chat.artifact.noteFallback': 'Vault note',
'ai.chat.artifact.openNotes': 'Open Notes',
'ai.chat.artifact.openHosts': 'Open Hosts',
'ai.chat.artifact.notesSummary': '{count} notes in Vault',
'ai.chat.artifact.hostsSummary': '{count} hosts in Vault',
'ai.chat.artifact.hostsAdded': 'Added {count} hosts',
'ai.chat.artifact.hostsPreview': 'Preview {count} hosts',
'ai.chat.artifact.failed': 'Vault operation failed',
'ai.chat.artifact.unavailableTitle': 'Unavailable',
'ai.chat.artifact.noteMissing': 'This note is no longer in your Vault.',
'ai.chat.artifact.hostMissing': 'This host is no longer in your Vault.',
'ai.chat.artifact.snippetMissing': 'This snippet or script is no longer in your Vault.',
'ai.chat.artifact.openSnippets': 'Open Snippets',
'ai.chat.artifact.snippetsSummary': '{count} snippets in Vault',
'ai.chat.artifact.scriptsSummary': '{count} scripts in Vault',
'ai.chat.artifact.snippetFallback': 'Vault snippet',
'ai.chat.artifact.scriptFallback': 'Automation script',
'ai.chat.artifact.scriptLanguage': '{language} script',
'ai.chat.artifact.snippetDeleted': 'Snippet deleted',
'ai.chat.artifact.scriptDeleted': 'Script deleted',
'ai.chat.artifact.snippetRan': 'Snippet executed',
'ai.chat.artifact.scriptStarted': 'Script run started',
'ai.chat.artifact.scriptRunStatus': 'Script run {status}',
'ai.chat.artifact.scriptRunsSummary': '{count} script runs',
'ai.chat.artifact.scriptRunStopped': 'Script run stopped',
'ai.chat.artifact.scriptRunPaused': 'Script run paused',
'ai.chat.artifact.scriptRunResumed': 'Script run resumed',
'ai.chat.artifact.scriptReference': 'nct API reference',
'zmodem.waitingForRemote': 'Waiting for remote...',
'zmodem.uploading': 'Uploading',
'zmodem.downloading': 'Downloading',
'zmodem.cancelTransfer': 'Cancel transfer (Ctrl+C)',
'zmodem.overwrite.title': 'Remote file already exists',
'zmodem.overwrite.applyToRest': 'Apply to remaining conflicts',
'zmodem.overwrite.overwrite': 'Overwrite',
'zmodem.overwrite.skip': 'Skip',
'zmodem.overwrite.cancel': 'Cancel',
'settings.shortcuts.resetToDefault': 'Reset to default',
};