mirror of
https://github.com/RightNow-AI/openfang.git
synced 2026-04-25 17:25:11 +02:00
Merge pull request #1015 from AlexZander85/feature/russian-i18n-translation
feat(i18n): add Russian localization and i18n framework
This commit is contained in:
608
crates/openfang-api/static/i18n/en.json
Normal file
608
crates/openfang-api/static/i18n/en.json
Normal file
@@ -0,0 +1,608 @@
|
||||
{
|
||||
"app.name": "OpenFang",
|
||||
"app.version": "v",
|
||||
|
||||
"nav.chat": "Chat",
|
||||
"nav.monitor": "Monitor",
|
||||
"nav.overview": "Overview",
|
||||
"nav.analytics": "Analytics",
|
||||
"nav.logs": "Logs",
|
||||
"nav.agents": "Agents",
|
||||
"nav.sessions": "Sessions",
|
||||
"nav.approvals": "Approvals",
|
||||
"nav.comms": "Comms",
|
||||
"nav.automation": "Automation",
|
||||
"nav.workflows": "Workflows",
|
||||
"nav.scheduler": "Scheduler",
|
||||
"nav.extensions": "Extensions",
|
||||
"nav.channels": "Channels",
|
||||
"nav.skills": "Skills",
|
||||
"nav.hands": "Hands",
|
||||
"nav.system": "System",
|
||||
"nav.runtime": "Runtime",
|
||||
"nav.settings": "Settings",
|
||||
|
||||
"auth.sign_in": "Sign In",
|
||||
"auth.enter_credentials": "Enter your dashboard credentials.",
|
||||
"auth.username": "Username",
|
||||
"auth.password": "Password",
|
||||
"auth.api_key_required": "API Key Required",
|
||||
"auth.api_key_desc": "This instance requires an API key. Enter the key from your config.toml.",
|
||||
"auth.api_key_hint": "Add api_key = \"your-key\" at the top of ~/.openfang/config.toml (not under any [section]).",
|
||||
"auth.enter_api_key": "Enter API key...",
|
||||
"auth.unlock_dashboard": "Unlock Dashboard",
|
||||
"auth.login_failed": "Login failed",
|
||||
|
||||
"status.agents_running": "agent(s) running",
|
||||
"status.connecting": "Connecting...",
|
||||
"status.reconnecting": "Reconnecting...",
|
||||
"status.disconnected": "disconnected",
|
||||
"status.ws": "WS",
|
||||
"status.http": "HTTP",
|
||||
"status.ready": "Ready",
|
||||
"status.loading": "Loading...",
|
||||
"status.loading_workflows": "Loading workflows...",
|
||||
"status.loading_channels": "Loading channels...",
|
||||
"status.loading_skills": "Loading skills...",
|
||||
"status.loading_jobs": "Loading scheduled jobs...",
|
||||
"status.loading_triggers": "Loading triggers...",
|
||||
"status.loading_history": "Loading run history...",
|
||||
"status.loading_hands": "Loading hands...",
|
||||
"status.loading_active_hands": "Loading active hands...",
|
||||
"status.loading_mcp": "Loading MCP servers...",
|
||||
"status.loading_files": "Loading files...",
|
||||
"status.loading_skills_details": "Loading skills details...",
|
||||
"status.no_channels_match": "No channels match your search",
|
||||
|
||||
"actions.logout": "Logout",
|
||||
"actions.new_agent": "New Agent",
|
||||
"actions.browse_skills": "Browse Skills",
|
||||
"actions.add_channel": "Add Channel",
|
||||
"actions.create_workflow": "Create Workflow",
|
||||
"actions.settings": "Settings",
|
||||
"actions.create_agent": "Create Agent",
|
||||
"actions.configure_provider": "Configure Provider",
|
||||
"actions.cancel": "Cancel",
|
||||
"actions.confirm": "Confirm",
|
||||
"actions.save": "Save",
|
||||
"actions.delete": "Delete",
|
||||
"actions.edit": "Edit",
|
||||
"actions.clone": "Clone",
|
||||
"actions.stop": "Stop",
|
||||
"actions.run": "Run",
|
||||
"actions.enable": "Enable",
|
||||
"actions.disable": "Disable",
|
||||
"actions.view_all": "View All",
|
||||
"actions.retry": "Retry",
|
||||
"actions.refresh": "Refresh",
|
||||
"actions.approve": "Approve",
|
||||
"actions.reject": "Reject",
|
||||
"actions.update": "Update",
|
||||
"actions.test_connection": "Test Connection",
|
||||
"actions.remove": "Remove",
|
||||
"actions.save_test": "Save & Test",
|
||||
"actions.export_toml": "Export TOML",
|
||||
"actions.save_workflow": "Save Workflow",
|
||||
"actions.auto_layout": "Auto Layout",
|
||||
"actions.clear": "Clear",
|
||||
"actions.zoom_out": "Zoom out",
|
||||
"actions.zoom_in": "Zoom in",
|
||||
"actions.fit": "Fit",
|
||||
"actions.duplicate": "Duplicate",
|
||||
"actions.copy_clipboard": "Copy to Clipboard",
|
||||
"actions.copied": "Copied!",
|
||||
"actions.copy": "Copy",
|
||||
"actions.hide_code": "Hide Code",
|
||||
"actions.view_code": "View Code",
|
||||
"actions.install": "Install",
|
||||
"actions.installing": "Installing...",
|
||||
"actions.installed": "Installed",
|
||||
"actions.load_more": "Load More",
|
||||
"actions.back_to_browse": "Back to browse",
|
||||
"actions.activate": "Activate",
|
||||
"actions.create_schedule": "Create Schedule",
|
||||
"actions.submit": "Submit",
|
||||
"actions.close": "Close",
|
||||
"actions.next": "Next",
|
||||
"actions.back": "Back",
|
||||
"actions.spawn_agent": "Spawn Agent",
|
||||
"actions.spawning": "Spawning...",
|
||||
"actions.create_job": "Create Job",
|
||||
"actions.spawn_wizard": "Wizard",
|
||||
"actions.raw_toml": "Raw TOML",
|
||||
"actions.setup_wizard": "Setup Wizard",
|
||||
"actions.configure_manually": "Configure Manually",
|
||||
"actions.dismiss": "Dismiss",
|
||||
"actions.create_workflow_btn": "Create Workflow",
|
||||
"actions.execute": "Execute",
|
||||
"actions.executing": "Executing...",
|
||||
"actions.generated_toml": "Generated TOML",
|
||||
"actions.running": "Running...",
|
||||
|
||||
"footer.shortcuts": "Ctrl+K agents | Ctrl+N new",
|
||||
|
||||
"theme.light": "Light",
|
||||
"theme.system": "System",
|
||||
"theme.dark": "Dark",
|
||||
|
||||
"errors.connection_error": "Connection Error",
|
||||
"errors.daemon_unreachable": "Cannot reach daemon — is openfang running?",
|
||||
"errors.not_authorized": "Not authorized — check your API key",
|
||||
"errors.permission_denied": "Permission denied",
|
||||
"errors.resource_not_found": "Resource not found",
|
||||
"errors.rate_limited": "Rate limited — slow down and try again",
|
||||
"errors.request_too_large": "Request too large",
|
||||
"errors.server_error": "Server error — check daemon logs",
|
||||
"errors.daemon_unavailable": "Daemon unavailable — is it running?",
|
||||
"errors.unexpected": "Unexpected error",
|
||||
"errors.reconnected": "Reconnected",
|
||||
"errors.connection_lost": "Connection lost, reconnecting...",
|
||||
"errors.switched_http": "Connection lost — switched to HTTP mode",
|
||||
"errors.connection_lost": "Connection lost, reconnecting...",
|
||||
"errors.switched_http": "Connection lost — switched to HTTP mode",
|
||||
"errors.reconnected": "Reconnected",
|
||||
|
||||
"toasts.approval_waiting": "An agent is waiting for approval. Open Approvals to review.",
|
||||
"toasts.agent_created": "Agent Created",
|
||||
"toasts.agent_stopped": "Agent Stopped",
|
||||
"toasts.tool_used": "Tool Used",
|
||||
"toasts.tool_completed": "Tool Completed",
|
||||
"toasts.message_in": "Message In",
|
||||
"toasts.response_sent": "Response Sent",
|
||||
"toasts.session_reset": "Session Reset",
|
||||
"toasts.compacted": "Compacted",
|
||||
"toasts.model_changed": "Model Changed",
|
||||
"toasts.login_attempt": "Login Attempt",
|
||||
"toasts.login_ok": "Login OK",
|
||||
"toasts.login_failed": "Login Failed",
|
||||
"toasts.denied": "Denied",
|
||||
"toasts.rate_limited": "Rate Limited",
|
||||
"toasts.workflow_run": "Workflow Run",
|
||||
"toasts.trigger_fired": "Trigger Fired",
|
||||
"toasts.skill_installed": "Skill Installed",
|
||||
"toasts.mcp_connected": "MCP Connected",
|
||||
"toasts.session_deleted": "Session deleted",
|
||||
|
||||
"overview.welcome": "Welcome to OpenFang",
|
||||
"overview.getting_started": "Getting Started",
|
||||
"overview.setup_wizard": "Setup Wizard",
|
||||
"overview.steps_completed": "of 5 steps completed",
|
||||
"overview.agents_running": "Agents Running",
|
||||
"overview.tokens_used": "Tokens Used",
|
||||
"overview.total_cost": "Total Cost",
|
||||
"overview.uptime": "Uptime",
|
||||
"overview.channels": "Channels",
|
||||
"overview.skills": "Skills",
|
||||
"overview.mcp_servers": "MCP Servers",
|
||||
"overview.tool_calls": "Tool Calls",
|
||||
"overview.providers": "Providers",
|
||||
"overview.recent_activity": "Recent Activity",
|
||||
"overview.no_recent_activity": "No Recent Activity",
|
||||
"overview.chat_with_agent": "Chat with an Agent",
|
||||
"overview.system_health": "System Health",
|
||||
"overview.healthy": "Healthy",
|
||||
"overview.unreachable": "Unreachable",
|
||||
"overview.security_systems": "Security Systems",
|
||||
"overview.llm_providers": "LLM Providers",
|
||||
"overview.defense_active": "9 defense-in-depth systems active",
|
||||
"overview.quick_actions": "Quick Actions",
|
||||
|
||||
"setup.configure_provider": "Configure an LLM provider",
|
||||
"setup.create_first_agent": "Create your first agent",
|
||||
"setup.send_first_message": "Send your first message",
|
||||
"setup.connect_channel": "Connect a messaging channel",
|
||||
"setup.browse_install_skill": "Browse or install a skill",
|
||||
|
||||
"tooltips.cooling_down": "cooling down (rate limited)",
|
||||
"tooltips.circuit_open": "circuit breaker open",
|
||||
"tooltips.ready": "ready",
|
||||
"tooltips.not_configured": "not configured",
|
||||
|
||||
"chat.placeholder": "Message OpenFang... (/ for commands)",
|
||||
"chat.ready": "Ready",
|
||||
"chat.generating": "Generating...",
|
||||
"chat.queued": "queued",
|
||||
"chat.sessions": "Sessions",
|
||||
"chat.new_session": "+ New",
|
||||
"chat.no_sessions": "No sessions",
|
||||
"chat.search_messages": "Search messages...",
|
||||
"chat.select_agent": "Select an agent to start chatting",
|
||||
"chat.recording": "Recording... release to send",
|
||||
"chat.drop_files": "Drop files here",
|
||||
"chat.attach_file": "Attach file",
|
||||
"chat.stop_generating": "Stop generating",
|
||||
"chat.switch_model": "Switch model",
|
||||
"chat.search_models": "Search models...",
|
||||
"chat.no_models_found": "No models found",
|
||||
"chat.available_models": "Available models — pick one or keep typing",
|
||||
"chat.switching": "Switching...",
|
||||
"chat.model_switched": "Switched to",
|
||||
"chat.model_switch_failed": "Model switch failed",
|
||||
"chat.using_http_mode": "Using HTTP mode (no streaming)",
|
||||
"chat.session_name_prompt": "Session name (optional):",
|
||||
"chat.session_created": "Session created",
|
||||
"chat.session_create_failed": "Failed to create session",
|
||||
"chat.stop_agent_title": "Stop Agent",
|
||||
"chat.stop_agent_confirm": "Stop agent",
|
||||
"chat.agent_stopped": "Agent stopped",
|
||||
"chat.stop_agent_failed": "Failed to stop agent",
|
||||
"chat.welcome_message": "**Welcome to OpenFang Chat!**\n\n- Type `/` to see available commands\n- `/help` shows all commands\n- `/think on` enables extended reasoning\n- `/context` shows context window usage\n- `/verbose off` hides tool details\n- `Ctrl+Shift+F` toggles focus mode\n- Drag & drop files to attach them\n- `Ctrl+/` opens the command palette",
|
||||
|
||||
"chat.slash.help": "Show available commands",
|
||||
"chat.slash.agents": "Switch to Agents page",
|
||||
"chat.slash.new": "New session (clear history)",
|
||||
"chat.slash.compact": "Compact session context",
|
||||
"chat.slash.model": "Show or switch model (/model [name])",
|
||||
"chat.slash.stop": "Cancel current agent run",
|
||||
"chat.slash.usage": "Show token usage",
|
||||
"chat.slash.think": "Toggle reasoning (/think [on|off|stream])",
|
||||
"chat.slash.context": "Show context window usage",
|
||||
"chat.slash.verbose": "Toggle tool details (/verbose [off|on|full])",
|
||||
"chat.slash.queue": "Check if agent is processing",
|
||||
"chat.slash.status": "Show system status",
|
||||
"chat.slash.clear": "Clear chat",
|
||||
"chat.slash.exit": "Disconnect from agent",
|
||||
"chat.slash.budget": "Show budget limits and costs",
|
||||
"chat.slash.peers": "Show OFP network status",
|
||||
"chat.slash.a2a": "List A2A agents",
|
||||
|
||||
"commands.help": "Show available commands",
|
||||
"commands.agents": "Switch to Agents page",
|
||||
"commands.new": "Reset session",
|
||||
"commands.switch": "Switch agent",
|
||||
"commands.clear": "Clear conversation",
|
||||
"commands.model": "Switch model",
|
||||
"commands.think": "Toggle reasoning mode",
|
||||
"commands.focus": "Toggle focus mode",
|
||||
"commands.theme": "Cycle theme",
|
||||
|
||||
"tips.commands": "Type / for commands",
|
||||
"tips.think": "/think on for reasoning",
|
||||
"tips.focus": "Ctrl+Shift+F for focus mode",
|
||||
|
||||
"agents.info": "Info",
|
||||
"agents.files": "Files",
|
||||
"agents.config": "Config",
|
||||
"agents.chat": "Chat",
|
||||
"agents.clone": "Clone",
|
||||
"agents.clear_history": "Clear History",
|
||||
"agents.change": "Change",
|
||||
"agents.none_fallback": "None — add a fallback chain",
|
||||
"agents.add": "+ Add",
|
||||
"agents.loading_files": "Loading files...",
|
||||
"agents.no_workspace_files": "No workspace files found",
|
||||
"agents.save_config": "Save Config",
|
||||
"agents.tool_filters": "Tool Filters",
|
||||
"agents.allowlist": "Allowlist",
|
||||
"agents.blocklist": "Blocklist",
|
||||
"agents.agent_name": "Agent Name",
|
||||
"agents.emoji": "Emoji",
|
||||
"agents.color": "Color",
|
||||
"agents.archetype": "Archetype",
|
||||
"agents.provider": "Provider",
|
||||
"agents.model": "Model",
|
||||
"agents.system_prompt": "System Prompt",
|
||||
"agents.soul_persona": "Soul / Persona",
|
||||
"agents.tool_profile": "Tool Profile",
|
||||
"agents.minimal_profile": "Minimal — Read-only file access",
|
||||
"agents.coding_profile": "Coding — Files + shell + web fetch",
|
||||
"agents.fullstack_profile": "Full-Stack — Files + shell + web fetch + search",
|
||||
"agents.research_profile": "Research — Web + search + analysis",
|
||||
"agents.admin_profile": "Admin — Full system access (dangerous)",
|
||||
"agents.agent_created": "Agent Created",
|
||||
"agents.agent_stopped": "Agent Stopped",
|
||||
"agents.agent_deleted": "Agent Deleted",
|
||||
|
||||
"presets.professional": "Professional",
|
||||
"presets.professional_desc": "Precise, business-oriented assistant focused on efficiency and clarity. Prioritizes actionable insights and structured communication.",
|
||||
"presets.professional_soul": "Communicate in a clear, professional tone. Be direct and structured. Use formal language and data-driven reasoning. Prioritize accuracy over personality.",
|
||||
"presets.friendly": "Friendly",
|
||||
"presets.friendly_desc": "Warm and approachable assistant that builds rapport and uses conversational language. Great for brainstorming and exploration.",
|
||||
"presets.friendly_soul": "Be warm, approachable, and conversational. Use casual language and show genuine interest in the user. Add personality to your responses while staying helpful.",
|
||||
"presets.technical": "Technical",
|
||||
"presets.technical_desc": "Expert developer companion optimized for code, architecture, and technical problem-solving. Precise terminology, deep dives, benchmarks.",
|
||||
"presets.technical_soul": "Focus on technical accuracy and depth. Use precise terminology. Show your work and reasoning. Prefer code examples and structured explanations.",
|
||||
"presets.creative": "Creative",
|
||||
"presets.creative_desc": "Imaginative collaborator for content creation, design thinking, and unconventional solutions. Embraces ambiguity and explores possibilities.",
|
||||
"presets.creative_soul": "Be imaginative and expressive. Use vivid language, analogies, and unexpected connections. Encourage creative thinking and explore multiple perspectives.",
|
||||
"presets.concise": "Concise",
|
||||
"presets.concise_desc": "Minimal and direct assistant that respects your time. Cuts through noise to deliver focused, actionable responses.",
|
||||
"presets.concise_soul": "Be extremely brief and to the point. No filler, no pleasantries. Answer in the fewest words possible while remaining accurate and complete.",
|
||||
"presets.mentor": "Mentor",
|
||||
"presets.mentor_desc": "Patient educator that explains concepts thoroughly, provides context, and guides learning. Socratic method when appropriate.",
|
||||
"presets.mentor_soul": "Be patient and encouraging like a great teacher. Break down complex topics step by step. Ask guiding questions. Celebrate progress and build confidence.",
|
||||
|
||||
"agents.profile.minimal": "Minimal",
|
||||
"agents.profile.minimal_desc": "Read-only file access",
|
||||
"agents.profile.coding": "Coding",
|
||||
"agents.profile.coding_desc": "Files + shell + web fetch",
|
||||
"agents.profile.research": "Research",
|
||||
"agents.profile.research_desc": "Web search + file read/write",
|
||||
"agents.profile.messaging": "Messaging",
|
||||
"agents.profile.messaging_desc": "Agents + memory access",
|
||||
"agents.profile.automation": "Automation",
|
||||
"agents.profile.automation_desc": "All tools except custom",
|
||||
"agents.profile.balanced": "Balanced",
|
||||
"agents.profile.balanced_desc": "General-purpose tool set",
|
||||
"agents.profile.precise": "Precise",
|
||||
"agents.profile.precise_desc": "Focused tool set for accuracy",
|
||||
"agents.profile.creative": "Creative",
|
||||
"agents.profile.creative_desc": "Full tools with creative emphasis",
|
||||
"agents.profile.full": "Full",
|
||||
"agents.profile.full_desc": "All 35+ tools",
|
||||
|
||||
"wizard.general_assistant": "General Assistant",
|
||||
"wizard.general_assistant_desc": "You are a versatile AI assistant that helps users with a wide range of tasks. You are knowledgeable, helpful, and able to adapt to the user's needs.",
|
||||
"wizard.code_helper": "Code Helper",
|
||||
"wizard.code_helper_desc": "You are an expert programming assistant specialized in software development. You help write, debug, and refactor code across multiple languages.",
|
||||
"wizard.researcher": "Research Assistant",
|
||||
"wizard.researcher_desc": "You are a research assistant that helps users find, analyze, and synthesize information from various sources.",
|
||||
"wizard.writer": "Writer",
|
||||
"wizard.writer_desc": "You are a skilled writer that helps with content creation, editing, and creative writing projects.",
|
||||
"wizard.data_analyst": "Data Analyst",
|
||||
"wizard.data_analyst_desc": "You are a data analyst that helps explore, analyze, and visualize data to extract insights.",
|
||||
"wizard.devops": "DevOps Engineer",
|
||||
"wizard.devops_desc": "You are a DevOps engineer that helps with infrastructure, deployment, CI/CD, and system administration.",
|
||||
"wizard.support": "Customer Support",
|
||||
"wizard.support_desc": "You are a customer support representative that helps resolve inquiries with patience and professionalism.",
|
||||
"wizard.tutor": "Tutor",
|
||||
"wizard.tutor_desc": "You are an educational tutor that explains concepts clearly and adapts teaching to the student's level.",
|
||||
"wizard.api_designer": "API Designer",
|
||||
"wizard.api_designer_desc": "You are an API designer that helps create well-structured, intuitive APIs following best practices.",
|
||||
"wizard.meeting_notes": "Meeting Notes",
|
||||
"wizard.meeting_notes_desc": "You are a meeting notes specialist that summarizes discussions, extracts action items, and tracks decisions.",
|
||||
|
||||
"wizard.step_welcome": "Welcome",
|
||||
"wizard.step_provider": "Provider",
|
||||
"wizard.step_agent": "Agent",
|
||||
"wizard.step_try_it": "Try It",
|
||||
"wizard.step_channel": "Channel",
|
||||
"wizard.step_done": "Done",
|
||||
|
||||
"wizard.cat_general": "General",
|
||||
"wizard.cat_development": "Development",
|
||||
"wizard.cat_research": "Research",
|
||||
"wizard.cat_writing": "Writing",
|
||||
"wizard.cat_business": "Business",
|
||||
|
||||
"wizard.channel_telegram": "Telegram",
|
||||
"wizard.channel_telegram_desc": "Connect your agent to a Telegram bot for messaging.",
|
||||
"wizard.channel_telegram_token": "Bot Token",
|
||||
"wizard.channel_telegram_help": "Create a bot via @BotFather on Telegram to get your token.",
|
||||
"wizard.channel_discord": "Discord",
|
||||
"wizard.channel_discord_desc": "Connect your agent to a Discord server via bot token.",
|
||||
"wizard.channel_discord_token": "Bot Token",
|
||||
"wizard.channel_discord_help": "Create a Discord application at discord.com/developers and add a bot.",
|
||||
"wizard.channel_slack": "Slack",
|
||||
"wizard.channel_slack_desc": "Connect your agent to a Slack workspace.",
|
||||
"wizard.channel_slack_token": "Bot Token",
|
||||
"wizard.channel_slack_help": "Create a Slack app at api.slack.com/apps and install it to your workspace.",
|
||||
|
||||
"wizard.profile_minimal": "Minimal",
|
||||
"wizard.profile_minimal_desc": "Read-only file access",
|
||||
"wizard.profile_coding": "Coding",
|
||||
"wizard.profile_coding_desc": "Files + shell + web fetch",
|
||||
"wizard.profile_research": "Research",
|
||||
"wizard.profile_research_desc": "Web search + file read/write",
|
||||
"wizard.profile_balanced": "Balanced",
|
||||
"wizard.profile_balanced_desc": "General-purpose tool set",
|
||||
"wizard.profile_precise": "Precise",
|
||||
"wizard.profile_precise_desc": "Focused tool set for accuracy",
|
||||
"wizard.profile_creative": "Creative",
|
||||
"wizard.profile_creative_desc": "Full tools with creative emphasis",
|
||||
"wizard.profile_full": "Full",
|
||||
"wizard.profile_full_desc": "All 35+ tools",
|
||||
|
||||
"wizard.enter_api_key": "Please enter an API key",
|
||||
"wizard.api_key_saved": "API key saved for",
|
||||
"wizard.failed_save_key": "Failed to save key:",
|
||||
"wizard.connected": "connected",
|
||||
"wizard.connection_failed": "Connection failed",
|
||||
"wizard.test_failed": "Test failed:",
|
||||
"wizard.enter_agent_name": "Please enter a name for your agent",
|
||||
"wizard.agent_created": "Agent created",
|
||||
"wizard.failed_create_agent": "Failed to create agent:",
|
||||
"wizard.enter_token": "Please enter the",
|
||||
"wizard.channel_configured": "configured and activated.",
|
||||
"wizard.failed_configure": "Failed:",
|
||||
|
||||
"wizard.suggestions.general.1": "What can you help me with?",
|
||||
"wizard.suggestions.general.2": "Tell me a fun fact",
|
||||
"wizard.suggestions.general.3": "Summarize the latest AI news",
|
||||
"wizard.suggestions.development.1": "Write a Python hello world",
|
||||
"wizard.suggestions.development.2": "Explain async/await",
|
||||
"wizard.suggestions.development.3": "Review this code snippet",
|
||||
"wizard.suggestions.research.1": "Explain quantum computing simply",
|
||||
"wizard.suggestions.research.2": "Compare React vs Vue",
|
||||
"wizard.suggestions.research.3": "What are the latest trends in AI?",
|
||||
"wizard.suggestions.writing.1": "Help me write a professional email",
|
||||
"wizard.suggestions.writing.2": "Improve this paragraph",
|
||||
"wizard.suggestions.writing.3": "Write a blog intro about AI",
|
||||
"wizard.suggestions.business.1": "Draft a meeting agenda",
|
||||
"wizard.suggestions.business.2": "How do I handle a complaint?",
|
||||
"wizard.suggestions.business.3": "Create a project status update",
|
||||
|
||||
"approvals.title": "Execution Approvals",
|
||||
"approvals.pending": "pending",
|
||||
"approvals.all": "All",
|
||||
"approvals.pending_tab": "Pending",
|
||||
"approvals.approved": "Approved",
|
||||
"approvals.rejected": "Rejected",
|
||||
"approvals.expired": "Expired",
|
||||
"approvals.no_approvals": "No approvals",
|
||||
"approvals.approve": "Approve",
|
||||
"approvals.reject": "Reject",
|
||||
|
||||
"workflows.title": "Workflows",
|
||||
"workflows.visual_builder": "Visual Builder",
|
||||
"workflows.what_are": "What are Workflows?",
|
||||
"workflows.no_workflows": "No workflows yet",
|
||||
"workflows.sequential": "Sequential",
|
||||
"workflows.fan_out": "Fan Out",
|
||||
"workflows.conditional": "Conditional",
|
||||
"workflows.loop": "Loop",
|
||||
"workflows.add_step": "+ Add Step",
|
||||
"workflows.execute": "Execute",
|
||||
"workflows.result": "Result",
|
||||
"workflows.node_palette": "Node Palette",
|
||||
"workflows.drag_nodes": "Drag nodes onto the canvas",
|
||||
"workflows.steps_connections": "steps, connections",
|
||||
"workflows.agent": "Agent",
|
||||
"workflows.prompt_template": "Prompt Template",
|
||||
"workflows.expression": "Expression",
|
||||
"workflows.top_port_true": "Top port = true, bottom port = false",
|
||||
"workflows.max_iterations": "Max Iterations",
|
||||
"workflows.until_stop": "Until (stop condition)",
|
||||
"workflows.fan_out_count": "Fan-out Count",
|
||||
"workflows.wait_all": "Wait for all",
|
||||
"workflows.first_finish": "First to finish",
|
||||
"workflows.majority_vote": "Majority vote",
|
||||
"workflows.connection_selected": "Connection selected",
|
||||
"workflows.delete_connection": "Delete Connection",
|
||||
|
||||
"scheduler.title": "Scheduler",
|
||||
"scheduler.scheduled_jobs": "Scheduled Jobs",
|
||||
"scheduler.event_triggers": "Event Triggers",
|
||||
"scheduler.run_history": "Run History",
|
||||
"scheduler.new_job": "+ New Job",
|
||||
"scheduler.job_name": "Job Name",
|
||||
"scheduler.cron_expression": "Cron Expression",
|
||||
"scheduler.quick_presets": "Quick Presets",
|
||||
"scheduler.target_agent": "Target Agent",
|
||||
"scheduler.any_agent": "Any available agent",
|
||||
"scheduler.message_send": "Message to Send",
|
||||
"scheduler.enabled": "Enabled (will start running immediately)",
|
||||
"scheduler.disabled": "Disabled (create paused)",
|
||||
"scheduler.active": "Active",
|
||||
"scheduler.paused": "Paused",
|
||||
"scheduler.cron_job": "Cron Job",
|
||||
"scheduler.trigger": "Trigger",
|
||||
"scheduler.no_jobs": "No scheduled jobs",
|
||||
"scheduler.no_triggers": "No event triggers",
|
||||
"scheduler.no_history": "No run history yet",
|
||||
|
||||
"channels.title": "Channels",
|
||||
"channels.configured": "configured",
|
||||
"channels.search": "Search channels...",
|
||||
"channels.setup": "Set up",
|
||||
"channels.edit": "Edit",
|
||||
"channels.configure": "Configure",
|
||||
"channels.verify": "Verify",
|
||||
"channels.ready": "Ready",
|
||||
"channels.is_ready": "is ready!",
|
||||
"channels.get_credentials": "How to get credentials",
|
||||
"channels.show_advanced": "Show advanced",
|
||||
"channels.hide_advanced": "Hide advanced",
|
||||
"channels.connecting": "Connecting to WhatsApp Web gateway...",
|
||||
"channels.linked_success": "WhatsApp linked successfully!",
|
||||
"channels.business_api": "Business API",
|
||||
|
||||
"skills.title": "Skills & Ecosystem",
|
||||
"skills.installed": "Installed",
|
||||
"skills.clawhub": "ClawHub",
|
||||
"skills.mcp_servers": "MCP Servers",
|
||||
"skills.quick_start": "Quick Start",
|
||||
"skills.no_installed": "No skills installed",
|
||||
"skills.browse_clawhub": "Browse ClawHub",
|
||||
"skills.search_clawhub": "Search ClawHub skills...",
|
||||
"skills.trending": "Trending",
|
||||
"skills.most_downloaded": "Most Downloaded",
|
||||
"skills.most_starred": "Most Starred",
|
||||
"skills.recently_updated": "Recently Updated",
|
||||
"skills.categories": "CATEGORIES",
|
||||
"skills.already_installed": "Already Installed",
|
||||
"skills.no_skills_found": "No skills found",
|
||||
"skills.security_warnings": "Security Warnings",
|
||||
"skills.security_scan": "Skills are security-scanned before installation",
|
||||
"skills.create": "Create Skill",
|
||||
"skills.created": "Created",
|
||||
|
||||
"skills.cat_coding": "Coding & IDEs",
|
||||
"skills.cat_git": "Git & GitHub",
|
||||
"skills.cat_frontend": "Web & Frontend",
|
||||
"skills.cat_devops": "DevOps & Cloud",
|
||||
"skills.cat_database": "Database",
|
||||
"skills.cat_security": "Security",
|
||||
"skills.cat_ai": "AI & ML",
|
||||
"skills.cat_data": "Data & Analytics",
|
||||
"skills.cat_mobile": "Mobile",
|
||||
"skills.cat_desktop": "Desktop Apps",
|
||||
"skills.cat_api": "API & Integrations",
|
||||
"skills.cat_testing": "Testing",
|
||||
"skills.cat_docs": "Documentation",
|
||||
"skills.cat_productivity": "Productivity",
|
||||
"skills.cat_other": "Other",
|
||||
|
||||
"skills.cat_browser": "Browser & Automation",
|
||||
"skills.cat_search": "Search & Research",
|
||||
"skills.cat_communication": "Communication",
|
||||
"skills.cat_media": "Media & Streaming",
|
||||
"skills.cat_notes": "Notes & PKM",
|
||||
"skills.cat_cli": "CLI Utilities",
|
||||
"skills.cat_marketing": "Marketing & Sales",
|
||||
"skills.cat_finance": "Finance",
|
||||
"skills.cat_smarthome": "Smart Home & IoT",
|
||||
|
||||
"skills.uninstall_skill": "Uninstall Skill",
|
||||
"skills.uninstall_confirm": "Uninstall skill",
|
||||
|
||||
"skills.source_clawhub": "ClawHub",
|
||||
"skills.source_openclaw": "OpenClaw",
|
||||
"skills.source_builtin": "Built-in",
|
||||
"skills.source_local": "Local",
|
||||
|
||||
"hands.title": "Hands — Curated Autonomous Capability Packages",
|
||||
"hands.available": "Available",
|
||||
"hands.active": "Active",
|
||||
"hands.ready": "Ready",
|
||||
"hands.setup_needed": "Setup needed",
|
||||
"hands.requirements": "REQUIREMENTS",
|
||||
"hands.details": "Details",
|
||||
"hands.no_hands": "No hands available",
|
||||
|
||||
"sessions.title": "Sessions",
|
||||
"sessions.memory": "Memory",
|
||||
"sessions.delete_session": "Delete Session",
|
||||
"sessions.delete_confirm": "This will permanently remove the session and its messages.",
|
||||
"sessions.delete_key": "Delete Key",
|
||||
"sessions.delete_key_confirm": "Delete key",
|
||||
|
||||
"logs.title": "Logs",
|
||||
"logs.live": "Live",
|
||||
"logs.audit_trail": "Audit Trail",
|
||||
|
||||
"settings.title": "Settings",
|
||||
"settings.providers": "Providers",
|
||||
"settings.models": "Models",
|
||||
"settings.config": "Config",
|
||||
"settings.tools": "Tools",
|
||||
"settings.migration": "Migration",
|
||||
"settings.security": "Security",
|
||||
"settings.network": "Network",
|
||||
"settings.migration": "Migration",
|
||||
"settings.language": "Language",
|
||||
|
||||
"settings.sec_path_traversal": "Path Traversal Prevention",
|
||||
"settings.sec_path_traversal_desc": "Blocks attempts to access files outside the workspace directory using .. or absolute paths.",
|
||||
"settings.sec_ssrf": "SSRF Protection",
|
||||
"settings.sec_ssrf_desc": "Prevents agents from making requests to internal IP ranges (localhost, cloud metadata, private networks).",
|
||||
"settings.sec_capability": "Capability-Based Access Control",
|
||||
"settings.sec_capability_desc": "Agents can only access explicitly granted capabilities. No implicit access to tools or data.",
|
||||
"settings.sec_taint": "Taint Tracking",
|
||||
"settings.sec_taint_desc": "Tracks untrusted data (user input, file content) through agent reasoning to prevent prompt injection.",
|
||||
"settings.sec_sandbox": "WASM Sandbox",
|
||||
"settings.sec_sandbox_desc": "Executes untrusted code in isolated WebAssembly sandboxes with memory and syscall restrictions.",
|
||||
"settings.sec_audit": "Merkle Audit",
|
||||
"settings.sec_audit_desc": "Maintains a verifiable audit log of all agent actions using Merkle tree cryptography.",
|
||||
"settings.sec_workspace": "Workspace Isolation",
|
||||
"settings.sec_workspace_desc": "Each agent has an isolated workspace directory. No cross-agent file access unless explicitly granted.",
|
||||
"settings.sec_rate_limit": "Rate Limiting",
|
||||
"settings.sec_rate_limit_desc": "Enforces per-agent and global rate limits to prevent resource exhaustion and cost overruns.",
|
||||
"settings.sec_approval": "Execution Approvals",
|
||||
"settings.sec_approval_desc": "Requires human approval for high-risk actions (shell commands, file writes, external requests).",
|
||||
|
||||
"settings.sec_enabled": "Enabled",
|
||||
"settings.sec_disabled": "Disabled",
|
||||
"settings.sec_inherited": "Inherited",
|
||||
"settings.sec_global": "Global"
|
||||
}
|
||||
230
crates/openfang-api/static/i18n/i18n.js
Normal file
230
crates/openfang-api/static/i18n/i18n.js
Normal file
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* OpenFang i18n (Internationalization) Module
|
||||
*
|
||||
* Provides runtime language switching for the OpenFang dashboard UI.
|
||||
* Supports English (default) and Russian.
|
||||
*
|
||||
* Usage:
|
||||
* - HTML: <span data-i18n="nav.overview">Overview</span>
|
||||
* - JS: window.t('nav.overview')
|
||||
* - Auto-applies translations on load based on stored/preferred language
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Language store
|
||||
let currentLang = 'en';
|
||||
let translations = {};
|
||||
let isInitialized = false;
|
||||
|
||||
/**
|
||||
* Load translations from a JSON file
|
||||
* @param {string} lang - Language code (en, ru)
|
||||
* @returns {Promise<Object>} Translation object
|
||||
*/
|
||||
async function loadTranslations(lang) {
|
||||
try {
|
||||
// Use cached translations if available
|
||||
if (window.__i18nCache && window.__i18nCache[lang]) {
|
||||
return window.__i18nCache[lang];
|
||||
}
|
||||
|
||||
const response = await fetch(`/i18n/${lang}.json`);
|
||||
if (!response.ok) {
|
||||
console.warn(`[i18n] Failed to load ${lang}.json, falling back to en`);
|
||||
if (lang !== 'en') {
|
||||
return loadTranslations('en');
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Cache for future use
|
||||
if (!window.__i18nCache) window.__i18nCache = {};
|
||||
window.__i18nCache[lang] = data;
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error(`[i18n] Error loading translations for ${lang}:`, error);
|
||||
if (lang !== 'en') {
|
||||
return loadTranslations('en');
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translated string by key
|
||||
* @param {string} key - Translation key (e.g., 'nav.overview')
|
||||
* @param {Object} params - Optional interpolation parameters
|
||||
* @returns {string} Translated string or key if not found
|
||||
*/
|
||||
function t(key, params) {
|
||||
if (!isInitialized) {
|
||||
console.warn('[i18n] Not initialized, returning key');
|
||||
return key;
|
||||
}
|
||||
|
||||
let text = translations[key] || key;
|
||||
|
||||
// Handle interpolation (e.g., 'Hello, {{name}}')
|
||||
if (params && typeof params === 'object') {
|
||||
Object.keys(params).forEach(param => {
|
||||
text = text.replace(new RegExp(`{{${param}}}`, 'g'), params[param]);
|
||||
});
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply translations to all elements with data-i18n attribute
|
||||
* Also updates the <html> lang attribute
|
||||
*/
|
||||
function applyTranslations() {
|
||||
// Update document language
|
||||
document.documentElement.lang = currentLang;
|
||||
|
||||
// Find and translate all elements with data-i18n attribute
|
||||
const elements = document.querySelectorAll('[data-i18n]');
|
||||
elements.forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
const translation = t(key);
|
||||
|
||||
// Check if element is a form input/textarea
|
||||
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
|
||||
// For form elements, only update if it's a placeholder or aria-label
|
||||
if (el.hasAttribute('placeholder')) {
|
||||
el.placeholder = translation;
|
||||
}
|
||||
if (el.hasAttribute('aria-label')) {
|
||||
el.setAttribute('aria-label', translation);
|
||||
}
|
||||
if (el.hasAttribute('title')) {
|
||||
el.setAttribute('title', translation);
|
||||
}
|
||||
} else {
|
||||
// For regular elements, update text content
|
||||
el.textContent = translation;
|
||||
}
|
||||
});
|
||||
|
||||
// Update elements with data-i18n-* attributes for attributes
|
||||
const attrElements = document.querySelectorAll('[data-i18n-placeholder], [data-i18n-title], [data-i18n-aria-label]');
|
||||
attrElements.forEach(el => {
|
||||
if (el.hasAttribute('data-i18n-placeholder')) {
|
||||
el.placeholder = t(el.getAttribute('data-i18n-placeholder'));
|
||||
}
|
||||
if (el.hasAttribute('data-i18n-title')) {
|
||||
el.title = t(el.getAttribute('data-i18n-title'));
|
||||
}
|
||||
if (el.hasAttribute('data-i18n-aria-label')) {
|
||||
el.setAttribute('aria-label', t(el.getAttribute('data-i18n-aria-label')));
|
||||
}
|
||||
});
|
||||
|
||||
// Update meta tags
|
||||
const metaDesc = document.querySelector('meta[name="description"]');
|
||||
if (metaDesc) {
|
||||
const desc = t('app.description', { name: 'OpenFang' });
|
||||
if (desc !== 'app.description') {
|
||||
metaDesc.content = desc;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[i18n] Applied translations for language: ${currentLang}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current language and apply translations
|
||||
* @param {string} lang - Language code (en, ru)
|
||||
* @param {boolean} persist - Whether to save to localStorage
|
||||
*/
|
||||
async function setLanguage(lang, persist = true) {
|
||||
if (!['en', 'ru'].includes(lang)) {
|
||||
console.warn(`[i18n] Unknown language: ${lang}, defaulting to en`);
|
||||
lang = 'en';
|
||||
}
|
||||
|
||||
currentLang = lang;
|
||||
translations = await loadTranslations(lang);
|
||||
isInitialized = true;
|
||||
|
||||
// Save preference
|
||||
if (persist) {
|
||||
localStorage.setItem('openfang_language', lang);
|
||||
}
|
||||
|
||||
// Apply to DOM
|
||||
applyTranslations();
|
||||
|
||||
// Dispatch event for Alpine.js components to react
|
||||
window.dispatchEvent(new CustomEvent('i18n:language-changed', {
|
||||
detail: { language: lang }
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current language
|
||||
* @returns {string} Current language code
|
||||
*/
|
||||
function getLanguage() {
|
||||
return currentLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize i18n system
|
||||
* Loads language preference and applies translations
|
||||
*/
|
||||
async function init() {
|
||||
// Determine language priority:
|
||||
// 1. localStorage (user preference)
|
||||
// 2. Browser language
|
||||
// 3. Default to English
|
||||
|
||||
let lang = localStorage.getItem('openfang_language');
|
||||
|
||||
if (!lang) {
|
||||
// Try to detect browser language
|
||||
const browserLang = navigator.language || navigator.userLanguage || '';
|
||||
if (browserLang.startsWith('ru')) {
|
||||
lang = 'ru';
|
||||
} else {
|
||||
lang = 'en';
|
||||
}
|
||||
}
|
||||
|
||||
await setLanguage(lang, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available languages
|
||||
* @returns {Array<{code: string, name: string}>}
|
||||
*/
|
||||
function getAvailableLanguages() {
|
||||
return [
|
||||
{ code: 'en', name: 'English' },
|
||||
{ code: 'ru', name: 'Русский' }
|
||||
];
|
||||
}
|
||||
|
||||
// Expose to global scope
|
||||
window.i18n = {
|
||||
t,
|
||||
setLanguage,
|
||||
getLanguage,
|
||||
getAvailableLanguages,
|
||||
init,
|
||||
isInitialized: () => isInitialized
|
||||
};
|
||||
|
||||
// Auto-initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
})();
|
||||
607
crates/openfang-api/static/i18n/ru.json
Normal file
607
crates/openfang-api/static/i18n/ru.json
Normal file
@@ -0,0 +1,607 @@
|
||||
{
|
||||
"app.name": "OpenFang",
|
||||
"app.version": "v",
|
||||
|
||||
"nav.chat": "Чат",
|
||||
"nav.monitor": "Мониторинг",
|
||||
"nav.overview": "Обзор",
|
||||
"nav.analytics": "Аналитика",
|
||||
"nav.logs": "Логи",
|
||||
"nav.agents": "Агенты",
|
||||
"nav.sessions": "Сессии",
|
||||
"nav.approvals": "Одобрения",
|
||||
"nav.comms": "Коммуникации",
|
||||
"nav.automation": "Автоматизация",
|
||||
"nav.workflows": "Рабочие процессы",
|
||||
"nav.scheduler": "Планировщик",
|
||||
"nav.extensions": "Расширения",
|
||||
"nav.channels": "Каналы",
|
||||
"nav.skills": "Навыки",
|
||||
"nav.hands": "Руки",
|
||||
"nav.system": "Система",
|
||||
"nav.runtime": "Среда выполнения",
|
||||
"nav.settings": "Настройки",
|
||||
|
||||
"auth.sign_in": "Войти",
|
||||
"auth.enter_credentials": "Введите учётные данные панели управления.",
|
||||
"auth.username": "Имя пользователя",
|
||||
"auth.password": "Пароль",
|
||||
"auth.api_key_required": "Требуется API-ключ",
|
||||
"auth.api_key_desc": "Этот экземпляр требует API-ключ. Введите ключ из вашего config.toml.",
|
||||
"auth.api_key_hint": "Добавьте api_key = \"your-key\" в начало ~/.openfang/config.toml (не внутри секции).",
|
||||
"auth.enter_api_key": "Введите API-ключ...",
|
||||
"auth.unlock_dashboard": "Разблокировать панель",
|
||||
"auth.login_failed": "Ошибка входа",
|
||||
|
||||
"status.agents_running": "агент(ов) запущено",
|
||||
"status.connecting": "Подключение...",
|
||||
"status.reconnecting": "Переподключение...",
|
||||
"status.disconnected": "отключено",
|
||||
"status.ws": "ВС",
|
||||
"status.http": "HTTP",
|
||||
"status.ready": "Готово",
|
||||
"status.loading": "Загрузка...",
|
||||
"status.loading_workflows": "Загрузка рабочих процессов...",
|
||||
"status.loading_channels": "Загрузка каналов...",
|
||||
"status.loading_skills": "Загрузка навыков...",
|
||||
"status.loading_jobs": "Загрузка заданий...",
|
||||
"status.loading_triggers": "Загрузка триггеров...",
|
||||
"status.loading_history": "Загрузка истории...",
|
||||
"status.loading_hands": "Загрузка модулей...",
|
||||
"status.loading_active_hands": "Загрузка активных модулей...",
|
||||
"status.loading_mcp": "Загрузка MCP-серверов...",
|
||||
"status.loading_files": "Загрузка файлов...",
|
||||
"status.loading_skills_details": "Загрузка деталей навыков...",
|
||||
"status.no_channels_match": "Нет каналов по запросу",
|
||||
|
||||
"actions.logout": "Выйти",
|
||||
"actions.new_agent": "Новый агент",
|
||||
"actions.browse_skills": "Навыки",
|
||||
"actions.add_channel": "Добавить канал",
|
||||
"actions.create_workflow": "Создать процесс",
|
||||
"actions.settings": "Настройки",
|
||||
"actions.create_agent": "Создать агента",
|
||||
"actions.configure_provider": "Настроить провайдера",
|
||||
"actions.cancel": "Отмена",
|
||||
"actions.confirm": "Подтвердить",
|
||||
"actions.save": "Сохранить",
|
||||
"actions.delete": "Удалить",
|
||||
"actions.edit": "Редактировать",
|
||||
"actions.clone": "Клонировать",
|
||||
"actions.stop": "Остановить",
|
||||
"actions.run": "Запустить",
|
||||
"actions.enable": "Включить",
|
||||
"actions.disable": "Отключить",
|
||||
"actions.view_all": "Показать все",
|
||||
"actions.retry": "Повторить",
|
||||
"actions.refresh": "Обновить",
|
||||
"actions.approve": "Одобрить",
|
||||
"actions.reject": "Отклонить",
|
||||
"actions.update": "Обновить",
|
||||
"actions.test_connection": "Проверить подключение",
|
||||
"actions.remove": "Удалить",
|
||||
"actions.save_test": "Сохранить и проверить",
|
||||
"actions.export_toml": "Экспорт TOML",
|
||||
"actions.save_workflow": "Сохранить процесс",
|
||||
"actions.auto_layout": "Автораскладка",
|
||||
"actions.clear": "Очистить",
|
||||
"actions.zoom_out": "Уменьшить",
|
||||
"actions.zoom_in": "Увеличить",
|
||||
"actions.fit": "По размеру",
|
||||
"actions.duplicate": "Дублировать",
|
||||
"actions.copy_clipboard": "Копировать в буфер",
|
||||
"actions.copied": "Скопировано!",
|
||||
"actions.copy": "Копировать",
|
||||
"actions.hide_code": "Скрыть код",
|
||||
"actions.view_code": "Показать код",
|
||||
"actions.install": "Установить",
|
||||
"actions.installing": "Установка...",
|
||||
"actions.installed": "Установлено",
|
||||
"actions.load_more": "Загрузить ещё",
|
||||
"actions.back_to_browse": "Назад",
|
||||
"actions.activate": "Активировать",
|
||||
"actions.create_schedule": "Создать расписание",
|
||||
"actions.submit": "Отправить",
|
||||
"actions.close": "Закрыть",
|
||||
"actions.next": "Далее",
|
||||
"actions.back": "Назад",
|
||||
"actions.spawn_agent": "Создать агента",
|
||||
"actions.spawning": "Создание...",
|
||||
"actions.spawn_wizard": "Мастер",
|
||||
"actions.raw_toml": "TOML",
|
||||
"actions.setup_wizard": "Мастер настройки",
|
||||
"actions.configure_manually": "Настроить вручную",
|
||||
"actions.dismiss": "Закрыть",
|
||||
"actions.create_workflow_btn": "Создать процесс",
|
||||
"actions.execute": "Выполнить",
|
||||
"actions.executing": "Выполнение...",
|
||||
"actions.generated_toml": "Сгенерированный TOML",
|
||||
"actions.running": "Выполняется...",
|
||||
|
||||
"footer.shortcuts": "Ctrl+K агенты | Ctrl+N новый",
|
||||
|
||||
"theme.light": "Светлая",
|
||||
"theme.system": "Системная",
|
||||
"theme.dark": "Тёмная",
|
||||
|
||||
"errors.connection_error": "Ошибка подключения",
|
||||
"errors.daemon_unreachable": "Не удаётся связаться с демоном — запущен ли openfang?",
|
||||
"errors.not_authorized": "Не авторизован — проверьте API-ключ",
|
||||
"errors.permission_denied": "Доступ запрещён",
|
||||
"errors.resource_not_found": "Ресурс не найден",
|
||||
"errors.rate_limited": "Превышен лимит — подождите и попробуйте снова",
|
||||
"errors.request_too_large": "Запрос слишком большой",
|
||||
"errors.server_error": "Ошибка сервера — проверьте логи демона",
|
||||
"errors.daemon_unavailable": "Демон недоступен — запущен ли он?",
|
||||
"errors.unexpected": "Неожиданная ошибка",
|
||||
"errors.reconnected": "Переподключено",
|
||||
"errors.connection_lost": "Соединение потеряно, переподключение...",
|
||||
"errors.switched_http": "Соединение потеряно — переход на режим HTTP",
|
||||
"errors.connection_lost": "Соединение потеряно, переподключение...",
|
||||
"errors.switched_http": "Соединение потеряно — переход на режим HTTP",
|
||||
"errors.reconnected": "Переподключено",
|
||||
|
||||
"toasts.approval_waiting": "Агент ожидает одобрения. Откройте раздел Одобрения.",
|
||||
"toasts.agent_created": "Агент создан",
|
||||
"toasts.agent_stopped": "Агент остановлен",
|
||||
"toasts.tool_used": "Инструмент использован",
|
||||
"toasts.tool_completed": "Инструмент завершён",
|
||||
"toasts.message_in": "Входящее сообщение",
|
||||
"toasts.response_sent": "Ответ отправлен",
|
||||
"toasts.session_reset": "Сессия сброшена",
|
||||
"toasts.compacted": "Сжато",
|
||||
"toasts.model_changed": "Модель изменена",
|
||||
"toasts.login_attempt": "Попытка входа",
|
||||
"toasts.login_ok": "Вход успешен",
|
||||
"toasts.login_failed": "Ошибка входа",
|
||||
"toasts.denied": "Отклонено",
|
||||
"toasts.rate_limited": "Лимит запросов",
|
||||
"toasts.workflow_run": "Запуск процесса",
|
||||
"toasts.trigger_fired": "Триггер сработал",
|
||||
"toasts.skill_installed": "Навык установлен",
|
||||
"toasts.mcp_connected": "MCP подключён",
|
||||
"toasts.session_deleted": "Сессия удалена",
|
||||
|
||||
"overview.welcome": "Добро пожаловать в OpenFang",
|
||||
"overview.getting_started": "Начало работы",
|
||||
"overview.setup_wizard": "Мастер настройки",
|
||||
"overview.steps_completed": "из 5 шагов выполнено",
|
||||
"overview.agents_running": "Агентов запущено",
|
||||
"overview.tokens_used": "Использовано токенов",
|
||||
"overview.total_cost": "Общая стоимость",
|
||||
"overview.uptime": "Время работы",
|
||||
"overview.channels": "Каналы",
|
||||
"overview.skills": "Навыки",
|
||||
"overview.mcp_servers": "MCP-серверы",
|
||||
"overview.tool_calls": "Вызовов инструментов",
|
||||
"overview.providers": "Провайдеры",
|
||||
"overview.recent_activity": "Недавняя активность",
|
||||
"overview.no_recent_activity": "Нет недавней активности",
|
||||
"overview.chat_with_agent": "Написать агенту",
|
||||
"overview.system_health": "Состояние системы",
|
||||
"overview.healthy": "Исправно",
|
||||
"overview.unreachable": "Недоступно",
|
||||
"overview.security_systems": "Системы безопасности",
|
||||
"overview.llm_providers": "LLM-провайдеры",
|
||||
"overview.defense_active": "9 уровней защиты активно",
|
||||
"overview.quick_actions": "Быстрые действия",
|
||||
|
||||
"setup.configure_provider": "Настройте LLM-провайдера",
|
||||
"setup.create_first_agent": "Создайте первого агента",
|
||||
"setup.send_first_message": "Отправьте первое сообщение",
|
||||
"setup.connect_channel": "Подключите канал связи",
|
||||
"setup.browse_install_skill": "Найдите или установите навык",
|
||||
|
||||
"tooltips.cooling_down": "остывает (лимит запросов)",
|
||||
"tooltips.circuit_open": "автомат сработал",
|
||||
"tooltips.ready": "готово",
|
||||
"tooltips.not_configured": "не настроено",
|
||||
|
||||
"chat.placeholder": "Напишите OpenFang... (/ для команд)",
|
||||
"chat.ready": "Готово",
|
||||
"chat.generating": "Генерация...",
|
||||
"chat.queued": "в очереди",
|
||||
"chat.sessions": "Сессии",
|
||||
"chat.new_session": "+ Новая",
|
||||
"chat.no_sessions": "Нет сессий",
|
||||
"chat.search_messages": "Поиск сообщений...",
|
||||
"chat.select_agent": "Выберите агента для начала общения",
|
||||
"chat.recording": "Запись... отпустите для отправки",
|
||||
"chat.drop_files": "Перетащите файлы сюда",
|
||||
"chat.attach_file": "Прикрепить файл",
|
||||
"chat.stop_generating": "Остановить генерацию",
|
||||
"chat.switch_model": "Сменить модель",
|
||||
"chat.search_models": "Поиск моделей...",
|
||||
"chat.no_models_found": "Модели не найдены",
|
||||
"chat.available_models": "Доступные модели — выберите или продолжите ввод",
|
||||
"chat.switching": "Переключение...",
|
||||
"chat.model_switched": "Модель изменена на",
|
||||
"chat.model_switch_failed": "Не удалось сменить модель",
|
||||
"chat.using_http_mode": "Используется HTTP режим (без потоковой передачи)",
|
||||
"chat.session_name_prompt": "Название сессии (необязательно):",
|
||||
"chat.session_created": "Сессия создана",
|
||||
"chat.session_create_failed": "Не удалось создать сессию",
|
||||
"chat.stop_agent_title": "Остановить агента",
|
||||
"chat.stop_agent_confirm": "Остановить агента",
|
||||
"chat.agent_stopped": "Агент остановлен",
|
||||
"chat.stop_agent_failed": "Не удалось остановить агента",
|
||||
"chat.welcome_message": "**Добро пожаловать в OpenFang Чат!**\n\n- Введите `/` для просмотра команд\n- `/help` покажет все команды\n- `/think on` включает расширенные размышления\n- `/context` покажет использование контекста\n- `/verbose off` скроет детали инструментов\n- `Ctrl+Shift+F` переключает режим фокуса\n- Перетащите файлы для прикрепления\n- `Ctrl+/` открывает палитру команд",
|
||||
|
||||
"chat.slash.help": "Показать доступные команды",
|
||||
"chat.slash.agents": "Перейти на страницу агентов",
|
||||
"chat.slash.new": "Новая сессия (очистить историю)",
|
||||
"chat.slash.compact": "Сжать контекст сессии",
|
||||
"chat.slash.model": "Показать или сменить модель (/model [имя])",
|
||||
"chat.slash.stop": "Отменить текущий запуск агента",
|
||||
"chat.slash.usage": "Показать использование токенов",
|
||||
"chat.slash.think": "Переключить размышления (/think [on|off|stream])",
|
||||
"chat.slash.context": "Показать использование контекста",
|
||||
"chat.slash.verbose": "Переключить детали инструментов (/verbose [off|on|full])",
|
||||
"chat.slash.queue": "Проверить очередь обработки",
|
||||
"chat.slash.status": "Показать статус системы",
|
||||
"chat.slash.clear": "Очистить чат",
|
||||
"chat.slash.exit": "Отключиться от агента",
|
||||
"chat.slash.budget": "Показать лимиты и расходы",
|
||||
"chat.slash.peers": "Показать статус сети OFP",
|
||||
"chat.slash.a2a": "Список A2A агентов",
|
||||
|
||||
"commands.help": "Показать доступные команды",
|
||||
"commands.agents": "Перейти на страницу агентов",
|
||||
"commands.new": "Новая сессия",
|
||||
"commands.switch": "Сменить агента",
|
||||
"commands.clear": "Очистить диалог",
|
||||
"commands.model": "Сменить модель",
|
||||
"commands.think": "Переключить режим размышлений",
|
||||
"commands.focus": "Переключить режим фокуса",
|
||||
"commands.theme": "Сменить тему",
|
||||
|
||||
"tips.commands": "Введите / для команд",
|
||||
"tips.think": "/think on для размышлений",
|
||||
"tips.focus": "Ctrl+Shift+F для режима фокуса",
|
||||
|
||||
"agents.info": "Информация",
|
||||
"agents.files": "Файлы",
|
||||
"agents.config": "Настройки",
|
||||
"agents.chat": "Чат",
|
||||
"agents.clone": "Клонировать",
|
||||
"agents.clear_history": "Очистить историю",
|
||||
"agents.change": "Изменить",
|
||||
"agents.none_fallback": "Нет — добавить цепочку резервов",
|
||||
"agents.add": "+ Добавить",
|
||||
"agents.loading_files": "Загрузка файлов...",
|
||||
"agents.no_workspace_files": "Файлы рабочей области не найдены",
|
||||
"agents.save_config": "Сохранить настройки",
|
||||
"agents.tool_filters": "Фильтры инструментов",
|
||||
"agents.allowlist": "Белый список",
|
||||
"agents.blocklist": "Чёрный список",
|
||||
"agents.agent_name": "Имя агента",
|
||||
"agents.emoji": "Эмодзи",
|
||||
"agents.color": "Цвет",
|
||||
"agents.archetype": "Архетип",
|
||||
"agents.provider": "Провайдер",
|
||||
"agents.model": "Модель",
|
||||
"agents.system_prompt": "Системный промпт",
|
||||
"agents.soul_persona": "Душa / Персона",
|
||||
"agents.tool_profile": "Профиль инструментов",
|
||||
"agents.minimal_profile": "Минимальный — только чтение файлов",
|
||||
"agents.coding_profile": "Кодинг — файлы + оболочка + веб-запросы",
|
||||
"agents.fullstack_profile": "Full-Stack — файлы + оболочка + веб + поиск",
|
||||
"agents.research_profile": "Исследование — веб + поиск + анализ",
|
||||
"agents.admin_profile": "Админ — полный доступ к системе (опасно)",
|
||||
"agents.agent_created": "Агент создан",
|
||||
"agents.agent_stopped": "Агент остановлен",
|
||||
"agents.agent_deleted": "Агент удалён",
|
||||
|
||||
"presets.professional": "Деловой",
|
||||
"presets.professional_desc": "Точный, бизнес-ориентированный ассистент, сосредоточенный на эффективности и ясности. Приоритет — практические выводы и структурированная коммуникация.",
|
||||
"presets.professional_soul": "Общайтесь чётко и профессионально. Будьте прямым и структурированным. Используйте формальный язык и выводы на основе данных. ставьте точность выше личности.",
|
||||
"presets.friendly": "Дружелюбный",
|
||||
"presets.friendly_desc": "Тёплый и открытый ассистент, который выстраивает rapport и использует разговорный язык. Отлично подходит для мозгового штурма и исследования.",
|
||||
"presets.friendly_soul": "Будьте тёплым, доступным и разговорчивым. Используйте неформальный язык и проявляйте искренний интерес к пользователю. Добавляйте личность к вашим ответам, оставаясь полезным.",
|
||||
"presets.technical": "Технический",
|
||||
"presets.technical_desc": "Эксперт-помощник по разработке, оптимизированный для кода, архитектуры и технических задач. Точная терминология, глубокие погружения, бенчмарки.",
|
||||
"presets.technical_soul": "Сосредоточьтесь на технической точности и глубине. Используйте точную терминологию. Покажите вашу работу и рассуждения. Предпочитайте примеры кода и структурированные объяснения.",
|
||||
"presets.creative": "Креативный",
|
||||
"presets.creative_desc": "Творческий партнёр для создания контента, дизайн-мышления и нестандартных решений. Приветствует неоднозначность и исследует возможности.",
|
||||
"presets.creative_soul": "Будьте изобретательным и выразительным. Используйте яркий язык, аналогии и неожиданные связи. Поощряйте творческое мышление и исследуйте различные перспективы.",
|
||||
"presets.concise": "Краткий",
|
||||
"presets.concise_desc": "Минималистичный и прямой ассистент, который ценит ваше время. Убирает лишнее и даёт сфокусированные, практичные ответы.",
|
||||
"presets.concise_soul": "Будьте предельно кратким и точным. Без воды и формальностей. Отвечайте наименьшим количеством слов, оставаясь точным и полным.",
|
||||
"presets.mentor": "Наставник",
|
||||
"presets.mentor_desc": "Терпеливый педагог, который подробно объясняет концепции, даёт контекст и направляет обучение. Метод Сократа при необходимости.",
|
||||
"presets.mentor_soul": "Будьте терпеливым и ободряющим как хороший учитель. Разбивайте сложные темы по шагам. Задавайте направляющие вопросы. Празднуйте прогресс и укрепляйте уверенность.",
|
||||
|
||||
"agents.profile.minimal": "Минимальный",
|
||||
"agents.profile.minimal_desc": "Только чтение файлов",
|
||||
"agents.profile.coding": "Кодинг",
|
||||
"agents.profile.coding_desc": "Файлы + оболочка + веб-запросы",
|
||||
"agents.profile.research": "Исследование",
|
||||
"agents.profile.research_desc": "Веб-поиск + чтение/запись файлов",
|
||||
"agents.profile.messaging": "Коммуникации",
|
||||
"agents.profile.messaging_desc": "Агенты + доступ к памяти",
|
||||
"agents.profile.automation": "Автоматизация",
|
||||
"agents.profile.automation_desc": "Все инструменты кроме пользовательских",
|
||||
"agents.profile.balanced": "Сбалансированный",
|
||||
"agents.profile.balanced_desc": "Набор инструментов общего назначения",
|
||||
"agents.profile.precise": "Точный",
|
||||
"agents.profile.precise_desc": "Фокусированный набор инструментов для точности",
|
||||
"agents.profile.creative": "Креативный",
|
||||
"agents.profile.creative_desc": "Полный набор инструментов с творческим уклоном",
|
||||
"agents.profile.full": "Полный",
|
||||
"agents.profile.full_desc": "Все 35+ инструментов",
|
||||
|
||||
"wizard.general_assistant": "Универсальный ассистент",
|
||||
"wizard.general_assistant_desc": "Вы универсальный AI-ассистент, который помогает пользователям с широким кругом задач. Вы знающий, полезный и способны адаптироваться к потребностям пользователя.",
|
||||
"wizard.code_helper": "Помощник по коду",
|
||||
"wizard.code_helper_desc": "Вы опытный программный ассистент, специализирующийся на разработке ПО. Вы помогаете писать, отлаживать и рефакторить код на разных языках.",
|
||||
"wizard.researcher": "Исследовательский ассистент",
|
||||
"wizard.researcher_desc": "Вы исследовательский ассистент, который помогает находить, анализировать и синтезировать информацию из различных источников.",
|
||||
"wizard.writer": "Писатель",
|
||||
"wizard.writer_desc": "Вы квалифицированный писатель, который помогает с созданием контента, редактированием и творческими проектами.",
|
||||
"wizard.data_analyst": "Аналитик данных",
|
||||
"wizard.data_analyst_desc": "Вы аналитик данных, который помогает исследовать, анализировать и визуализировать данные для извлечения инсайтов.",
|
||||
"wizard.devops": "DevOps-инженер",
|
||||
"wizard.devops_desc": "Вы DevOps-инженер, который помогает с инфраструктурой, деплоем, CI/CD и системным администрированием.",
|
||||
"wizard.support": "Поддержка клиентов",
|
||||
"wizard.support_desc": "Вы представитель поддержки клиентов, который помогает решать вопросы с терпением и профессионализмом.",
|
||||
"wizard.tutor": "Репетитор",
|
||||
"wizard.tutor_desc": "Вы образовательный репетитор, который ясно объясняет концепции и адаптирует обучение к уровню ученика.",
|
||||
"wizard.api_designer": "API-дизайнер",
|
||||
"wizard.api_designer_desc": "Вы дизайнер API, который помогает создавать хорошо структурированные, интуитивные API по лучшим практикам.",
|
||||
"wizard.meeting_notes": "Заметки к встрече",
|
||||
"wizard.meeting_notes_desc": "Вы специалист по заметкам встреч, который суммирует обсуждения, извлекает задачи и отслеживает решения.",
|
||||
|
||||
"wizard.step_welcome": "Приветствие",
|
||||
"wizard.step_provider": "Провайдер",
|
||||
"wizard.step_agent": "Агент",
|
||||
"wizard.step_try_it": "Попробовать",
|
||||
"wizard.step_channel": "Канал",
|
||||
"wizard.step_done": "Готово",
|
||||
|
||||
"wizard.cat_general": "Общее",
|
||||
"wizard.cat_development": "Разработка",
|
||||
"wizard.cat_research": "Исследования",
|
||||
"wizard.cat_writing": "Написание",
|
||||
"wizard.cat_business": "Бизнес",
|
||||
|
||||
"wizard.channel_telegram": "Telegram",
|
||||
"wizard.channel_telegram_desc": "Подключите агента к Telegram-боту для обмена сообщениями.",
|
||||
"wizard.channel_telegram_token": "Токен бота",
|
||||
"wizard.channel_telegram_help": "Создайте бота через @BotFather в Telegram, чтобы получить токен.",
|
||||
"wizard.channel_discord": "Discord",
|
||||
"wizard.channel_discord_desc": "Подключите агента к Discord-серверу через токен бота.",
|
||||
"wizard.channel_discord_token": "Токен бота",
|
||||
"wizard.channel_discord_help": "Создайте приложение Discord на discord.com/developers и добавьте бота.",
|
||||
"wizard.channel_slack": "Slack",
|
||||
"wizard.channel_slack_desc": "Подключите агента к рабочему пространству Slack.",
|
||||
"wizard.channel_slack_token": "Токен бота",
|
||||
"wizard.channel_slack_help": "Создайте приложение Slack на api.slack.com/apps и установите его в рабочее пространство.",
|
||||
|
||||
"wizard.profile_minimal": "Минимальный",
|
||||
"wizard.profile_minimal_desc": "Только чтение файлов",
|
||||
"wizard.profile_coding": "Кодинг",
|
||||
"wizard.profile_coding_desc": "Файлы + оболочка + веб-запросы",
|
||||
"wizard.profile_research": "Исследование",
|
||||
"wizard.profile_research_desc": "Веб-поиск + чтение/запись файлов",
|
||||
"wizard.profile_balanced": "Сбалансированный",
|
||||
"wizard.profile_balanced_desc": "Набор инструментов общего назначения",
|
||||
"wizard.profile_precise": "Точный",
|
||||
"wizard.profile_precise_desc": "Фокусированный набор инструментов для точности",
|
||||
"wizard.profile_creative": "Креативный",
|
||||
"wizard.profile_creative_desc": "Полный набор инструментов с творческим уклоном",
|
||||
"wizard.profile_full": "Полный",
|
||||
"wizard.profile_full_desc": "Все 35+ инструментов",
|
||||
|
||||
"wizard.enter_api_key": "Пожалуйста, введите API-ключ",
|
||||
"wizard.api_key_saved": "API-ключ сохранён для",
|
||||
"wizard.failed_save_key": "Не удалось сохранить ключ:",
|
||||
"wizard.connected": "подключён",
|
||||
"wizard.connection_failed": "Ошибка подключения",
|
||||
"wizard.test_failed": "Тест не прошёл:",
|
||||
"wizard.enter_agent_name": "Пожалуйста, введите имя агента",
|
||||
"wizard.agent_created": "Агент создан",
|
||||
"wizard.failed_create_agent": "Не удалось создать агента:",
|
||||
"wizard.enter_token": "Пожалуйста, введите",
|
||||
"wizard.channel_configured": "настроен и активирован.",
|
||||
"wizard.failed_configure": "Ошибка:",
|
||||
|
||||
"wizard.suggestions.general.1": "Чем вы можете помочь?",
|
||||
"wizard.suggestions.general.2": "Расскажите интересный факт",
|
||||
"wizard.suggestions.general.3": "Резюмируйте последние новости AI",
|
||||
"wizard.suggestions.development.1": "Напишите Python hello world",
|
||||
"wizard.suggestions.development.2": "Объясните async/await",
|
||||
"wizard.suggestions.development.3": "Проверьте этот фрагмент кода",
|
||||
"wizard.suggestions.research.1": "Объясните квантовые вычисления просто",
|
||||
"wizard.suggestions.research.2": "Сравните React и Vue",
|
||||
"wizard.suggestions.research.3": "Какие последние тренды в AI?",
|
||||
"wizard.suggestions.writing.1": "Помогите написать профессиональное письмо",
|
||||
"wizard.suggestions.writing.2": "Улучшите этот абзац",
|
||||
"wizard.suggestions.writing.3": "Напишите введение в блог об AI",
|
||||
"wizard.suggestions.business.1": "Составьте повестку встречи",
|
||||
"wizard.suggestions.business.2": "Как обработать жалобу?",
|
||||
"wizard.suggestions.business.3": "Создайте статус-отчёт проекта",
|
||||
|
||||
"approvals.title": "Одобрения выполнения",
|
||||
"approvals.pending": "ожидает",
|
||||
"approvals.all": "Все",
|
||||
"approvals.pending_tab": "Ожидающие",
|
||||
"approvals.approved": "Одобрено",
|
||||
"approvals.rejected": "Отклонено",
|
||||
"approvals.expired": "Истекло",
|
||||
"approvals.no_approvals": "Нет одобрений",
|
||||
"approvals.approve": "Одобрить",
|
||||
"approvals.reject": "Отклонить",
|
||||
|
||||
"workflows.title": "Рабочие процессы",
|
||||
"workflows.visual_builder": "Визуальный конструктор",
|
||||
"workflows.what_are": "Что такое рабочие процессы?",
|
||||
"workflows.no_workflows": "Нет рабочих процессов",
|
||||
"workflows.sequential": "Последовательный",
|
||||
"workflows.fan_out": "Распределение",
|
||||
"workflows.conditional": "Условный",
|
||||
"workflows.loop": "Цикл",
|
||||
"workflows.add_step": "+ Добавить шаг",
|
||||
"workflows.execute": "Выполнить",
|
||||
"workflows.result": "Результат",
|
||||
"workflows.node_palette": "Палитра узлов",
|
||||
"workflows.drag_nodes": "Перетащите узлы на холст",
|
||||
"workflows.steps_connections": "шагов, связей",
|
||||
"workflows.agent": "Агент",
|
||||
"workflows.prompt_template": "Шаблон промпта",
|
||||
"workflows.expression": "Выражение",
|
||||
"workflows.top_port_true": "Верхний порт = истина, нижний = ложь",
|
||||
"workflows.max_iterations": "Макс. итераций",
|
||||
"workflows.until_stop": "До (условие остановки)",
|
||||
"workflows.fan_out_count": "Количество ветвей",
|
||||
"workflows.wait_all": "Ждать все",
|
||||
"workflows.first_finish": "Первый завершился",
|
||||
"workflows.majority_vote": "Большинство",
|
||||
"workflows.connection_selected": "Связь выбрана",
|
||||
"workflows.delete_connection": "Удалить связь",
|
||||
|
||||
"scheduler.title": "Планировщик",
|
||||
"scheduler.scheduled_jobs": "Запланированные задания",
|
||||
"scheduler.event_triggers": "Триггеры событий",
|
||||
"scheduler.run_history": "История запусков",
|
||||
"scheduler.new_job": "+ Новое задание",
|
||||
"scheduler.job_name": "Название задания",
|
||||
"scheduler.cron_expression": "Cron-выражение",
|
||||
"scheduler.quick_presets": "Быстрые шаблоны",
|
||||
"scheduler.target_agent": "Целевой агент",
|
||||
"scheduler.any_agent": "Любой доступный агент",
|
||||
"scheduler.message_send": "Сообщение для отправки",
|
||||
"scheduler.enabled": "Включено (запустится сразу)",
|
||||
"scheduler.disabled": "Отключено (создать приостановленным)",
|
||||
"scheduler.active": "Активно",
|
||||
"scheduler.paused": "Приостановлено",
|
||||
"scheduler.cron_job": "Cron-задание",
|
||||
"scheduler.trigger": "Триггер",
|
||||
"scheduler.no_jobs": "Нет запланированных заданий",
|
||||
"scheduler.no_triggers": "Нет триггеров событий",
|
||||
"scheduler.no_history": "Нет истории запусков",
|
||||
|
||||
"channels.title": "Каналы",
|
||||
"channels.configured": "настроено",
|
||||
"channels.search": "Поиск каналов...",
|
||||
"channels.setup": "Настроить",
|
||||
"channels.edit": "Изменить",
|
||||
"channels.configure": "Настройка",
|
||||
"channels.verify": "Проверить",
|
||||
"channels.ready": "Готово",
|
||||
"channels.is_ready": "готово!",
|
||||
"channels.get_credentials": "Как получить учётные данные",
|
||||
"channels.show_advanced": "Показать расширенные",
|
||||
"channels.hide_advanced": "Скрыть расширенные",
|
||||
"channels.connecting": "Подключение к шлюзу WhatsApp Web...",
|
||||
"channels.linked_success": "WhatsApp успешно связан!",
|
||||
"channels.business_api": "Business API",
|
||||
|
||||
"skills.title": "Навыки и экосистема",
|
||||
"skills.installed": "Установленные",
|
||||
"skills.clawhub": "ClawHub",
|
||||
"skills.mcp_servers": "MCP-серверы",
|
||||
"skills.quick_start": "Быстрый старт",
|
||||
"skills.no_installed": "Нет установленных навыков",
|
||||
"skills.browse_clawhub": "Обзор ClawHub",
|
||||
"skills.search_clawhub": "Поиск навыков ClawHub...",
|
||||
"skills.trending": "Популярные",
|
||||
"skills.most_downloaded": "Самые скачиваемые",
|
||||
"skills.most_starred": "Самые оценённые",
|
||||
"skills.recently_updated": "Недавно обновлённые",
|
||||
"skills.categories": "КАТЕГОРИИ",
|
||||
"skills.already_installed": "Уже установлено",
|
||||
"skills.no_skills_found": "Навыки не найдены",
|
||||
"skills.security_warnings": "Предупреждения безопасности",
|
||||
"skills.security_scan": "Навыки проверяются на безопасность перед установкой",
|
||||
"skills.create": "Создать навык",
|
||||
"skills.created": "Создан",
|
||||
|
||||
"skills.cat_coding": "Кодинг и IDE",
|
||||
"skills.cat_git": "Git и GitHub",
|
||||
"skills.cat_frontend": "Веб и фронтенд",
|
||||
"skills.cat_devops": "DevOps и облака",
|
||||
"skills.cat_database": "Базы данных",
|
||||
"skills.cat_security": "Безопасность",
|
||||
"skills.cat_ai": "AI и ML",
|
||||
"skills.cat_data": "Данные и аналитика",
|
||||
"skills.cat_mobile": "Мобильная разработка",
|
||||
"skills.cat_desktop": "Десктопные приложения",
|
||||
"skills.cat_api": "API и интеграции",
|
||||
"skills.cat_testing": "Тестирование",
|
||||
"skills.cat_docs": "Документация",
|
||||
"skills.cat_productivity": "Продуктивность",
|
||||
"skills.cat_other": "Другое",
|
||||
|
||||
"skills.cat_browser": "Браузер и автоматизация",
|
||||
"skills.cat_search": "Поиск и исследования",
|
||||
"skills.cat_communication": "Коммуникации",
|
||||
"skills.cat_media": "Медиа и стриминг",
|
||||
"skills.cat_notes": "Заметки и PKM",
|
||||
"skills.cat_cli": "CLI утилиты",
|
||||
"skills.cat_marketing": "Маркетинг и продажи",
|
||||
"skills.cat_finance": "Финансы",
|
||||
"skills.cat_smarthome": "Умный дом и IoT",
|
||||
|
||||
"skills.uninstall_skill": "Удалить навык",
|
||||
"skills.uninstall_confirm": "Удалить навык",
|
||||
|
||||
"skills.source_clawhub": "ClawHub",
|
||||
"skills.source_openclaw": "OpenClaw",
|
||||
"skills.source_builtin": "Встроенный",
|
||||
"skills.source_local": "Локальный",
|
||||
|
||||
"hands.title": "Руки — Наборы автономных возможностей",
|
||||
"hands.available": "Доступные",
|
||||
"hands.active": "Активные",
|
||||
"hands.ready": "Готово",
|
||||
"hands.setup_needed": "Требуется настройка",
|
||||
"hands.requirements": "ТРЕБОВАНИЯ",
|
||||
"hands.details": "Подробности",
|
||||
"hands.no_hands": "Нет доступных модулей",
|
||||
|
||||
"sessions.title": "Сессии",
|
||||
"sessions.memory": "Память",
|
||||
"sessions.delete_session": "Удалить сессию",
|
||||
"sessions.delete_confirm": "Это навсегда удалит сессию и все её сообщения.",
|
||||
"sessions.delete_key": "Удалить ключ",
|
||||
"sessions.delete_key_confirm": "Удалить ключ",
|
||||
|
||||
"logs.title": "Логи",
|
||||
"logs.live": "Онлайн",
|
||||
"logs.audit_trail": "Аудит",
|
||||
|
||||
"settings.title": "Настройки",
|
||||
"settings.providers": "Провайдеры",
|
||||
"settings.models": "Модели",
|
||||
"settings.config": "Конфигурация",
|
||||
"settings.tools": "Инструменты",
|
||||
"settings.migration": "Миграция",
|
||||
"settings.security": "Безопасность",
|
||||
"settings.network": "Сеть",
|
||||
"settings.migration": "Миграция",
|
||||
"settings.language": "Язык",
|
||||
|
||||
"settings.sec_path_traversal": "Защита от обхода пути",
|
||||
"settings.sec_path_traversal_desc": "Блокирует попытки доступа к файлам за пределами рабочей директории через .. или абсолютные пути.",
|
||||
"settings.sec_ssrf": "Защита от SSRF",
|
||||
"settings.sec_ssrf_desc": "Предотвращает запросы агентов к внутренним IP-диапазонам (localhost, облачный метаданные, частные сети).",
|
||||
"settings.sec_capability": "Управление доступом по возможностям",
|
||||
"settings.sec_capability_desc": "Агенты могут получать доступ только к явно предоставленным возможностям. Нет неявного доступа к инструментам или данным.",
|
||||
"settings.sec_taint": "Отслеживание заражения",
|
||||
"settings.sec_taint_desc": "Отслеживает ненадёжные данные (ввод пользователя, содержимое файлов) через рассуждения агента для предотвращения инъекции промпта.",
|
||||
"settings.sec_sandbox": "WASM-песочница",
|
||||
"settings.sec_sandbox_desc": "Выполняет ненадёжный код в изолированных WebAssembly-песочницах с ограничениями памяти и системных вызовов.",
|
||||
"settings.sec_audit": "Меркл-проверка",
|
||||
"settings.sec_audit_desc": "Ведёт верифицируемый журнал аудита всех действий агентов с использованием криптографии деревьев Меркла.",
|
||||
"settings.sec_workspace": "Изоляция рабочих областей",
|
||||
"settings.sec_workspace_desc": "Каждый агент имеет изолированную рабочую директорию. Нет межагентного доступа к файлам без явного разрешения.",
|
||||
"settings.sec_rate_limit": "Ограничение частоты",
|
||||
"settings.sec_rate_limit_desc": "Устанавливает лимиты на запросы для каждого агента и глобально для предотвращения истощения ресурсов и перерасхода.",
|
||||
"settings.sec_approval": "Одобрения выполнения",
|
||||
"settings.sec_approval_desc": "Требует одобрения человека для рискованных действий (команды оболочки, запись файлов, внешние запросы).",
|
||||
|
||||
"settings.sec_enabled": "Включено",
|
||||
"settings.sec_disabled": "Отключено",
|
||||
"settings.sec_inherited": "Унаследовано",
|
||||
"settings.sec_global": "Глобально"
|
||||
}
|
||||
@@ -53,14 +53,23 @@ function agentsPage() {
|
||||
'\u{2764}\uFE0F', '\u{1F31F}', '\u{1F527}', '\u{1F4DD}', '\u{1F4A1}', '\u{1F3A8}'
|
||||
],
|
||||
archetypeOptions: ['Assistant', 'Researcher', 'Coder', 'Writer', 'DevOps', 'Support', 'Analyst', 'Custom'],
|
||||
personalityPresets: [
|
||||
{ id: 'professional', label: 'Professional', soul: 'Communicate in a clear, professional tone. Be direct and structured. Use formal language and data-driven reasoning. Prioritize accuracy over personality.' },
|
||||
{ id: 'friendly', label: 'Friendly', soul: 'Be warm, approachable, and conversational. Use casual language and show genuine interest in the user. Add personality to your responses while staying helpful.' },
|
||||
{ id: 'technical', label: 'Technical', soul: 'Focus on technical accuracy and depth. Use precise terminology. Show your work and reasoning. Prefer code examples and structured explanations.' },
|
||||
{ id: 'creative', label: 'Creative', soul: 'Be imaginative and expressive. Use vivid language, analogies, and unexpected connections. Encourage creative thinking and explore multiple perspectives.' },
|
||||
{ id: 'concise', label: 'Concise', soul: 'Be extremely brief and to the point. No filler, no pleasantries. Answer in the fewest words possible while remaining accurate and complete.' },
|
||||
{ id: 'mentor', label: 'Mentor', soul: 'Be patient and encouraging like a great teacher. Break down complex topics step by step. Ask guiding questions. Celebrate progress and build confidence.' }
|
||||
],
|
||||
_personalityPresetsLoaded: false,
|
||||
personalityPresets: [], // Loaded dynamically with i18n
|
||||
|
||||
// Load personality presets with i18n
|
||||
loadPersonalityPresets: function() {
|
||||
if (this._personalityPresetsLoaded) return;
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
this.personalityPresets = [
|
||||
{ id: 'professional', label: t('presets.professional'), soul: t('presets.professional_soul') },
|
||||
{ id: 'friendly', label: t('presets.friendly'), soul: t('presets.friendly_soul') },
|
||||
{ id: 'technical', label: t('presets.technical'), soul: t('presets.technical_soul') },
|
||||
{ id: 'creative', label: t('presets.creative'), soul: t('presets.creative_soul') },
|
||||
{ id: 'concise', label: t('presets.concise'), soul: t('presets.concise_soul') },
|
||||
{ id: 'mentor', label: t('presets.mentor'), soul: t('presets.mentor_soul') }
|
||||
];
|
||||
this._personalityPresetsLoaded = true;
|
||||
},
|
||||
|
||||
// -- Detail modal tabs --
|
||||
detailTab: 'info',
|
||||
@@ -99,21 +108,31 @@ function agentsPage() {
|
||||
// Load templates from API
|
||||
async init() {
|
||||
await this.loadTemplates();
|
||||
// Load personality presets with i18n
|
||||
this.loadPersonalityPresets();
|
||||
},
|
||||
|
||||
// ── Profile Descriptions ──
|
||||
profileDescriptions: {
|
||||
minimal: { label: 'Minimal', desc: 'Read-only file access' },
|
||||
coding: { label: 'Coding', desc: 'Files + shell + web fetch' },
|
||||
research: { label: 'Research', desc: 'Web search + file read/write' },
|
||||
messaging: { label: 'Messaging', desc: 'Agents + memory access' },
|
||||
automation: { label: 'Automation', desc: 'All tools except custom' },
|
||||
balanced: { label: 'Balanced', desc: 'General-purpose tool set' },
|
||||
precise: { label: 'Precise', desc: 'Focused tool set for accuracy' },
|
||||
creative: { label: 'Creative', desc: 'Full tools with creative emphasis' },
|
||||
full: { label: 'Full', desc: 'All 35+ tools' }
|
||||
// ── Profile Descriptions (loaded dynamically with i18n) ──
|
||||
_profileDescriptionsLoaded: false,
|
||||
profileDescriptions: {},
|
||||
loadProfileDescriptions: function() {
|
||||
if (this._profileDescriptionsLoaded) return;
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
this.profileDescriptions = {
|
||||
minimal: { label: t('agents.profile.minimal'), desc: t('agents.profile.minimal_desc') },
|
||||
coding: { label: t('agents.profile.coding'), desc: t('agents.profile.coding_desc') },
|
||||
research: { label: t('agents.profile.research'), desc: t('agents.profile.research_desc') },
|
||||
messaging: { label: t('agents.profile.messaging'), desc: t('agents.profile.messaging_desc') },
|
||||
automation: { label: t('agents.profile.automation'), desc: t('agents.profile.automation_desc') },
|
||||
balanced: { label: t('agents.profile.balanced'), desc: t('agents.profile.balanced_desc') },
|
||||
precise: { label: t('agents.profile.precise'), desc: t('agents.profile.precise_desc') },
|
||||
creative: { label: t('agents.profile.creative'), desc: t('agents.profile.creative_desc') },
|
||||
full: { label: t('agents.profile.full'), desc: t('agents.profile.full_desc') }
|
||||
};
|
||||
this._profileDescriptionsLoaded = true;
|
||||
},
|
||||
profileInfo: function(name) {
|
||||
this.loadProfileDescriptions();
|
||||
return this.profileDescriptions[name] || { label: name, desc: '' };
|
||||
},
|
||||
|
||||
@@ -197,6 +216,8 @@ function agentsPage() {
|
||||
try {
|
||||
await Alpine.store('app').refreshAgents();
|
||||
await this.loadTemplates();
|
||||
this.loadPersonalityPresets();
|
||||
this.loadProfileDescriptions();
|
||||
} catch(e) {
|
||||
this.loadError = e.message || 'Could not load agents. Is the daemon running?';
|
||||
}
|
||||
|
||||
@@ -42,34 +42,31 @@ function chatPage() {
|
||||
modelSwitching: false,
|
||||
_modelCache: null,
|
||||
_modelCacheTime: 0,
|
||||
slashCommands: [
|
||||
{ cmd: '/help', desc: 'Show available commands' },
|
||||
{ cmd: '/agents', desc: 'Switch to Agents page' },
|
||||
{ cmd: '/new', desc: 'Reset session (clear history)' },
|
||||
{ cmd: '/compact', desc: 'Trigger LLM session compaction' },
|
||||
{ cmd: '/model', desc: 'Show or switch model (/model [name])' },
|
||||
{ cmd: '/stop', desc: 'Cancel current agent run' },
|
||||
{ cmd: '/usage', desc: 'Show session token usage & cost' },
|
||||
{ cmd: '/think', desc: 'Toggle extended thinking (/think [on|off|stream])' },
|
||||
{ cmd: '/context', desc: 'Show context window usage & pressure' },
|
||||
{ cmd: '/verbose', desc: 'Cycle tool detail level (/verbose [off|on|full])' },
|
||||
{ cmd: '/queue', desc: 'Check if agent is processing' },
|
||||
{ cmd: '/status', desc: 'Show system status' },
|
||||
{ cmd: '/clear', desc: 'Clear chat display' },
|
||||
{ cmd: '/exit', desc: 'Disconnect from agent' },
|
||||
{ cmd: '/budget', desc: 'Show spending limits and current costs' },
|
||||
{ cmd: '/peers', desc: 'Show OFP peer network status' },
|
||||
{ cmd: '/a2a', desc: 'List discovered external A2A agents' }
|
||||
],
|
||||
slashCommands: [], // Loaded dynamically with i18n in init()
|
||||
_slashCommandsLoaded: false,
|
||||
tokenCount: 0,
|
||||
|
||||
// ── Tip Bar ──
|
||||
tipIndex: 0,
|
||||
tips: ['Type / for commands', '/think on for reasoning', 'Ctrl+Shift+F for focus mode', 'Drag files to attach', '/model to switch models', '/context to check usage', '/verbose off to hide tool details'],
|
||||
tips: [],
|
||||
_tipsInitialized: false,
|
||||
tipTimer: null,
|
||||
get currentTip() {
|
||||
if (localStorage.getItem('of-tips-off') === 'true') return '';
|
||||
return this.tips[this.tipIndex % this.tips.length];
|
||||
if (!this._tipsInitialized) {
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
this.tips = [
|
||||
t('tips.commands'),
|
||||
t('tips.think'),
|
||||
t('tips.focus'),
|
||||
'Drag files to attach',
|
||||
'/model to switch models',
|
||||
'/context to check usage',
|
||||
'/verbose off to hide tool details'
|
||||
];
|
||||
this._tipsInitialized = true;
|
||||
}
|
||||
return this.tips[this.tipIndex % this.tips.length] || '';
|
||||
},
|
||||
dismissTips: function() { localStorage.setItem('of-tips-off', 'true'); },
|
||||
startTipCycle: function() {
|
||||
@@ -137,6 +134,9 @@ function chatPage() {
|
||||
init() {
|
||||
var self = this;
|
||||
|
||||
// Initialize slash commands with i18n
|
||||
this.initSlashCommands();
|
||||
|
||||
// Start tip cycle
|
||||
this.startTipCycle();
|
||||
|
||||
@@ -264,19 +264,46 @@ function chatPage() {
|
||||
if (model.id === this.currentAgent.model_name) { this.showModelSwitcher = false; return; }
|
||||
var self = this;
|
||||
this.modelSwitching = true;
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
OpenFangAPI.put('/api/agents/' + this.currentAgent.id + '/model', { model: model.id }).then(function(resp) {
|
||||
// Use server-resolved model/provider to stay in sync (fixes #387/#466)
|
||||
self.currentAgent.model_name = (resp && resp.model) || model.id;
|
||||
self.currentAgent.model_provider = (resp && resp.provider) || model.provider;
|
||||
OpenFangToast.success('Switched to ' + (model.display_name || model.id));
|
||||
OpenFangToast.success(t('chat.model_switched') + ' ' + (model.display_name || model.id));
|
||||
self.showModelSwitcher = false;
|
||||
self.modelSwitching = false;
|
||||
}).catch(function(e) {
|
||||
OpenFangToast.error('Switch failed: ' + e.message);
|
||||
OpenFangToast.error(t('chat.model_switch_failed') + ': ' + e.message);
|
||||
self.modelSwitching = false;
|
||||
});
|
||||
},
|
||||
|
||||
// Initialize slash commands with i18n translations
|
||||
initSlashCommands: function() {
|
||||
if (this._slashCommandsLoaded) return;
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
this.slashCommands = [
|
||||
{ cmd: '/help', desc: t('chat.slash.help') },
|
||||
{ cmd: '/agents', desc: t('chat.slash.agents') },
|
||||
{ cmd: '/new', desc: t('chat.slash.new') },
|
||||
{ cmd: '/compact', desc: t('chat.slash.compact') },
|
||||
{ cmd: '/model', desc: t('chat.slash.model') },
|
||||
{ cmd: '/stop', desc: t('chat.slash.stop') },
|
||||
{ cmd: '/usage', desc: t('chat.slash.usage') },
|
||||
{ cmd: '/think', desc: t('chat.slash.think') },
|
||||
{ cmd: '/context', desc: t('chat.slash.context') },
|
||||
{ cmd: '/verbose', desc: t('chat.slash.verbose') },
|
||||
{ cmd: '/queue', desc: t('chat.slash.queue') },
|
||||
{ cmd: '/status', desc: t('chat.slash.status') },
|
||||
{ cmd: '/clear', desc: t('chat.slash.clear') },
|
||||
{ cmd: '/exit', desc: t('chat.slash.exit') },
|
||||
{ cmd: '/budget', desc: t('chat.slash.budget') },
|
||||
{ cmd: '/peers', desc: t('chat.slash.peers') },
|
||||
{ cmd: '/a2a', desc: t('chat.slash.a2a') }
|
||||
];
|
||||
this._slashCommandsLoaded = true;
|
||||
},
|
||||
|
||||
// Fetch dynamic slash commands from server
|
||||
fetchCommands: function() {
|
||||
var self = this;
|
||||
@@ -486,20 +513,13 @@ function chatPage() {
|
||||
this.currentAgent = agent;
|
||||
this.messages = [];
|
||||
this.connectWs(agent.id);
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
// Show welcome tips on first use
|
||||
if (!localStorage.getItem('of-chat-tips-seen')) {
|
||||
this.messages.push({
|
||||
id: ++msgId,
|
||||
role: 'system',
|
||||
text: '**Welcome to OpenFang Chat!**\n\n' +
|
||||
'- Type `/` to see available commands\n' +
|
||||
'- `/help` shows all commands\n' +
|
||||
'- `/think on` enables extended reasoning\n' +
|
||||
'- `/context` shows context window usage\n' +
|
||||
'- `/verbose off` hides tool details\n' +
|
||||
'- `Ctrl+Shift+F` toggles focus mode\n' +
|
||||
'- Drag & drop files to attach them\n' +
|
||||
'- `Ctrl+/` opens the command palette',
|
||||
text: t('chat.welcome_message'),
|
||||
meta: '',
|
||||
tools: []
|
||||
});
|
||||
@@ -563,7 +583,8 @@ function chatPage() {
|
||||
// Multi-session: create a new session
|
||||
async createSession() {
|
||||
if (!this.currentAgent) return;
|
||||
var label = prompt('Session name (optional):');
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
var label = prompt(t('chat.session_name_prompt'));
|
||||
if (label === null) return; // cancelled
|
||||
try {
|
||||
await OpenFangAPI.post('/api/agents/' + this.currentAgent.id + '/sessions', {
|
||||
@@ -573,9 +594,9 @@ function chatPage() {
|
||||
await this.loadSession(this.currentAgent.id);
|
||||
this.messages = [];
|
||||
this.scrollToBottom();
|
||||
if (typeof OpenFangToast !== 'undefined') OpenFangToast.success('New session created');
|
||||
if (typeof OpenFangToast !== 'undefined') OpenFangToast.success(t('chat.session_created'));
|
||||
} catch(e) {
|
||||
if (typeof OpenFangToast !== 'undefined') OpenFangToast.error('Failed to create session');
|
||||
if (typeof OpenFangToast !== 'undefined') OpenFangToast.error(t('chat.session_create_failed'));
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1012,8 +1033,9 @@ function chatPage() {
|
||||
}
|
||||
|
||||
// HTTP fallback
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
if (!OpenFangAPI.isWsConnected()) {
|
||||
OpenFangToast.info('Using HTTP mode (no streaming)');
|
||||
OpenFangToast.info(t('chat.using_http_mode'));
|
||||
}
|
||||
this.messages.push({ id: ++msgId, role: 'agent', text: '', meta: '', thinking: true, tools: [], ts: Date.now() });
|
||||
this.scrollToBottom();
|
||||
@@ -1056,18 +1078,19 @@ function chatPage() {
|
||||
killAgent() {
|
||||
if (!this.currentAgent) return;
|
||||
var self = this;
|
||||
var t = typeof window.t === 'function' ? window.t : function(s) { return s; };
|
||||
var name = this.currentAgent.name;
|
||||
OpenFangToast.confirm('Stop Agent', 'Stop agent "' + name + '"? The agent will be shut down.', async function() {
|
||||
OpenFangToast.confirm(t('chat.stop_agent_title'), t('chat.stop_agent_confirm') + ' "' + name + '"?', async function() {
|
||||
try {
|
||||
await OpenFangAPI.del('/api/agents/' + self.currentAgent.id);
|
||||
OpenFangAPI.wsDisconnect();
|
||||
self._wsAgent = null;
|
||||
self.currentAgent = null;
|
||||
self.messages = [];
|
||||
OpenFangToast.success('Agent "' + name + '" stopped');
|
||||
OpenFangToast.success(t('chat.agent_stopped') + ' "' + name + '"');
|
||||
Alpine.store('app').refreshAgents();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to stop agent: ' + e.message);
|
||||
OpenFangToast.error(t('chat.stop_agent_failed') + ': ' + e.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -64,15 +64,20 @@ function sessionsPage() {
|
||||
|
||||
deleteSession(sessionId) {
|
||||
var self = this;
|
||||
OpenFangToast.confirm('Delete Session', 'This will permanently remove the session and its messages.', async function() {
|
||||
try {
|
||||
await OpenFangAPI.del('/api/sessions/' + sessionId);
|
||||
self.sessions = self.sessions.filter(function(s) { return s.session_id !== sessionId; });
|
||||
OpenFangToast.success('Session deleted');
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to delete session: ' + e.message);
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
OpenFangToast.confirm(
|
||||
t('sessions.delete_session') || 'Delete Session',
|
||||
t('sessions.delete_confirm') || 'This will permanently remove the session and its messages.',
|
||||
async function() {
|
||||
try {
|
||||
await OpenFangAPI.del('/api/sessions/' + sessionId);
|
||||
self.sessions = self.sessions.filter(function(s) { return s.session_id !== sessionId; });
|
||||
OpenFangToast.success('Session deleted');
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to delete session: ' + e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
// -- Memory methods --
|
||||
@@ -108,15 +113,20 @@ function sessionsPage() {
|
||||
|
||||
deleteKey(key) {
|
||||
var self = this;
|
||||
OpenFangToast.confirm('Delete Key', 'Delete key "' + key + '"? This cannot be undone.', async function() {
|
||||
try {
|
||||
await OpenFangAPI.del('/api/memory/agents/' + self.memAgentId + '/kv/' + encodeURIComponent(key));
|
||||
OpenFangToast.success('Key "' + key + '" deleted');
|
||||
await self.loadKv();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to delete key: ' + e.message);
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
OpenFangToast.confirm(
|
||||
t('sessions.delete_key') || 'Delete Key',
|
||||
(t('sessions.delete_key_confirm') || 'Delete key') + ' "' + key + '"? This cannot be undone.',
|
||||
async function() {
|
||||
try {
|
||||
await OpenFangAPI.del('/api/memory/agents/' + self.memAgentId + '/kv/' + encodeURIComponent(key));
|
||||
OpenFangToast.success('Key "' + key + '" deleted');
|
||||
await self.loadKv();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to delete key: ' + e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
startEdit(kv) {
|
||||
|
||||
@@ -34,27 +34,30 @@ function skillsPage() {
|
||||
mcpServers: [],
|
||||
mcpLoading: false,
|
||||
|
||||
// Category definitions from the OpenClaw ecosystem
|
||||
categories: [
|
||||
{ id: 'coding', name: 'Coding & IDEs' },
|
||||
{ id: 'git', name: 'Git & GitHub' },
|
||||
{ id: 'web', name: 'Web & Frontend' },
|
||||
{ id: 'devops', name: 'DevOps & Cloud' },
|
||||
{ id: 'browser', name: 'Browser & Automation' },
|
||||
{ id: 'search', name: 'Search & Research' },
|
||||
{ id: 'ai', name: 'AI & LLMs' },
|
||||
{ id: 'data', name: 'Data & Analytics' },
|
||||
{ id: 'productivity', name: 'Productivity' },
|
||||
{ id: 'communication', name: 'Communication' },
|
||||
{ id: 'media', name: 'Media & Streaming' },
|
||||
{ id: 'notes', name: 'Notes & PKM' },
|
||||
{ id: 'security', name: 'Security' },
|
||||
{ id: 'cli', name: 'CLI Utilities' },
|
||||
{ id: 'marketing', name: 'Marketing & Sales' },
|
||||
{ id: 'finance', name: 'Finance' },
|
||||
{ id: 'smart-home', name: 'Smart Home & IoT' },
|
||||
{ id: 'docs', name: 'PDF & Documents' },
|
||||
],
|
||||
// Category definitions from the OpenClaw ecosystem (loaded from i18n)
|
||||
get categories() {
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
return [
|
||||
{ id: 'coding', name: t('skills.cat_coding') || 'Coding & IDEs' },
|
||||
{ id: 'git', name: t('skills.cat_git') || 'Git & GitHub' },
|
||||
{ id: 'web', name: t('skills.cat_frontend') || 'Web & Frontend' },
|
||||
{ id: 'devops', name: t('skills.cat_devops') || 'DevOps & Cloud' },
|
||||
{ id: 'browser', name: t('skills.cat_browser') || 'Browser & Automation' },
|
||||
{ id: 'search', name: t('skills.cat_search') || 'Search & Research' },
|
||||
{ id: 'ai', name: t('skills.cat_ai') || 'AI & ML' },
|
||||
{ id: 'data', name: t('skills.cat_data') || 'Data & Analytics' },
|
||||
{ id: 'productivity', name: t('skills.cat_productivity') || 'Productivity' },
|
||||
{ id: 'communication', name: t('skills.cat_communication') || 'Communication' },
|
||||
{ id: 'media', name: t('skills.cat_media') || 'Media & Streaming' },
|
||||
{ id: 'notes', name: t('skills.cat_notes') || 'Notes & PKM' },
|
||||
{ id: 'security', name: t('skills.cat_security') || 'Security' },
|
||||
{ id: 'cli', name: t('skills.cat_cli') || 'CLI Utilities' },
|
||||
{ id: 'marketing', name: t('skills.cat_marketing') || 'Marketing & Sales' },
|
||||
{ id: 'finance', name: t('skills.cat_finance') || 'Finance' },
|
||||
{ id: 'smart-home', name: t('skills.cat_smarthome') || 'Smart Home & IoT' },
|
||||
{ id: 'docs', name: t('skills.cat_docs') || 'Documentation' },
|
||||
];
|
||||
},
|
||||
|
||||
runtimeBadge: function(rt) {
|
||||
var r = (rt || '').toLowerCase();
|
||||
@@ -264,15 +267,20 @@ function skillsPage() {
|
||||
// Uninstall
|
||||
uninstallSkill: function(name) {
|
||||
var self = this;
|
||||
OpenFangToast.confirm('Uninstall Skill', 'Uninstall skill "' + name + '"? This cannot be undone.', async function() {
|
||||
try {
|
||||
await OpenFangAPI.post('/api/skills/uninstall', { name: name });
|
||||
OpenFangToast.success('Skill "' + name + '" uninstalled');
|
||||
await self.loadSkills();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to uninstall skill: ' + e.message);
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
OpenFangToast.confirm(
|
||||
t('skills.uninstall_skill') || 'Uninstall Skill',
|
||||
t('skills.uninstall_confirm') + ' "' + name + '"? This cannot be undone.',
|
||||
async function() {
|
||||
try {
|
||||
await OpenFangAPI.post('/api/skills/uninstall', { name: name });
|
||||
OpenFangToast.success('Skill "' + name + '" uninstalled');
|
||||
await self.loadSkills();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to uninstall skill: ' + e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
// Create prompt-only skill
|
||||
|
||||
@@ -158,15 +158,17 @@ function wizardPage() {
|
||||
return this.templates.filter(function(t) { return t.category === cat; });
|
||||
},
|
||||
|
||||
// Step 3: Profile/tool descriptions
|
||||
profileDescriptions: {
|
||||
minimal: { label: 'Minimal', desc: 'Read-only file access' },
|
||||
coding: { label: 'Coding', desc: 'Files + shell + web fetch' },
|
||||
research: { label: 'Research', desc: 'Web search + file read/write' },
|
||||
balanced: { label: 'Balanced', desc: 'General-purpose tool set' },
|
||||
precise: { label: 'Precise', desc: 'Focused tool set for accuracy' },
|
||||
creative: { label: 'Creative', desc: 'Full tools with creative emphasis' },
|
||||
full: { label: 'Full', desc: 'All 35+ tools' }
|
||||
// Step 3: Profile/tool descriptions (loaded from i18n)
|
||||
get profileDescriptions() {
|
||||
return {
|
||||
minimal: { label: window.i18n ? window.i18n.t('wizard.profile_minimal') : 'Minimal', desc: window.i18n ? window.i18n.t('wizard.profile_minimal_desc') : 'Read-only file access' },
|
||||
coding: { label: window.i18n ? window.i18n.t('wizard.profile_coding') : 'Coding', desc: window.i18n ? window.i18n.t('wizard.profile_coding_desc') : 'Files + shell + web fetch' },
|
||||
research: { label: window.i18n ? window.i18n.t('wizard.profile_research') : 'Research', desc: window.i18n ? window.i18n.t('wizard.profile_research_desc') : 'Web search + file read/write' },
|
||||
balanced: { label: window.i18n ? window.i18n.t('wizard.profile_balanced') : 'Balanced', desc: window.i18n ? window.i18n.t('wizard.profile_balanced_desc') : 'General-purpose tool set' },
|
||||
precise: { label: window.i18n ? window.i18n.t('wizard.profile_precise') : 'Precise', desc: window.i18n ? window.i18n.t('wizard.profile_precise_desc') : 'Focused tool set for accuracy' },
|
||||
creative: { label: window.i18n ? window.i18n.t('wizard.profile_creative') : 'Creative', desc: window.i18n ? window.i18n.t('wizard.profile_creative_desc') : 'Full tools with creative emphasis' },
|
||||
full: { label: window.i18n ? window.i18n.t('wizard.profile_full') : 'Full', desc: window.i18n ? window.i18n.t('wizard.profile_full_desc') : 'All 35+ tools' }
|
||||
};
|
||||
},
|
||||
profileInfo: function(name) { return this.profileDescriptions[name] || { label: name, desc: '' }; },
|
||||
|
||||
@@ -174,12 +176,35 @@ function wizardPage() {
|
||||
tryItMessages: [],
|
||||
tryItInput: '',
|
||||
tryItSending: false,
|
||||
suggestedMessages: {
|
||||
'General': ['What can you help me with?', 'Tell me a fun fact', 'Summarize the latest AI news'],
|
||||
'Development': ['Write a Python hello world', 'Explain async/await', 'Review this code snippet'],
|
||||
'Research': ['Explain quantum computing simply', 'Compare React vs Vue', 'What are the latest trends in AI?'],
|
||||
'Writing': ['Help me write a professional email', 'Improve this paragraph', 'Write a blog intro about AI'],
|
||||
'Business': ['Draft a meeting agenda', 'How do I handle a complaint?', 'Create a project status update']
|
||||
get suggestedMessages() {
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
return {
|
||||
'General': [
|
||||
t('wizard.suggestions.general.1') || 'What can you help me with?',
|
||||
t('wizard.suggestions.general.2') || 'Tell me a fun fact',
|
||||
t('wizard.suggestions.general.3') || 'Summarize the latest AI news'
|
||||
],
|
||||
'Development': [
|
||||
t('wizard.suggestions.development.1') || 'Write a Python hello world',
|
||||
t('wizard.suggestions.development.2') || 'Explain async/await',
|
||||
t('wizard.suggestions.development.3') || 'Review this code snippet'
|
||||
],
|
||||
'Research': [
|
||||
t('wizard.suggestions.research.1') || 'Explain quantum computing simply',
|
||||
t('wizard.suggestions.research.2') || 'Compare React vs Vue',
|
||||
t('wizard.suggestions.research.3') || 'What are the latest trends in AI?'
|
||||
],
|
||||
'Writing': [
|
||||
t('wizard.suggestions.writing.1') || 'Help me write a professional email',
|
||||
t('wizard.suggestions.writing.2') || 'Improve this paragraph',
|
||||
t('wizard.suggestions.writing.3') || 'Write a blog intro about AI'
|
||||
],
|
||||
'Business': [
|
||||
t('wizard.suggestions.business.1') || 'Draft a meeting agenda',
|
||||
t('wizard.suggestions.business.2') || 'How do I handle a complaint?',
|
||||
t('wizard.suggestions.business.3') || 'Create a project status update'
|
||||
]
|
||||
};
|
||||
},
|
||||
get currentSuggestions() {
|
||||
var tpl = this.templates[this.selectedTemplate];
|
||||
@@ -204,38 +229,41 @@ function wizardPage() {
|
||||
|
||||
// Step 5: Channel setup (optional)
|
||||
channelType: '',
|
||||
channelOptions: [
|
||||
{
|
||||
name: 'telegram',
|
||||
display_name: 'Telegram',
|
||||
icon: 'TG',
|
||||
description: 'Connect your agent to a Telegram bot for messaging.',
|
||||
token_label: 'Bot Token',
|
||||
token_placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
|
||||
token_env: 'TELEGRAM_BOT_TOKEN',
|
||||
help: 'Create a bot via @BotFather on Telegram to get your token.'
|
||||
},
|
||||
{
|
||||
name: 'discord',
|
||||
display_name: 'Discord',
|
||||
icon: 'DC',
|
||||
description: 'Connect your agent to a Discord server via bot token.',
|
||||
token_label: 'Bot Token',
|
||||
token_placeholder: 'MTIz...abc',
|
||||
token_env: 'DISCORD_BOT_TOKEN',
|
||||
help: 'Create a Discord application at discord.com/developers and add a bot.'
|
||||
},
|
||||
{
|
||||
name: 'slack',
|
||||
display_name: 'Slack',
|
||||
icon: 'SL',
|
||||
description: 'Connect your agent to a Slack workspace.',
|
||||
token_label: 'Bot Token',
|
||||
token_placeholder: 'xoxb-...',
|
||||
token_env: 'SLACK_BOT_TOKEN',
|
||||
help: 'Create a Slack app at api.slack.com/apps and install it to your workspace.'
|
||||
}
|
||||
],
|
||||
get channelOptions() {
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
return [
|
||||
{
|
||||
name: 'telegram',
|
||||
display_name: t('wizard.channel_telegram') || 'Telegram',
|
||||
icon: 'TG',
|
||||
description: t('wizard.channel_telegram_desc') || 'Connect your agent to a Telegram bot for messaging.',
|
||||
token_label: t('wizard.channel_telegram_token') || 'Bot Token',
|
||||
token_placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
|
||||
token_env: 'TELEGRAM_BOT_TOKEN',
|
||||
help: t('wizard.channel_telegram_help') || 'Create a bot via @BotFather on Telegram to get your token.'
|
||||
},
|
||||
{
|
||||
name: 'discord',
|
||||
display_name: t('wizard.channel_discord') || 'Discord',
|
||||
icon: 'DC',
|
||||
description: t('wizard.channel_discord_desc') || 'Connect your agent to a Discord server via bot token.',
|
||||
token_label: t('wizard.channel_discord_token') || 'Bot Token',
|
||||
token_placeholder: 'MTIz...abc',
|
||||
token_env: 'DISCORD_BOT_TOKEN',
|
||||
help: t('wizard.channel_discord_help') || 'Create a Discord application at discord.com/developers and add a bot.'
|
||||
},
|
||||
{
|
||||
name: 'slack',
|
||||
display_name: t('wizard.channel_slack') || 'Slack',
|
||||
icon: 'SL',
|
||||
description: t('wizard.channel_slack_desc') || 'Connect your agent to a Slack workspace.',
|
||||
token_label: t('wizard.channel_slack_token') || 'Bot Token',
|
||||
token_placeholder: 'xoxb-...',
|
||||
token_env: 'SLACK_BOT_TOKEN',
|
||||
help: t('wizard.channel_slack_help') || 'Create a Slack app at api.slack.com/apps and install it to your workspace.'
|
||||
}
|
||||
];
|
||||
},
|
||||
channelToken: '',
|
||||
configuringChannel: false,
|
||||
channelConfigured: false,
|
||||
@@ -297,7 +325,15 @@ function wizardPage() {
|
||||
},
|
||||
|
||||
stepLabel(n) {
|
||||
var labels = ['Welcome', 'Provider', 'Agent', 'Try It', 'Channel', 'Done'];
|
||||
var t = window.i18n ? window.i18n.t.bind(window.i18n) : function(k) { return k; };
|
||||
var labels = [
|
||||
t('wizard.step_welcome') || 'Welcome',
|
||||
t('wizard.step_provider') || 'Provider',
|
||||
t('wizard.step_agent') || 'Agent',
|
||||
t('wizard.step_try_it') || 'Try It',
|
||||
t('wizard.step_channel') || 'Channel',
|
||||
t('wizard.step_done') || 'Done'
|
||||
];
|
||||
return labels[n - 1] || '';
|
||||
},
|
||||
|
||||
@@ -382,7 +418,7 @@ function wizardPage() {
|
||||
if (!provider) return;
|
||||
var key = this.apiKeyInput.trim();
|
||||
if (!key) {
|
||||
OpenFangToast.error('Please enter an API key');
|
||||
OpenFangToast.error(window.i18n ? window.i18n.t('wizard.enter_api_key') : 'Please enter an API key');
|
||||
return;
|
||||
}
|
||||
this.savingKey = true;
|
||||
@@ -391,12 +427,12 @@ function wizardPage() {
|
||||
this.apiKeyInput = '';
|
||||
this.keySaved = true;
|
||||
this.setupSummary.provider = provider.display_name;
|
||||
OpenFangToast.success('API key saved for ' + provider.display_name);
|
||||
OpenFangToast.success((window.i18n ? window.i18n.t('wizard.api_key_saved') : 'API key saved for') + ' ' + provider.display_name);
|
||||
await this.loadProviders();
|
||||
// Auto-test after saving
|
||||
await this.testKey();
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to save key: ' + e.message);
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.failed_save_key') : 'Failed to save key:') + ' ' + e.message);
|
||||
}
|
||||
this.savingKey = false;
|
||||
},
|
||||
@@ -410,13 +446,13 @@ function wizardPage() {
|
||||
var result = await OpenFangAPI.post('/api/providers/' + encodeURIComponent(provider.id) + '/test', {});
|
||||
this.testResult = result;
|
||||
if (result.status === 'ok') {
|
||||
OpenFangToast.success(provider.display_name + ' connected (' + (result.latency_ms || '?') + 'ms)');
|
||||
OpenFangToast.success(provider.display_name + ' ' + (window.i18n ? window.i18n.t('wizard.connected') : 'connected') + ' (' + (result.latency_ms || '?') + 'ms)');
|
||||
} else {
|
||||
OpenFangToast.error(provider.display_name + ': ' + (result.error || 'Connection failed'));
|
||||
OpenFangToast.error(provider.display_name + ': ' + (result.error || (window.i18n ? window.i18n.t('wizard.connection_failed') : 'Connection failed')));
|
||||
}
|
||||
} catch(e) {
|
||||
this.testResult = { status: 'error', error: e.message };
|
||||
OpenFangToast.error('Test failed: ' + e.message);
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.test_failed') : 'Test failed:') + ' ' + e.message);
|
||||
}
|
||||
this.testingProvider = false;
|
||||
},
|
||||
@@ -458,7 +494,7 @@ function wizardPage() {
|
||||
if (!tpl) return;
|
||||
var name = this.agentName.trim();
|
||||
if (!name) {
|
||||
OpenFangToast.error('Please enter a name for your agent');
|
||||
OpenFangToast.error(window.i18n ? window.i18n.t('wizard.enter_agent_name') : 'Please enter a name for your agent');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -485,13 +521,13 @@ function wizardPage() {
|
||||
if (res.agent_id) {
|
||||
this.createdAgent = { id: res.agent_id, name: res.name || name };
|
||||
this.setupSummary.agent = res.name || name;
|
||||
OpenFangToast.success('Agent "' + (res.name || name) + '" created');
|
||||
OpenFangToast.success((window.i18n ? window.i18n.t('wizard.agent_created') : 'Agent') + ' "' + (res.name || name) + '" ' + (window.i18n ? window.i18n.t('wizard.agent_created_suffix') || 'created' : 'created'));
|
||||
await Alpine.store('app').refreshAgents();
|
||||
} else {
|
||||
OpenFangToast.error('Failed: ' + (res.error || 'Unknown error'));
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.failed_create_agent') : 'Failed:') + ' ' + (res.error || 'Unknown error'));
|
||||
}
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed to create agent: ' + e.message);
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.failed_create_agent') : 'Failed to create agent:') + ' ' + e.message);
|
||||
}
|
||||
this.creatingAgent = false;
|
||||
},
|
||||
@@ -538,7 +574,7 @@ function wizardPage() {
|
||||
if (!ch) return;
|
||||
var token = this.channelToken.trim();
|
||||
if (!token) {
|
||||
OpenFangToast.error('Please enter the ' + ch.token_label);
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.enter_token') : 'Please enter the') + ' ' + ch.token_label);
|
||||
return;
|
||||
}
|
||||
this.configuringChannel = true;
|
||||
@@ -549,9 +585,9 @@ function wizardPage() {
|
||||
await OpenFangAPI.post('/api/channels/' + ch.name + '/configure', { fields: fields });
|
||||
this.channelConfigured = true;
|
||||
this.setupSummary.channel = ch.display_name;
|
||||
OpenFangToast.success(ch.display_name + ' configured and activated.');
|
||||
OpenFangToast.success(ch.display_name + ' ' + (window.i18n ? window.i18n.t('wizard.channel_configured') : 'configured and activated.'));
|
||||
} catch(e) {
|
||||
OpenFangToast.error('Failed: ' + (e.message || 'Unknown error'));
|
||||
OpenFangToast.error((window.i18n ? window.i18n.t('wizard.failed_configure') : 'Failed:') + ' ' + (e.message || 'Unknown error'));
|
||||
}
|
||||
this.configuringChannel = false;
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user