diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b6da7b1..39bed22f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- 支持通过 `agent.task_timeout_minutes` 配置单代理、多代理、工作流及异步任务执行超时:未配置默认 600 分钟,`0` 表示不限时,负数拒绝加载;续跑保留重新计时行为,单次工具超时仍独立生效。 + - 漏洞列表的单条下载按钮新增格式选择弹窗,支持 Markdown 与 Word(.docx),Word 下载复用漏洞读取权限并仅包含所选漏洞(#365)。 - 新增单代理与多代理异步对话 API:接收后返回任务回执,客户端断线不停止执行;支持按用户隔离的幂等提交、持久状态与结果查询、显式取消以及服务重启后的中断状态。同步接口保持兼容。 @@ -12,6 +14,10 @@ ### Fixed +- 修复 WebShell 批量探活因请求构建异常一直停留在“探活中”的问题;使用连接自身 ID 读取已保存凭据,并在探活失败后恢复按钮、继续处理其他连接。 + +- 修复审计 Agent 向不支持 thinking 字段的兼容网关发起请求失败的问题;支持独立兼容 profile,并按模型和地址识别默认行为。 + - 修复已知支持 `none` 的官方 OpenAI 模型在关闭推理后仍使用服务端默认推理的问题(#366):按提供商、端点和模型发送并保留显式关闭参数,覆盖 Planner 和强制工具选择;保留 DeepSeek 与第三方兼容网关行为,并明确提示需要 Responses 的工具调用限制。 - 防止内置管理员失权:拒绝停用内置 `admin` 或移除其系统管理员角色,失败时保留原资料、权限和会话;界面同步禁用对应操作。 diff --git a/config.example.yaml b/config.example.yaml index a25f1882..92db51b2 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -157,6 +157,8 @@ shodan: # Agent 配置 # 达到最大迭代次数时,AI 会自动总结测试结果 agent: + # 修改后重启服务,对新任务生效;例如 1440 = 24 小时,单次工具超时独立生效。 + task_timeout_minutes: 600 # 任务执行超时(分钟);未配置=600,0=不限时,负数无效;续跑重新计时 max_iterations: 12000 # 全局最大迭代次数(单代理 / Deep / Supervisor / Plan-Execute 主执行器 / 子代理均沿用;agents/*.md 中 max_iterations>0 可单独覆盖) tool_timeout_minutes: 60 # 单次工具执行最大时长(分钟),超时自动终止;0 表示不限制(不推荐,易出现长时间挂起) tool_wait_timeout_seconds: 300 # 工具本轮最多等待(秒);到时返回 execution_id,worker 继续后台执行,可用 wait_tool_execution 继续等待;0=等到完成 @@ -201,6 +203,8 @@ hitl: base_url: "" # openai 后端留空跟随主模型;typesafe 后端留空使用 https://api.typesafe.ai api_key: "" # openai 后端留空跟随主模型;typesafe 后端填写 TypeSafe API Key model: "" # openai 后端建议填小模型;typesafe 后端留空使用 jev-latest + reasoning: + profile: "" # auto / deepseek_compat / openai_compat;仅控制审计请求的 thinking 字段 # 已决策审计日志保留天数(与 MCP 监控一致;省略默认 90;0 表示不自动清理) retention_days: 90 # 按你环境里的真实工具名增删(与侧栏一致、小写不敏感);不需要全局免审批可改为 [] diff --git a/internal/config/agent_timeout.go b/internal/config/agent_timeout.go new file mode 100644 index 00000000..5e3efe85 --- /dev/null +++ b/internal/config/agent_timeout.go @@ -0,0 +1,27 @@ +package config + +import ( + "fmt" + "time" +) + +// EffectiveTaskTimeout returns the execution budget for each task segment. +// An omitted value retains the historical ten-hour limit; zero disables it. +func (a AgentConfig) EffectiveTaskTimeout() time.Duration { + if a.TaskTimeoutMinutes == nil { + return 600 * time.Minute + } + return time.Duration(*a.TaskTimeoutMinutes) * time.Minute +} + +func (a AgentConfig) validateTaskTimeout() error { + if a.TaskTimeoutMinutes == nil { + return nil + } + const maxMinutes = int64((1<<63 - 1) / time.Minute) + minutes := int64(*a.TaskTimeoutMinutes) + if minutes < 0 || minutes > maxMinutes { + return fmt.Errorf("agent.task_timeout_minutes must be between 0 and %d minutes", maxMinutes) + } + return nil +} diff --git a/internal/config/agent_timeout_test.go b/internal/config/agent_timeout_test.go new file mode 100644 index 00000000..4a1e7412 --- /dev/null +++ b/internal/config/agent_timeout_test.go @@ -0,0 +1,61 @@ +package config + +import ( + "os" + "path/filepath" + "strings" + "testing" + "time" + + "gopkg.in/yaml.v3" +) + +func TestLoadTaskTimeout(t *testing.T) { + for _, tc := range []struct { + name, yaml string + want time.Duration + invalid bool + }{ + {"absent agent", "{}", 600 * time.Minute, false}, + {"absent field", "agent: {}", 600 * time.Minute, false}, + {"custom", "agent:\n task_timeout_minutes: 1440", 24 * time.Hour, false}, + {"unlimited", "agent:\n task_timeout_minutes: 0", 0, false}, + {"negative", "agent:\n task_timeout_minutes: -1", 0, true}, + {"overflow", "agent:\n task_timeout_minutes: 153722868", 0, true}, + } { + t.Run(tc.name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "config.yaml") + if err := os.WriteFile(path, []byte(tc.yaml), 0600); err != nil { + t.Fatal(err) + } + cfg, err := Load(path) + if tc.invalid { + if err == nil || !strings.Contains(err.Error(), "agent.task_timeout_minutes") { + t.Fatalf("expected field validation error, got %v", err) + } + return + } + if err != nil { + t.Fatal(err) + } + if got := cfg.Agent.EffectiveTaskTimeout(); got != tc.want { + t.Fatalf("timeout = %v, want %v", got, tc.want) + } + // YAML saves must preserve explicit zero as distinct from omission. + data, err := yaml.Marshal(cfg) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(path, data, 0600); err != nil { + t.Fatal(err) + } + again, err := Load(path) + if err != nil { + t.Fatal(err) + } + if again.Agent.EffectiveTaskTimeout() != tc.want { + t.Fatal("timeout changed after save/load") + } + }) + } +} diff --git a/internal/config/config.go b/internal/config/config.go index ac4d35b1..f4eaa2a9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1112,13 +1112,14 @@ type DatabaseConfig struct { } type AgentConfig struct { - MaxIterations int `yaml:"max_iterations" json:"max_iterations"` - ToolTimeoutMinutes int `yaml:"tool_timeout_minutes" json:"tool_timeout_minutes"` // 单次工具执行最大时长(分钟),超时自动终止,防止长时间挂起;0 表示不限制(不推荐) - ToolWaitTimeoutSeconds int `yaml:"tool_wait_timeout_seconds" json:"tool_wait_timeout_seconds"` // 工具本轮等待秒数;到时返回 execution_id,worker 继续后台执行;0 表示等到完成 - ExternalMCPMaxConcurrentPerServer int `yaml:"external_mcp_max_concurrent_per_server" json:"external_mcp_max_concurrent_per_server"` // 单个外部 MCP server 同时运行的工具数;0 表示默认 2 - ExternalMCPMaxConcurrentTotal int `yaml:"external_mcp_max_concurrent_total" json:"external_mcp_max_concurrent_total"` // 所有外部 MCP 工具全局并发;0 表示默认 16 - ExternalMCPCircuitFailureThreshold int `yaml:"external_mcp_circuit_failure_threshold" json:"external_mcp_circuit_failure_threshold"` // 单个 MCP server 连续失败多少次后打开熔断;0 表示默认 3;负数关闭 - ExternalMCPCircuitCooldownSeconds int `yaml:"external_mcp_circuit_cooldown_seconds" json:"external_mcp_circuit_cooldown_seconds"` // 熔断后冷却秒数;0 表示默认 60 + TaskTimeoutMinutes *int `yaml:"task_timeout_minutes,omitempty" json:"task_timeout_minutes,omitempty"` // nil=600 minutes; 0=unlimited + MaxIterations int `yaml:"max_iterations" json:"max_iterations"` + ToolTimeoutMinutes int `yaml:"tool_timeout_minutes" json:"tool_timeout_minutes"` // 单次工具执行最大时长(分钟),超时自动终止,防止长时间挂起;0 表示不限制(不推荐) + ToolWaitTimeoutSeconds int `yaml:"tool_wait_timeout_seconds" json:"tool_wait_timeout_seconds"` // 工具本轮等待秒数;到时返回 execution_id,worker 继续后台执行;0 表示等到完成 + ExternalMCPMaxConcurrentPerServer int `yaml:"external_mcp_max_concurrent_per_server" json:"external_mcp_max_concurrent_per_server"` // 单个外部 MCP server 同时运行的工具数;0 表示默认 2 + ExternalMCPMaxConcurrentTotal int `yaml:"external_mcp_max_concurrent_total" json:"external_mcp_max_concurrent_total"` // 所有外部 MCP 工具全局并发;0 表示默认 16 + ExternalMCPCircuitFailureThreshold int `yaml:"external_mcp_circuit_failure_threshold" json:"external_mcp_circuit_failure_threshold"` // 单个 MCP server 连续失败多少次后打开熔断;0 表示默认 3;负数关闭 + ExternalMCPCircuitCooldownSeconds int `yaml:"external_mcp_circuit_cooldown_seconds" json:"external_mcp_circuit_cooldown_seconds"` // 熔断后冷却秒数;0 表示默认 60 // ShellNoOutputTimeoutSeconds execute/exec 无任何 stdout/stderr 时的空闲终止秒数(通用防挂死,不维护命令黑名单);0=默认 300(5 分钟);-1=关闭。 ShellNoOutputTimeoutSeconds int `yaml:"shell_no_output_timeout_seconds" json:"shell_no_output_timeout_seconds"` // WorkspaceRootDir 会话工作目录根路径(curl/wget 下载、read_file/glob/grep 本地分析);空=tmp/workspace,其下按 projects/{id} 或 conversations/{id} 隔离。 @@ -1230,6 +1231,12 @@ func (h HitlConfig) TypeSafeConfigEffective() (baseURL, apiKey, model string) { func (h HitlConfig) AuditModelEffective(main OpenAIConfig) OpenAIConfig { out := main am := h.AuditModel + if strings.TrimSpace(am.Provider) != "" || strings.TrimSpace(am.BaseURL) != "" || strings.TrimSpace(am.Model) != "" { + out.Reasoning.Profile = "" + } + if strings.TrimSpace(am.Reasoning.Profile) != "" { + out.Reasoning.Profile = am.Reasoning.Profile + } if strings.TrimSpace(am.Provider) != "" { out.Provider = strings.TrimSpace(am.Provider) } @@ -1646,6 +1653,9 @@ func Load(path string) (*Config, error) { if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("解析配置文件失败: %w", err) } + if err := cfg.Agent.validateTaskTimeout(); err != nil { + return nil, err + } if cfg.ToolGuard != nil { if err := validateToolGuardYAML(data); err != nil { return nil, fmt.Errorf("调用拦截配置无效: %w", err) diff --git a/internal/config/hitl_audit_profile_test.go b/internal/config/hitl_audit_profile_test.go new file mode 100644 index 00000000..64c72d72 --- /dev/null +++ b/internal/config/hitl_audit_profile_test.go @@ -0,0 +1,27 @@ +package config + +import "testing" + +func TestAuditModelProfileInheritance(t *testing.T) { + main := OpenAIConfig{Reasoning: OpenAIReasoningConfig{Profile: "deepseek_compat"}} + for _, tc := range []struct { + name string + audit OpenAIConfig + want string + }{ + {"inherit", OpenAIConfig{}, "deepseek_compat"}, + {"key alone inherits", OpenAIConfig{APIKey: "other"}, "deepseek_compat"}, + {"model isolates", OpenAIConfig{Model: "glm"}, ""}, + {"url isolates", OpenAIConfig{BaseURL: "https://example.com"}, ""}, + {"provider isolates", OpenAIConfig{Provider: "openai"}, ""}, + {"explicit overrides", OpenAIConfig{Reasoning: OpenAIReasoningConfig{Profile: "openai_compat"}}, "openai_compat"}, + {"explicit auto overrides", OpenAIConfig{Reasoning: OpenAIReasoningConfig{Profile: "auto"}}, "auto"}, + } { + t.Run(tc.name, func(t *testing.T) { + got := (HitlConfig{AuditModel: tc.audit}).AuditModelEffective(main).Reasoning.Profile + if got != tc.want { + t.Fatalf("profile=%q want %q", got, tc.want) + } + }) + } +} diff --git a/internal/handler/async_agent_runs.go b/internal/handler/async_agent_runs.go index e2a12ec2..ce25df11 100644 --- a/internal/handler/async_agent_runs.go +++ b/internal/handler/async_agent_runs.go @@ -10,7 +10,6 @@ import ( "net/http" "strings" "sync" - "time" "cyberstrike-ai/internal/database" "cyberstrike-ai/internal/mcp" @@ -98,7 +97,7 @@ func (h *AgentHandler) submitAgentRunWithExecutor(c *gin.Context, multi bool, ex return } base, cancel := context.WithCancelCause(detachedAgentContext(c.Request.Context())) - ctx, timeoutCancel := context.WithTimeout(base, 600*time.Minute) + ctx, timeoutCancel := h.newTaskContext(base) task, err := h.tasks.StartTask(prep.ConversationID, req.Message, cancel) if err != nil { timeoutCancel() diff --git a/internal/handler/config.go b/internal/handler/config.go index d36c8e55..f963f47a 100644 --- a/internal/handler/config.go +++ b/internal/handler/config.go @@ -2359,6 +2359,7 @@ func updateHitlConfig(doc *yaml.Node, cfg config.HitlConfig) { setStringInMap(auditModelNode, "base_url", cfg.AuditModel.BaseURL) setStringInMap(auditModelNode, "api_key", cfg.AuditModel.APIKey) setStringInMap(auditModelNode, "model", cfg.AuditModel.Model) + setStringInMap(ensureMap(auditModelNode, "reasoning"), "profile", cfg.AuditModel.Reasoning.Profile) // flow 样式 [a, b, c] 单行展示,工具多时比块序列省行数 setFlowStringSliceInMap(hitlNode, "tool_whitelist", cfg.ToolWhitelist) setStringInMap(hitlNode, "default_mode", cfg.EffectiveDefaultMode()) diff --git a/internal/handler/eino_empty_response_continue.go b/internal/handler/eino_empty_response_continue.go index 2c57df86..a946feaa 100644 --- a/internal/handler/eino_empty_response_continue.go +++ b/internal/handler/eino_empty_response_continue.go @@ -19,7 +19,7 @@ func (h *AgentHandler) rebindEinoRunningTask(parent context.Context, conversatio } baseCtx, cancelWithCause := context.WithCancelCause(detachedAgentContext(parent)) h.tasks.BindTaskCancel(conversationID, cancelWithCause) - taskCtx, newTimeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, newTimeoutCancel := h.newTaskContext(baseCtx) h.tasks.UpdateTaskStatus(conversationID, "running") return baseCtx, cancelWithCause, taskCtx, newTimeoutCancel } diff --git a/internal/handler/eino_single_agent.go b/internal/handler/eino_single_agent.go index b3ed6207..3701190b 100644 --- a/internal/handler/eino_single_agent.go +++ b/internal/handler/eino_single_agent.go @@ -166,7 +166,7 @@ func (h *AgentHandler) EinoSingleAgentLoopStream(c *gin.Context) { var runErr error baseCtx, cancelWithCause = context.WithCancelCause(detachedAgentContext(c.Request.Context())) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) if startedTask, err := h.tasks.StartTask(conversationID, req.Message, cancelWithCause); err != nil { var errorMsg string @@ -327,7 +327,7 @@ func (h *AgentHandler) EinoSingleAgentLoopStream(c *gin.Context) { timeoutCancel() baseCtx, cancelWithCause = context.WithCancelCause(detachedAgentContext(baseCtx)) h.tasks.BindTaskCancel(conversationID, cancelWithCause) - taskCtx, timeoutCancel = context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel = h.newTaskContext(baseCtx) h.tasks.UpdateTaskStatus(conversationID, "running") continue } @@ -458,7 +458,7 @@ func (h *AgentHandler) EinoSingleAgentLoop(c *gin.Context) { baseCtx, cancelWithCause := context.WithCancelCause(c.Request.Context()) defer cancelWithCause(nil) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) defer timeoutCancel() jsonTask, startErr := h.tasks.StartTask(prep.ConversationID, req.Message, cancelWithCause) if startErr != nil { diff --git a/internal/handler/hitl_audit_agent.go b/internal/handler/hitl_audit_agent.go index 4da67c75..c7db6119 100644 --- a/internal/handler/hitl_audit_agent.go +++ b/internal/handler/hitl_audit_agent.go @@ -51,8 +51,9 @@ func (h *AgentHandler) auditAgentReview(ctx context.Context, hitlMode, toolName }, "temperature": 0.1, "max_completion_tokens": 1024, - // 审计裁决需要结构化 JSON;关闭 thinking 避免 Qwen 等把正文放进 reasoning_content 导致解析失败。 - "thinking": map[string]interface{}{"type": "disabled"}, + } + if auditDisableThinking(llmCfg) { + requestBody["thinking"] = map[string]interface{}{"type": "disabled"} } var apiResponse struct { diff --git a/internal/handler/hitl_audit_reasoning.go b/internal/handler/hitl_audit_reasoning.go new file mode 100644 index 00000000..df0e8e8d --- /dev/null +++ b/internal/handler/hitl_audit_reasoning.go @@ -0,0 +1,23 @@ +package handler + +import ( + "strings" + + "cyberstrike-ai/internal/config" +) + +// auditDisableThinking selects only the audit request's provider-specific field. +func auditDisableThinking(cfg config.OpenAIConfig) bool { + switch cfg.Reasoning.ProfileEffective() { + case "deepseek", "deepseek_compat": + return true + case "auto": + default: + return false + } + identity := strings.ToLower(cfg.Model + " " + cfg.BaseURL) + if strings.Contains(identity, "glm") { + return false + } + return strings.Contains(identity, "deepseek") || strings.Contains(identity, "qwen") +} diff --git a/internal/handler/hitl_audit_reasoning_test.go b/internal/handler/hitl_audit_reasoning_test.go new file mode 100644 index 00000000..5f08f0b9 --- /dev/null +++ b/internal/handler/hitl_audit_reasoning_test.go @@ -0,0 +1,108 @@ +package handler + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "cyberstrike-ai/internal/config" + "go.uber.org/zap" + "gopkg.in/yaml.v3" +) + +func TestAuditAgentRequestCompatibility(t *testing.T) { + cases := []struct { + name, model, profile, mainProfile string + wantThinking, fail bool + }{ + {name: "glm", model: "tencent:glm-5.3"}, + {name: "deepseek", model: "deepseek-chat", wantThinking: true}, + {name: "qwen", model: "qwen-plus", wantThinking: true}, + {name: "unknown", model: "other"}, + {name: "explicit deepseek", model: "glm-5", profile: "deepseek_compat", wantThinking: true}, + {name: "explicit openai", model: "deepseek-chat", profile: "openai_compat"}, + {name: "isolated main profile", model: "glm-5", mainProfile: "deepseek_compat"}, + {name: "failure is rejected", model: "glm-5", fail: true}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + calls := 0 + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + calls++ + var body map[string]any + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + t.Error(err) + w.WriteHeader(400) + return + } + thinking, present := body["thinking"] + if present != tc.wantThinking { + t.Errorf("thinking present=%v, want %v", present, tc.wantThinking) + } + if present { + v, ok := thinking.(map[string]any) + if !ok || v["type"] != "disabled" { + t.Errorf("thinking=%v", thinking) + } + } + if body["max_completion_tokens"] != float64(1024) || body["temperature"] != 0.1 { + t.Errorf("unexpected request limits: %v", body) + } + if tc.fail { + w.WriteHeader(400) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"choices":[{"message":{"content":"{\"decision\":\"approve\"}"}}]}`)) + })) + defer server.Close() + h := &AgentHandler{logger: zap.NewNop(), config: &config.Config{OpenAI: config.OpenAIConfig{APIKey: "test", Model: "main", Reasoning: config.OpenAIReasoningConfig{Profile: tc.mainProfile}}, Hitl: config.HitlConfig{AuditModel: config.OpenAIConfig{BaseURL: server.URL, Model: tc.model, Reasoning: config.OpenAIReasoningConfig{Profile: tc.profile}}}}} + decision := h.auditAgentReview(context.Background(), "approval", "test", nil) + want := "approve" + if tc.fail { + want = "reject" + } + if decision.Decision != want || calls != 1 { + t.Fatalf("decision=%+v calls=%d", decision, calls) + } + }) + } +} + +func TestAuditThinkingDetectionAddressAndPrecedence(t *testing.T) { + for _, tc := range []struct { + model, url, profile string + want bool + }{ + {"other", "https://deepseek.example/v1", "", true}, + {"other", "https://qwen.example/v1", "", true}, + {"glm-5", "https://deepseek.example/v1", "", false}, + {"deepseek", "https://example/v1", "openai_compat", false}, + } { + got := auditDisableThinking(config.OpenAIConfig{Model: tc.model, BaseURL: tc.url, Reasoning: config.OpenAIReasoningConfig{Profile: tc.profile}}) + if got != tc.want { + t.Errorf("model=%s url=%s profile=%s got %v", tc.model, tc.url, tc.profile, got) + } + } +} + +func TestAuditProfileConfigPersistence(t *testing.T) { + var doc yaml.Node + if err := yaml.Unmarshal([]byte("hitl:\n audit_model:\n reasoning:\n profile: openai_compat\n"), &doc); err != nil { + t.Fatal(err) + } + updateHitlConfig(&doc, config.HitlConfig{AuditModel: config.OpenAIConfig{Reasoning: config.OpenAIReasoningConfig{Profile: "deepseek_compat"}}}) + raw, err := yaml.Marshal(&doc) + if err != nil { + t.Fatal(err) + } + var decoded config.Config + if err := yaml.Unmarshal(raw, &decoded); err != nil { + t.Fatal(err) + } + if got := decoded.Hitl.AuditModel.Reasoning.Profile; got != "deepseek_compat" { + t.Fatalf("profile=%q", got) + } +} diff --git a/internal/handler/multi_agent.go b/internal/handler/multi_agent.go index 6e9ecfa3..912de564 100644 --- a/internal/handler/multi_agent.go +++ b/internal/handler/multi_agent.go @@ -173,7 +173,7 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) { var runErr error baseCtx, cancelWithCause = context.WithCancelCause(detachedAgentContext(c.Request.Context())) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) if startedTask, err := h.tasks.StartTask(conversationID, req.Message, cancelWithCause); err != nil { var errorMsg string @@ -341,7 +341,7 @@ func (h *AgentHandler) MultiAgentLoopStream(c *gin.Context) { timeoutCancel() baseCtx, cancelWithCause = context.WithCancelCause(detachedAgentContext(baseCtx)) h.tasks.BindTaskCancel(conversationID, cancelWithCause) - taskCtx, timeoutCancel = context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel = h.newTaskContext(baseCtx) h.tasks.UpdateTaskStatus(conversationID, "running") continue } @@ -477,7 +477,7 @@ func (h *AgentHandler) MultiAgentLoop(c *gin.Context) { baseCtx, cancelWithCause := context.WithCancelCause(c.Request.Context()) defer cancelWithCause(nil) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) defer timeoutCancel() jsonTask, startErr := h.tasks.StartTask(prep.ConversationID, req.Message, cancelWithCause) if startErr != nil { diff --git a/internal/handler/task_deadline.go b/internal/handler/task_deadline.go index 37d7c8be..0448b261 100644 --- a/internal/handler/task_deadline.go +++ b/internal/handler/task_deadline.go @@ -3,6 +3,8 @@ package handler import ( "context" "errors" + + "cyberstrike-ai/internal/config" ) // taskDeadlineExceeded distinguishes the task's deadline from a child operation's @@ -11,3 +13,17 @@ import ( func taskDeadlineExceeded(taskCtx context.Context) bool { return taskCtx != nil && errors.Is(taskCtx.Err(), context.DeadlineExceeded) } + +// newTaskContext starts a fresh execution budget. Unlimited tasks remain cancellable. +// A parent's cancellation or earlier deadline is always respected. +func (h *AgentHandler) newTaskContext(parent context.Context) (context.Context, context.CancelFunc) { + var cfg config.AgentConfig + if h.config != nil { + cfg = h.config.Agent + } + timeout := cfg.EffectiveTaskTimeout() + if timeout == 0 { + return context.WithCancel(parent) + } + return context.WithTimeout(parent, timeout) +} diff --git a/internal/handler/task_timeout_test.go b/internal/handler/task_timeout_test.go new file mode 100644 index 00000000..3288500d --- /dev/null +++ b/internal/handler/task_timeout_test.go @@ -0,0 +1,122 @@ +package handler + +import ( + "context" + "errors" + "gopkg.in/yaml.v3" + "strconv" + "testing" + "time" + + "cyberstrike-ai/internal/config" +) + +func TestConfiguredTaskContext(t *testing.T) { + for _, minutes := range []int{-1, 0, 1440} { + t.Run(taskTimeoutCaseName(minutes), func(t *testing.T) { + h := &AgentHandler{} + want := 600 * time.Minute + if minutes >= 0 { + h.config = &config.Config{Agent: config.AgentConfig{TaskTimeoutMinutes: &minutes}} + want = time.Duration(minutes) * time.Minute + } + parent, cancelParent := context.WithCancelCause(context.Background()) + defer cancelParent(nil) + before := time.Now() + ctx, cancel := h.newTaskContext(parent) + defer cancel() + deadline, ok := ctx.Deadline() + if want == 0 { + if ok { + t.Fatal("unlimited task has a deadline") + } + } else if !ok || deadline.Before(before.Add(want)) || deadline.After(time.Now().Add(want)) { + t.Fatalf("unexpected deadline %v", deadline) + } + cancelParent(ErrTaskCancelled) + if !errors.Is(context.Cause(ctx), ErrTaskCancelled) { + t.Fatal("parent cancellation lost") + } + + ctx, cancel = h.newTaskContext(context.Background()) + cancel() + if !errors.Is(ctx.Err(), context.Canceled) { + t.Fatal("explicit cancellation lost") + } + + early := time.Now().Add(time.Minute) + parentDeadline, stop := context.WithDeadline(context.Background(), early) + defer stop() + ctx, cancel = h.newTaskContext(parentDeadline) + defer cancel() + if got, ok := ctx.Deadline(); !ok || !got.Equal(early) { + t.Fatal("earlier parent deadline lost") + } + }) + } +} + +func taskTimeoutCaseName(minutes int) string { + if minutes < 0 { + return "default" + } + if minutes == 0 { + return "unlimited" + } + return "custom" +} + +func TestRebindTaskRestartsConfiguredBudget(t *testing.T) { + for _, minutes := range []int{0, 1440} { + t.Run(taskTimeoutCaseName(minutes), func(t *testing.T) { + h := &AgentHandler{config: &config.Config{Agent: config.AgentConfig{TaskTimeoutMinutes: &minutes}}, tasks: NewAgentTaskManager()} + base, stop := context.WithCancelCause(context.Background()) + defer stop(nil) + if _, err := h.tasks.StartTask("resume", "test", stop); err != nil { + t.Fatal(err) + } + expired, cancelExpired := context.WithDeadline(base, time.Now().Add(-time.Second)) + before := time.Now() + _, stopNew, resumed, cancelNew := h.rebindEinoRunningTask(expired, "resume", cancelExpired) + defer stopNew(nil) + defer cancelNew() + if resumed.Err() != nil { + t.Fatalf("resumed task inherited expired context: %v", resumed.Err()) + } + deadline, ok := resumed.Deadline() + if minutes == 0 { + if ok { + t.Fatal("unlimited resume has deadline") + } + } else if !ok || deadline.Before(before.Add(24*time.Hour)) || deadline.After(time.Now().Add(24*time.Hour)) { + t.Fatalf("resume budget was not reset: %v", deadline) + } + stopNew(ErrTaskCancelled) + if !errors.Is(context.Cause(resumed), ErrTaskCancelled) { + t.Fatal("resumed task cannot be cancelled") + } + }) + } +} + +// Saving unrelated settings must not erase a manually configured task budget. +func TestSettingsSavePreservesTaskTimeout(t *testing.T) { + for _, value := range []string{"0", "1440"} { + var doc yaml.Node + if err := yaml.Unmarshal([]byte("agent:\n task_timeout_minutes: "+value+"\n"), &doc); err != nil { + t.Fatal(err) + } + updateAgentConfig(&doc, config.AgentConfig{MaxIterations: 100}) + data, err := yaml.Marshal(&doc) + if err != nil { + t.Fatal(err) + } + var cfg config.Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + t.Fatal(err) + } + if cfg.Agent.TaskTimeoutMinutes == nil || strconv.Itoa(*cfg.Agent.TaskTimeoutMinutes) != value { + t.Fatalf("settings save changed timeout: %s", data) + } + } +} diff --git a/internal/handler/workflow_integration.go b/internal/handler/workflow_integration.go index 3c40ea8c..042a3e8b 100644 --- a/internal/handler/workflow_integration.go +++ b/internal/handler/workflow_integration.go @@ -88,7 +88,7 @@ func (h *AgentHandler) runRoleWorkflowStreamIfBound( } baseCtx, cancelWithCause := context.WithCancelCause(detachedAgentContext(c.Request.Context())) defer cancelWithCause(nil) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) defer timeoutCancel() if startedTask, err := h.tasks.StartTask(conversationID, userMessage, cancelWithCause); err != nil { @@ -231,7 +231,7 @@ func (h *AgentHandler) runRoleWorkflowJSONIfBound(c *gin.Context, req *ChatReque baseCtx, cancelWithCause := context.WithCancelCause(c.Request.Context()) defer cancelWithCause(nil) - taskCtx, timeoutCancel := context.WithTimeout(baseCtx, 600*time.Minute) + taskCtx, timeoutCancel := h.newTaskContext(baseCtx) defer timeoutCancel() if startedTask, err := h.tasks.StartTask(conversationID, userMessage, cancelWithCause); err != nil { diff --git a/web/static/i18n/ar-SA.json b/web/static/i18n/ar-SA.json new file mode 100644 index 00000000..735612a6 --- /dev/null +++ b/web/static/i18n/ar-SA.json @@ -0,0 +1,4592 @@ +{ + "lang": { + "zhCN": "中文", + "enUS": "English", + "ruRU": "Русский", + "arSA": "العربية" + }, + "mobile": { + "openNav": "فتح قائمة التنقّل", + "closeNav": "إغلاق قائمة التنقّل", + "moreActions": "إجراءات إضافية", + "searchMenu": "البحث في القائمة…", + "noMatch": "لا توجد عناصر قائمة مطابقة", + "backToTop": "العودة إلى الأعلى", + "chatList": "المحادثات", + "closeChatList": "إغلاق قائمة المحادثات", + "mainNav": "التنقّل الرئيسي", + "switchTheme": "تبديل السمة", + "themeNow": "السمة: {{name}}", + "switchLanguage": "تبديل اللغة", + "refreshPage": "إعادة تحميل الصفحة", + "fullscreen": "ملء الشاشة", + "fullscreenUnsupported": "هذا المتصفّح لا يدعم وضع ملء الشاشة", + "conversations": "المحادثات", + "newChat": "محادثة جديدة", + "chatModuleMissing": "لم يتمّ تحميل وحدة المحادثة بعد" + }, + "common": { + "ok": "موافق", + "cancel": "إلغاء", + "refresh": "تحديث", + "close": "إغلاق", + "edit": "تعديل", + "delete": "حذف", + "remove": "إزالة", + "save": "حفظ", + "loading": "جارٍ التحميل…", + "search": "بحث", + "clearSearch": "مسح البحث", + "noData": "لا توجد بيانات", + "confirm": "تأكيد", + "copy": "نسخ", + "copied": "تمّ النسخ", + "copyFailed": "فشل النسخ", + "view": "عرض", + "actions": "الإجراءات", + "loadFailed": "فشل التحميل", + "loadMore": "تحميل المزيد", + "untitled": "بلا عنوان", + "justNow": "الآن", + "minutesAgo": "قبل {{n}} دقيقة", + "hoursAgo": "قبل {{n}} ساعة", + "daysAgo": "قبل {{n}} يوم" + }, + "header": { + "title": "CyberStrikeAI", + "apiDocs": "توثيق API", + "github": "GitHub", + "logout": "تسجيل الخروج", + "language": "لغة الواجهة", + "backToDashboard": "العودة إلى لوحة التحكّم", + "userMenu": "قائمة المستخدم", + "userMenuFor": "قائمة المستخدم: {{name}}", + "currentUser": "المستخدم الحالي", + "unknownUser": "مستخدم غير معروف", + "scopeAll": "جميع الموارد", + "scopeAssigned": "الموارد المُسنَدة", + "scopeOwn": "الموارد الخاصّة", + "scopeUnknown": "نطاق غير معروف", + "rolesCount": "{{count}} دور", + "permissionsCount": "{{count}} صلاحية", + "version": "الإصدار الحالي", + "toggleSidebar": "طيّ/توسيع الشريط الجانبي" + }, + "theme": { + "system": "النظام", + "light": "فاتح", + "dark": "داكن", + "titleSystem": "الحالي: سمة النظام. انقر للتبديل إلى الفاتح.", + "titleLight": "الحالي: السمة الفاتحة. انقر للتبديل إلى الداكن.", + "titleDark": "الحالي: السمة الداكنة. انقر للتبديل إلى سمة النظام.", + "toggle": "تبديل السمة" + }, + "notifications": { + "title": "الإشعارات", + "empty": "لا توجد أحداث جديدة", + "markAllRead": "تحديد الكلّ كمقروء", + "markSingleRead": "مقروء", + "itemDefaultTitle": "إشعار" + }, + "login": { + "showPassword": "إظهار كلمة المرور", + "hidePassword": "إخفاء كلمة المرور", + "title": "تسجيل الدخول إلى CyberStrikeAI", + "titlePrefix": "تسجيل الدخول إلى", + "subtitle": "سجّل الدخول بحساب المنصّة. المسؤول الافتراضي هو admin.", + "usernameLabel": "اسم المستخدم", + "usernamePlaceholder": "admin", + "passwordLabel": "كلمة المرور", + "passwordPlaceholder": "أدخل كلمة المرور", + "submit": "تسجيل الدخول" + }, + "update": { + "title": "تحديث بنقرة واحدة", + "intro": "تُغيّر هذه الصفحة التثبيت نفسه الذي يستجيب لطلباتك: فهي تجلب أحدث إيداع من الفرع الافتراضي لمصدر التحديث (المستودع الرسمي ما لم يُضبط غير ذلك)، وتعيد بناء الملف الثنائي وتستبدله. لا تُستبدَل الأدوار/المهارات/الأدوات ولا الإعدادات التي تحتفظ بها هنا.", + "loading": "جارٍ قراءة حالة هذا التثبيت", + "loadFailed": "تعذّرت قراءة حالة هذا التثبيت: {{reason}}", + "installTitle": "هذا التثبيت", + "remoteTitle": "مقابل مصدر التحديث", + "applyTitle": "تحديث بنقرة واحدة", + "jobTitle": "تقدّم التحديث", + "rollbackTitle": "التراجع", + "root": "دليل التثبيت", + "branch": "الفرع المحلّي", + "commit": "الإيداع الحالي", + "committedAt": "أُودِع في", + "subject": "موضوع الإيداع", + "gitTree": "شجرة عمل git", + "notGitTree": "ليست شجرة عمل git", + "toolchainFound": "سلسلة أدوات Go متوفّرة", + "toolchainMissing": "لا توجد سلسلة أدوات Go", + "toolchainMissingHint": "لم يُعثر على أمر go: يمكن للتحديث نقل المصدر فقط، أمّا البناء فيحتاج إلى جهاز آخر", + "binaryPresent": "الملف الثنائي موجود", + "binaryMissing": "الملف الثنائي مفقود", + "rollbackPresent": "توجد نقطة تراجع", + "rollbackMissing": "لا توجد نقطة تراجع", + "notChecked": "لم تتمّ مراجعة مصدر التحديث بعد. يؤدّي فتح هذه الصفحة إلى جدولة فحص تلقائي واحد (مرّة واحدة لكلّ جلسة)؛ اضغط على التحقّق من التحديثات لإعادة السؤال في أيّ وقت.", + "checkBtn": "التحقّق من التحديثات", + "checking": "جارٍ التحقّق من مصدر التحديث...", + "checkFailed": "فشل التحقّق: {{reason}}", + "applyFailed": "تعذّر بدء التحديث: {{reason}}", + "rollbackFailed": "فشل التراجع: {{reason}}", + "behindCount": "متأخّر بمقدار {{n}} إيداع", + "aheadCount": "متقدّم محلّيًا بمقدار {{n}} إيداع", + "upToDate": "محدّث: الإيداع المحلّي {{commit}} مطابق لمصدر التحديث", + "remoteHead": "رأس المصدر", + "remoteSubject": "موضوع إيداع المصدر", + "incomingTitle": "الإيداعات الواردة", + "incomingTruncated": "{{total}} إيداع جديد إجمالًا؛ تُعرَض الإيداعات الأولى {{shown}} فقط", + "applyBtn": "تحديث الآن", + "jobRunningHint": "هناك تحديث قيد التشغيل بالفعل؛ تقدّمه معروض أدناه.", + "noToolchainWarn": "لا توجد سلسلة أدوات Go هنا: سيُحدَّث المصدر دون استبدال الملف الثنائي؛ ابنِه خارجيًا ثمّ أعد التشغيل.", + "rollbackPendingHint": "التراجع السابق غير مكتمل. انقر على التراجع لإنهاء الاستعادة قبل التحديث.", + "buildPendingHint": "نقل التحديث الأخير المصدر لكنّه لم يستبدل الملف الثنائي أبدًا (فشل البناء، أو لم تكن هناك سلسلة أدوات Go): اضغط على تحديث لإنهاء ذلك البناء.", + "blockedNotGitTree": "هذا الدليل ليس شجرة عمل git، لذا لا يمكن تحديثه تلقائيًا.", + "blockedJobRunning": "هناك تحديث قيد التشغيل بالفعل؛ انتظر حتّى ينتهي.", + "blockedLocalEdits": "{{n}} من ملفّات مصدر المنتج معدّلة محلّيًا ولن يستبدلها التحديث: أودِعها أو تراجع عنها أوّلًا.", + "blockedDiverged": "السجلّ المحلّي متقدّم بمقدار {{ahead}} إيداع ومتأخّر بمقدار {{behind}}: هذا دمج وليس تنزيلًا، لذا عالجه يدويًا.", + "blockedNoUpdate": "مصدر التحديث لا يحتوي على أيّ شيء أحدث من هذا الجهاز (اضغط على التحقّق من التحديثات إن لم تكن قد راجعت بعد).", + "restartChoiceLabel": "إنهاء العملية بعد التحديث (لن يعيد تشغيلها إلّا مشرف خارجي مثل systemd أو run.sh؛ أعد تشغيلها بنفسك إن لم يشرف عليها شيء)", + "restartNotWired": "لم يوفّر هذا التشغيل خطّاف إعادة تشغيل: أعد تشغيل الخدمة بنفسك بعد التحديث.", + "confirmRestartYes": "محدّد: تنتهي العملية بمجرّد نجاح التحديث، ولن يعيد تشغيلها إلّا مشرف خارجي (systemd، run.sh، ...).", + "confirmRestartNo": "غير محدّد: تستمرّ العملية في تشغيل الملف الثنائي القديم حتّى تعيد تشغيلها بنفسك.", + "confirmApply": "على وشك التنفيذ: التحديث من {{repo}} إلى {{target}} ({{n}} إيداع جديد، هذا الجهاز عند {{commit}}) -> إعادة بناء الملف الثنائي -> نسخ القديم احتياطيًا واستبداله.", + "confirmBuildPending": "على وشك التنفيذ: بناء الملف الثنائي المفقود للمصدر الموجود بالفعل (هذا الجهاز عند {{commit}}) -> إعادة البناء -> نسخ القديم احتياطيًا واستبدال الملف الثنائي القديم.", + "applyAccepted": "بدأ التحديث (المهمّة {{id}})؛ يتحدّث التقدّم كلّ 1.5 ثانية.", + "localChangesTitle": "التغييرات المحلّية", + "localChangesHint": "تُحفظ الملفّات المعلّمة كمحتوى محلّي كما هي تمامًا؛ أمّا الملفّات المعلّمة كمصدر منتج فتمنع التحديث التلقائي.", + "noLocalChanges": "شجرة العمل نظيفة: لا يوجد ملفّ متتبَّع معدّل أو محذوف.", + "colPath": "الملفّ", + "colChangeStatus": "الحالة", + "colOwner": "المالك", + "keptFile": "محتوى محلّي (يحتفظ به التحديث)", + "productSource": "مصدر المنتج (يمنع التحديث)", + "noJob": "لم يُشغَّل أيّ تحديث بعد", + "noSteps": "لم تُنتِج المهمّة سطر تقدّم بعد", + "pollingHint": "تُقرأ حالة التقدّم كلّ {{sec}} ثانية", + "stateRunning": "قيد التشغيل", + "stateSucceeded": "نجح", + "stateFailed": "فشل", + "stateUnknown": "حالة غير معروفة", + "restartRequested": "طُلب الإنهاء بعد الاكتمال", + "jobPollFailed": "تعذّرت قراءة التقدّم: {{reason}}", + "jobDoneToast": "انتهى التحديث", + "buildDoneToast": "انتهى البناء", + "jobFailedToast": "فشل التحديث", + "needsRestartToast": "تمّ استبدال الملف الثنائي؛ يسري المفعول بعد إعادة التشغيل", + "restartBanner": "تمّ التحديث إلى {{commit}}، لكنّ هذه العملية لا تزال تشغّل البناء القديم (بُني الملف الثنائي على القرص في {{builtAt}}). يسري المفعول بعد إعادة التشغيل.", + "restartBannerNoCommit": "تمّ استبدال الملف الثنائي على القرص (بُني في {{builtAt}})، لكنّ هذه العملية لا تزال تشغّل البناء القديم؛ يسري المفعول بعد إعادة التشغيل.", + "restartNowBtn": "إعادة التشغيل الآن", + "restartNowConfirm": "ستنتهي عملية الخدمة؛ مع وجود مشرف (systemd/launchd) تعود النسخة الجديدة خلال ثوانٍ وتعيد هذه الصفحة تحميل نفسها. ومن دونه تبقى الخدمة متوقّفة حتّى تشغّلها. إعادة التشغيل الآن؟", + "restartRequestedToast": "طُلبت إعادة التشغيل: يعيدها المشرف وتتعافى هذه الصفحة من تلقاء نفسها", + "restartFailed": "فشل طلب إعادة التشغيل: {{reason}}", + "restartOverlayTitle": "جارٍ إعادة تشغيل الخدمة…", + "restartOverlayBody": "تعيد هذه الصفحة تحميل نفسها بمجرّد أن تستجيب العملية الجديدة (ستنتهي جلستك، لذا سيتبع ذلك تسجيل دخول جديد).", + "restartOverlaySlowRunning": "لا تزال الخدمة تستجيب بوصفها العملية القديمة - يبدو أنّ إعادة التشغيل لم تحدث. استخدم الزرّ أدناه لمعاينة الحالة الحالية.", + "restartOverlaySlowDown": "لم تعد الخدمة. إن لم يشرف عليها شيء (systemd/launchd) فلن تبدأ من تلقاء نفسها؛ شغّلها ثمّ حدّث هذه الصفحة.", + "restartOverlayRefresh": "تحديث هذه الصفحة", + "failureNoMessage": "لم يقدّم الخادم أيّ رسالة فشل", + "resultTitle": "نتيجة التحديث", + "resultFrom": "من", + "resultTo": "إلى", + "resultCommits": "الإيداعات المُدخَلة", + "resultFiles": "الملفّات المتأثّرة", + "resultDuration": "المدّة", + "resultBackup": "دليل النسخ الاحتياطي", + "keptTitle": "ملفّات المحتوى المحلّي التي تمّ الاحتفاظ بها", + "nothingKept": "لم يكن هناك ملفّ محتوى محلّي بحاجة إلى إزاحته جانبًا", + "binarySwapped": "تمّ استبدال الملف الثنائي", + "binaryNotSwapped": "الملف الثنائي دون تغيير", + "restartNeeded": "يلزم إعادة التشغيل لتشغيل النسخة الجديدة", + "restartNotNeeded": "لا حاجة لإعادة التشغيل", + "failureTitle": "سبب الفشل", + "noRollback": "لا توجد نقطة تراجع: لا توجد إلّا بعد تشغيل تحديث", + "rollbackTarget": "هدف التراجع: {{commit}} (حيث كانت هذه الشجرة قبل آخر تحديث)", + "rollbackBtn": "التراجع عن التحديث", + "confirmRollback": "التراجع إلى {{commit}}؟ يعود المصدر إلى ما قبل ذلك التحديث ويُستعاد الملف الثنائي السابق.", + "rollbackDone": "تمّ التراجع إلى {{commit}}", + "sourceTitle": "مصدر التحديث", + "sourceFromConfig": "يُضبط مصدر التحديث في config.yaml.", + "sourceIsDefault": "لم يُضبط أيّ مصدر تحديث: يُستخدَم المستودع الرسمي {{repo}}.", + "sourceUrl": "مصدر التحديث", + "sourceBranch": "الفرع الافتراضي للمصدر", + "sourceUrlLabel": "عنوان مستودع مصدر التحديث", + "sourceRepoHint": "اتركه فارغًا لاستخدام المستودع الرسمي؛ وجّهه إلى نسختك المتفرّعة أو إلى مرآة لمتابعتها بدلًا من ذلك.", + "sourceSaveBtn": "حفظ المصدر", + "sourceSaveOk": "تمّ حفظ مصدر التحديث", + "sourceSaveFailed": "تعذّر حفظ مصدر التحديث: {{reason}}", + "adoptHint": "هذا الدليل ليس شجرة عمل git بعد. يؤدّي ربطه بمصدر التحديث إلى تحويله إلى تثبيت عادي يُحدَّث بنقرة واحدة.", + "adoptPreviewBtn": "معاينة وربط", + "adoptIncoming": "الملفّات في الفرع الهدف", + "adoptOverwrittenTotal": "سيُستبدَل بالهدف", + "adoptProtectedTotal": "محتوى المشغّل (محفوظ)", + "adoptCommit": "إيداع الهدف", + "adoptOverwrittenTitle": "الملفّات المحلّية التي سيتمّ استبدالها", + "adoptProtectedTitle": "محتوى المشغّل الذي سيُحتفظ به", + "adoptBackupNote": "يُحفظ كلّ ملفّ مُستبدَل ضمن .update-backup//overwritten/ ويمكن استعادته من هناك.", + "adoptConfirmBtn": "ربط", + "adoptConfirmText": "يستبدل الربط {{overwritten}} ملفًّا محلّيًا بنسخ الهدف (من أصل {{incoming}} ملفّ إجمالًا؛ يُحتفظ بـ {{protectedFiles}} ملفّ مشغّل). هل تريد المتابعة؟", + "adoptAccepted": "بدأ الربط (المهمّة {{id}})؛ يتحدّث التقدّم كلّ 1.5 ثانية.", + "adoptFailed": "فشل الربط: {{reason}}", + "adoptedNoPreviousCommit": "هذه نتيجة ربط: لم يكن هناك سجلّ git قبلها، لذا لا يوجد إيداع سابق للتراجع إليه؛ يكتب التحديث التالي أوّل نقطة تراجع.", + "resultOverwrittenTitle": "الملفّات المحلّية التي تمّ استبدالها (نسخ ضمن overwritten/)", + "rollbackItems": "الملفّات التي لم يُتراجَع عنها" + }, + "nav": { + "security": "الأمن", + "toolGuard": "حظر الاستدعاءات", + "dashboard": "لوحة التحكّم", + "chat": "المحادثة", + "assets": "إدارة الأصول", + "assetOverview": "نظرة عامّة على الأصول", + "assetLibrary": "مكتبة الأصول", + "infoCollect": "الاستطلاع", + "tasks": "المهامّ", + "projects": "المشاريع", + "vulnerabilities": "الثغرات", + "webshell": "إدارة WebShell", + "chatFiles": "إدارة الملفّات", + "mcp": "MCP", + "mcpMonitor": "مراقبة MCP", + "mcpManagement": "إدارة MCP", + "knowledge": "المعرفة", + "knowledgeRetrievalLogs": "سجلّ الاسترجاع", + "knowledgeManagement": "إدارة المعرفة", + "skills": "المهارات", + "skillsMonitor": "مراقبة المهارات", + "skillsManagement": "إدارة المهارات", + "agents": "الوكلاء", + "agentsManagement": "إدارة الوكلاء", + "roles": "الأدوار", + "rolesManagement": "إدارة الأدوار", + "workflows": "مسارات العمل", + "settings": "إعدادات النظام", + "hitl": "التدخّل البشري (HITL)", + "c2": "C2", + "c2Listeners": "المستمعون", + "c2Sessions": "الجلسات", + "c2Tasks": "المهامّ", + "c2Payloads": "الحمولة", + "c2Events": "الأحداث", + "c2Profiles": "ملفّات تعريف حركة البيانات", + "platformRbac": "الوصول إلى المنصّة" + }, + "navGroups": { + "workbench": "مساحة العمل", + "operations": "العمليات الأمنية", + "capabilities": "القدرات", + "administration": "الإدارة" + }, + "tokenUsageUI": { + "chartHint": "مرّر المؤشّر فوق شريط أو ركّز عليه لعرض الاستهلاك الدقيق", + "scopeProject": "المشروع الحالي", + "scopeAccessible": "المحادثات المتاحة", + "subtitle": "استكشف استهلاك النماذج والتخزين المؤقّت واتّجاهات الاستخدام", + "scopeNote": "النطاق وقواعد العدّ", + "trendMetric": "مقياس الاتّجاه", + "noDailyData": "لا توجد سجلّات استخدام يومية", + "cacheComposition": "تفصيل التخزين المؤقّت للمدخلات", + "cacheHit": "معدّل إصابة التخزين المؤقّت", + "uncachedInput": "المدخلات دون قراءات من التخزين المؤقّت", + "cacheNote": "قراءات التخزين المؤقّت جزء من المدخلات، ولا تُضاف إلى الإجمالي مرّة أخرى. أمّا عمليات الكتابة في التخزين المؤقّت فتُدرَج على حدة." + }, + "dashboard": { + "title": "لوحة التحكّم", + "refresh": "تحديث", + "refreshData": "تحديث البيانات", + "runningTasks": "المهامّ قيد التشغيل", + "runningConversations": "المحادثات قيد التشغيل", + "vulnTotal": "إجمالي الثغرات", + "toolCalls": "استدعاءات الأدوات", + "successRate": "معدّل نجاح الأدوات", + "tokenUsage": "استخدام الرموز (Tokens)", + "usageCacheHitRate": "إصابة التخزين المؤقّت {{rate}}", + "tokenUsageSub": "إصابة التخزين المؤقّت {{rate}}", + "noTokenUsageYet": "لا يوجد استخدام بعد", + "clickToViewTasks": "انقر لعرض المهامّ", + "clickToViewChat": "انقر لعرض المحادثات", + "clickToViewVuln": "انقر لعرض الثغرات", + "clickToViewMCP": "انقر لعرض مراقبة MCP", + "accessOverviewTitle": "نظرة عامّة على الوصول", + "accessTabsAria": "C2 وWebShell", + "c2OverviewTitle": "نظرة عامّة على C2", + "c2GoManage": "فتح C2 ←", + "webshellGoManage": "فتح WebShell ←", + "webshellConnections": "الاتّصالات النشطة", + "webshellClickConnections": "عرض الاتّصالات", + "c2ListenersRunning": "المستمعون قيد التشغيل", + "c2SessionsOnline": "الجلسات المتّصلة", + "c2TasksPending": "المهامّ المعلّقة / في قائمة الانتظار", + "c2ClickListeners": "عرض المستمعين", + "c2ClickSessions": "عرض الجلسات", + "c2ClickTasks": "عرض المهامّ", + "severityDistribution": "توزيع خطورة الثغرات", + "severityCritical": "حرجة", + "severityHigh": "عالية", + "severityMedium": "متوسّطة", + "severityLow": "منخفضة", + "severityInfo": "معلوماتية", + "totalVulns": "إجمالي الثغرات", + "riskLevel": "مستوى الخطر", + "riskScore": "درجة الخطر المرجّحة", + "riskSafe": "آمن", + "riskLow": "منخفض", + "riskMedium": "متوسّط", + "riskHigh": "عالٍ", + "riskSevere": "شديد", + "latestFound": "آخر ما اكتُشف", + "noneYet": "لا شيء بعد", + "runOverview": "نظرة عامّة على التشغيل", + "batchQueues": "قوائم انتظار المهامّ المجمّعة", + "pending": "معلّقة", + "executing": "قيد التشغيل", + "completed": "مكتملة", + "toolInvocations": "استدعاءات الأدوات", + "callsUnit": "استدعاء", + "toolsUnit": "أداة", + "knowledgeLabel": "المعرفة", + "knowledgeItems": "عنصر", + "categoriesUnit": "فئة", + "skillsLabel": "المهارات", + "skillUnit": "مهارات", + "quickLinks": "روابط سريعة", + "toolsExecCount": "عدد تنفيذ الأدوات", + "ctaTitle": "ابدأ رحلتك الأمنية", + "ctaSub": "صِف هدفك في المحادثة، وسيساعدك الذكاء الاصطناعي في الفحص وتحليل الثغرات", + "goToChat": "الانتقال إلى المحادثة", + "noTasks": "لا توجد مهامّ", + "totalCount": "{{count}} إجمالًا", + "notEnabled": "معطّل", + "enabled": "مفعّل", + "toConfigure": "للإعداد", + "toUse": "للاستخدام", + "active": "نشط", + "highFreq": "تردّد عالٍ", + "noCallData": "لا توجد بيانات استدعاء", + "severityClickHint": "انقر للعرض", + "lastUpdated": "آخر تحديث", + "viewAll": "عرض الكلّ ←", + "recentVulns": "الثغرات الأخيرة", + "recentFacts": "الحقائق الأخيرة", + "noVulnYet": "لا توجد ثغرات أخيرة", + "noFactsYet": "لا توجد حقائق أخيرة", + "noFactsDesc": "في المحادثات المرتبطة بمشروع، يسجّل الوكيل الأهداف والنتائج وسلاسل الهجوم", + "createFirstProjectBtn": "إنشاء أوّل مشروع", + "factProjectMeta": "{{project}} · {{key}}", + "factsAcrossProjects_one": "{{count}} مشروع نشط · {{facts}} حقيقة", + "factsAcrossProjects_other": "{{count}} مشروع نشط · {{facts}} حقيقة", + "capabilities": "القدرات", + "mcpTools": "أدوات MCP", + "rolesLabel": "الأدوار", + "agentsLabel": "الوكلاء", + "webshellLabel": "WebShell", + "pendingCountLabel": "{{count}} معلّق", + "highCountLabel": "عالية {{count}}", + "toolsCountLabel_one": "{{count}} أداة", + "toolsCountLabel_other": "{{count}} أداة", + "failedNCalls_one": "{{count}} فاشل", + "failedNCalls_other": "{{count}} فاشل", + "noCompletedYet": "لا توجد نتائج مكتملة بعد", + "noCallYet": "لا توجد استدعاءات بعد", + "allClear": "لا توجد مخاطر جديدة", + "allIdle": "النظام خامل", + "executingNow": "قيد التشغيل", + "healthyStatus": "سليم", + "normalStatus": "جيّد في معظمه", + "degradedStatus": "يحتاج إلى انتباه", + "alertTitle": "تنبيه", + "alertWarningTitle": "يحتاج إلى انتباه", + "alertDangerTitle": "إجراء مطلوب", + "alertCriticalReason_one": "{{count}} ثغرة حرجة مفتوحة — يُرجى المراجعة فورًا", + "alertCriticalReason_other": "{{count}} ثغرة حرجة مفتوحة — يُرجى المراجعة فورًا", + "alertFailedReason_one": "معدّل نجاح الأدوات منخفض ({{count}} استدعاء فاشل) — تحقّق من مراقبة MCP", + "alertFailedReason_other": "معدّل نجاح الأدوات منخفض ({{count}} استدعاء فاشل) — تحقّق من مراقبة MCP", + "alertHitlReason_one": "{{count}} طلب تدخّل بشري معلّق — الوكيل ينتظر قرارك", + "alertHitlReason_other": "{{count}} طلب تدخّل بشري معلّق — الوكيل ينتظر قرارك", + "alertMcpDownReason_one": "{{count}} خادم MCP خارجي متوقّف — الأدوات المرتبطة به غير متاحة", + "alertMcpDownReason_other": "{{count}} خادم MCP خارجي متوقّف — الأدوات المرتبطة بها غير متاحة", + "alertDismiss": "تجاهل (لهذه الجلسة)", + "openHighCountLabel": "عالية مفتوحة {{count}}", + "allHandled": "تمّت معالجة جميع الثغرات عالية الخطورة", + "viewVulns": "عرض الثغرات", + "viewMonitor": "عرض المراقبة", + "viewHitl": "الموافقة", + "viewMcpManagement": "إدارة MCP", + "statusOpen": "مفتوحة", + "statusConfirmed": "مؤكّدة", + "statusFixed": "مصلَحة", + "statusFalsePositive": "إيجابية كاذبة", + "statusIgnored": "متجاهَلة", + "statusFilter": "تصفية الحالة", + "allStatuses": "جميع الحالات", + "fixRate": "معدّل الإصلاح", + "dataStale": "قد تكون البيانات قديمة — يُرجى التحديث", + "recommendedActions": "الإجراءات الموصى بها", + "recommendedActionsHint": "مُولَّدة بناءً على الحالة الراهنة", + "recoFixCritical_one": "إصلاح {{count}} ثغرة حرجة مفتوحة", + "recoFixCritical_other": "إصلاح {{count}} ثغرة حرجة مفتوحة", + "recoFixCriticalDesc": "ينبغي معالجة الثغرات ذات المستوى الحرج أوّلًا", + "recoApproveHitl_one": "الموافقة على {{count}} طلب تدخّل بشري", + "recoApproveHitl_other": "الموافقة على {{count}} طلب تدخّل بشري", + "recoApproveHitlDesc": "يحتاج الوكيل إلى قرارك للمتابعة", + "recoRestartMcp_one": "التحقّق من {{count}} خادم MCP خارجي متوقّف", + "recoRestartMcp_other": "التحقّق من {{count}} خادم MCP خارجي متوقّف", + "recoRestartMcpDesc": "الأدوات المرتبطة غير متاحة حتّى يتعافى MCP", + "recoCheckMonitor_one": "التحقيق في {{count}} استدعاء أداة فاشل", + "recoCheckMonitor_other": "التحقيق في {{count}} استدعاء أداة فاشل", + "recoCheckMonitorDesc": "اعرض تفاصيل الطلبات الفاشلة في مراقبة MCP", + "recoSetupMcp": "إعداد أوّل أداة MCP لديك", + "recoSetupMcpDesc": "ثبّت خادم MCP قبل أن يتمكّن الوكيل من استدعاء قدرات محدّدة", + "recoStartScan": "ابدأ فحصًا من المحادثة", + "recoStartScanDesc": "صِف هدفك في المحادثة، وسيساعد الذكاء الاصطناعي في التنفيذ", + "recentEvents": "الأحداث الأخيرة", + "eventUntitled": "حدث", + "externalMcpServers": "MCP الخارجي", + "mcpAllRunning": "الكلّ قيد التشغيل", + "mcpPartialDown_one": "{{count}} متوقّف", + "mcpPartialDown_other": "{{count}} متوقّف", + "mcpAllDown": "الكلّ متوقّف", + "noVulnDesc": "تعرض هذه القائمة السجلّات الأخيرة؛ تظهر النتائج الجديدة هنا عند اكتمال الكشف في المحادثة", + "startScanBtn": "انتقل إلى المحادثة للفحص", + "usageView": "عرض استخدام الرموز (Tokens)", + "usagePeriod": "الفترة", + "usage7Days": "آخر 7 أيّام", + "usage30Days": "آخر 30 يومًا", + "usage90Days": "آخر 90 يومًا", + "usageTotal": "إجمالي الرموز (Tokens)", + "usageInput": "رموز الإدخال", + "usageOutput": "رموز الإخراج", + "usageCacheRead": "قراءة من التخزين المؤقّت", + "usageCacheWrite": "كتابة في التخزين المؤقّت", + "usageReasoning": "رموز الاستدلال", + "usageCalls": "استدعاءات النموذج", + "usageCacheRate": "قراءة التخزين المؤقّت / الإدخال", + "usageByModel": "الاستخدام حسب النموذج (حتّى 20)", + "usageByDay": "الاستخدام اليومي", + "usageRecent": "عمليات التشغيل الأخيرة (حتّى 20)", + "usageDate": "التاريخ", + "usageTime": "الوقت", + "usageModel": "النموذج", + "usageStatus": "سبب الإنهاء", + "usageUnavailable": "استخدام الرموز (Tokens) غير متاح", + "usageScope": "الاستخدام المُبلَّغ من المزوّد من عمليات تشغيل الوكيل المسجّلة، بما في ذلك الوكلاء الفرعيون. قراءات التخزين المؤقّت وعمليات الكتابة فيه جزء من الإدخال؛ والاستدلال جزء من الإخراج. لا تُضاف هذه التفاصيل إلى الإجماليات مرّة أخرى. يُستبعَد الاستخدام غير المُبلَّغ أو غير المُجمَّع. هذه ليست فاتورة من المزوّد. تُجمَّع النماذج حسب إعداد التشغيل؛ وقد تحتوي عمليات التشغيل ذات التبديل الاحتياطي للنموذج على استخدام مختلط.", + "usageShare": "حصّة الرموز (Tokens)", + "usageTrend": "الاتّجاه اليومي للرموز (Tokens)", + "usageFinal": "مكتمل", + "usagePartial": "استخدام جزئي" + }, + "projects": { + "title": "المشاريع", + "showArchived": "عرض المؤرشفة", + "newProjectCta": "+ مشروع جديد", + "projectList": "قائمة المشاريع", + "searchProjectsPlaceholder": "ابحث في المشاريع…", + "paginationShow": "عرض {{start}}-{{end}} من {{total}}", + "paginationRange": "{{start}}-{{end}}/{{total}}", + "paginationTotal": "{{total}} إجمالاً", + "paginationPage": "{{page}}/{{total}}", + "paginationPerPage": "لكل صفحة", + "paginationPrev": "السابق", + "paginationNext": "التالي", + "selectOrCreateTitle": "اختر مشروعاً أو أنشئ واحداً", + "selectOrCreateHint": "تشترك المشاريع في لوحة حقائق متقاطعة بين المحادثات؛ تُحقن الأهداف والبيئة والمصادقة وغيرها من الحقائق تلقائياً في المحادثات المرتبطة.", + "createFirstProject": "إنشاء أول مشروع", + "defaultProjectName": "مشروع", + "statusActive": "نشط", + "statusArchived": "مؤرشف", + "vulnerabilityManagement": "إدارة الثغرات", + "addFactCta": "+ إضافة حقيقة", + "tabFacts": "لوحة الحقائق", + "tabGraph": "مسار الهجوم", + "tabAssets": "مكتبة الأصول", + "tabConversations": "المحادثات المرتبطة", + "tabVulns": "الثغرات ذات الصلة", + "tabSettings": "الإعدادات", + "boundAssetsHint": "تُعرض الأصول المرتبطة بهذا المشروع فقط؛ انقر على هدف لعرض التفاصيل الكاملة", + "noBoundAssets": "لا توجد أصول مرتبطة بهذا المشروع", + "assetCount": "{{count}} أصل", + "factToolbarHint": "يتضمّن الفهرس المفتاح والملخّص فقط (يجب أن يشمل ماذا + أين + كيفية التحقّق)؛ ضع سلسلة الهجوم / إثبات المفهوم في المتن، وأعد الإنتاج عبر get_project_fact.", + "graphToolbarHint": "تطابق أسهم الرسم روابط الحقائق المخزّنة (المصدر → الهدف). تُرتّب العُقد طبقياً: الهدف ← البنية التحتية ← النتيجة ← الاستغلال. الحواف المتقطّعة مبدئية.", + "graphView": "عرض", + "graphViewPath": "مسار الهجوم", + "graphViewFull": "الرسم الكامل", + "graphSearchSr": "البحث في العُقد", + "graphSearchPlaceholder": "ابحث في العُقد…", + "graphRefresh": "تحديث", + "graphCenter": "توسيط", + "graphEmpty": "لا توجد بيانات رسم بعد. أضف روابط على حقائق النتائج/الاستغلال (discovered_on → target/*) لبناء المسار.", + "graphEmptyTitle": "ابنِ مسار هجومك", + "graphEmptyStep1": "أضف حقائق الأهداف (النطاقات، نقاط النهاية، النطاق)", + "graphEmptyStep2": "سجّل النتائج/الاستغلالات مع روابط بين الحقائق", + "graphEmptyStep3": "استخدم وضع الربط أو عدّل الحقائق لإضافة العلاقات", + "graphEmptyCta": "إضافة أول حقيقة", + "graphStats": "العُقد: {{nodes}} | الحواف: {{edges}}", + "graphStatsNodes": "العُقد", + "graphStatsEdges": "الحواف", + "graphLegendNodes": "العُقد", + "graphLegendEdges": "الحواف", + "graphLegendNodeTarget": "هدف", + "graphLegendNodeInfra": "بنية تحتية", + "graphLegendNodeFinding": "نتيجة", + "graphLegendNodeVuln": "ثغرة", + "graphLegendNodeExploit": "استغلال", + "graphLegendNodeMissing": "مفقود", + "graphLegendDiscovered": "discovered_on", + "graphLegendLeads": "leads_to", + "graphLegendExploits": "exploits", + "graphLegendTentative": "مبدئي (متقطّع)", + "factLinksLabel": "الروابط (من → هذه الحقيقة)", + "factBodyRelationsHint": "أدخل العلاقات مثل discovered_on في حقل الروابط أدناه لإنشاء حواف الرسم.", + "factLinksPlaceholder": "discovered_on: target/primary_domain\nexploits: exploit/upload-rce", + "factLinksHint": "واحدة لكل سطر: type: source_fact_key (المصدر → هذه الحقيقة). الأنواع الشائعة: discovered_on، depends_on، leads_to، enables، exploits. يؤدّي الحفظ إلى استبدال جميع الروابط.", + "factIncomingLinksLabel": "الروابط الواردة (للقراءة فقط)", + "factIncomingLinksHint": "مُشتقّة من الروابط الصادرة على حقائق المصدر. مثال: نتيجة discovered_on → target/* تظهر كرابط وارد على الهدف؛ عدّل الروابط الصادرة لحقيقة المصدر.", + "factIncomingLinksEmpty": "لا توجد روابط واردة", + "graphEdgeFromSelf": "من هذه العُقدة", + "graphEdgeToSelf": "إلى هذه العُقدة", + "linksColumn": "الروابط", + "linkCountsTitle": "أعداد الحواف الصادرة / الواردة", + "graphConnect": "ربط", + "graphConnectActive": "جارٍ الربط…", + "graphConnectPickTarget": "تم تحديد المصدر {{source}} — انقر على العُقدة الهدف", + "graphEdgeTypePrompt": "نوع الحافة (discovered_on / leads_to / depends_on / enables / exploits)", + "graphConnectFailed": "فشل إنشاء الحافة", + "graphConnectSuccess": "تم إنشاء الحافة", + "graphEdgesTitle": "الروابط", + "graphEdgesHint": "يطابق اتجاه السهم قاعدة البيانات ونافذة التعديل (المصدر → الهدف). انقر على حافة للتركيز عليها.", + "graphEdgesEmpty": "لا توجد روابط بعد", + "graphEdgeOutgoing": "صادرة", + "graphEdgeIncoming": "واردة", + "graphEdgeSynthetic": "مُولّدة تلقائياً من رابط الحقيقة؛ عدّل الحقيقة للإزالة", + "confirmDeleteGraphEdge": "حذف هذا الرابط؟", + "graphEdgeDeleteFailed": "فشل حذف الحافة", + "graphEdgeDeleteSuccess": "تم حذف الحافة", + "graphDeleteEdge": "حذف", + "viewVulnerability": "عرض الثغرة", + "graphVulnSidebarHint": "عُقدة ثغرة مرتبطة. استخدم الزر أدناه لفتحها في إدارة الثغرات.", + "promoteAttackChain": "ترقية السلسلة", + "promoteAttackChainTitle": "ترقية سلسلة هجوم المحادثة إلى حقائق المشروع", + "confirmPromoteAttackChain": "ترقية سلسلة هجوم هذه المحادثة إلى المشروع؟ سيتمّ إنشاء أو تحديث الحقائق والحواف.", + "promoteAttackChainFailed": "فشلت الترقية", + "promoteAttackChainSuccess": "تمت الترقية: {{facts_created}} جديدة / {{facts_updated}} محدّثة / {{edges_created}} حافة", + "searchFactsSr": "البحث في الحقائق", + "searchFactsPlaceholder": "ابحث في المفتاح والملخّص والمتن…", + "category": "الفئة", + "all": "الكل", + "confidence": "مستوى الثقة", + "confidenceConfirmed": "مؤكَّد", + "confidenceTentative": "مبدئي", + "confidenceDeprecated": "مهمَل", + "displayOptions": "خيارات العرض", + "sparseOnly": "الناقصة فقط", + "hideDeprecated": "إخفاء المهمَلة", + "summary": "الملخّص", + "updated": "آخر تحديث", + "boundConversationsHint": "المحادثات المرتبطة بهذا المشروع؛ انقر للفتح", + "titleLabel": "العنوان", + "projectVulnSummaryHint": "ملخّص الثغرات ضمن هذا المشروع", + "searchVulnsSr": "البحث في الثغرات", + "searchVulnsPlaceholder": "ابحث في العنوان والوصف والنوع والهدف…", + "noMatchingVulns": "لا توجد ثغرات مطابقة، جرّب تعديل عوامل التصفية", + "viewInVulnerabilityManagement": "عرض في إدارة الثغرات", + "severity": "الخطورة", + "status": "الحالة", + "modalNewTitle": "مشروع جديد", + "modalNewSubtitle": "بعد الإنشاء، اربط المحادثات لمشاركة لوحة الحقائق عبر المحادثات", + "modalEditTitle": "تعديل المشروع", + "modalEditSubtitle": "تحديث اسم المشروع ووصفه", + "projectName": "اسم المشروع", + "projectNamePlaceholder": "مثال: اختبار اختراق ويب للعميل أ", + "projectDescription": "وصف المشروع", + "projectDescriptionPlaceholder": "ملاحظات العميل/المهمّة، تعاون الفريق…", + "createProject": "إنشاء مشروع", + "newProject": "مشروع جديد", + "chatSelectorButton": "شارك لوحة الحقائق عبر المحادثات بعد ربط مشروع", + "selectProject": "اختر مشروعاً", + "noProject": "بلا مشروع", + "factBodyEnvTitle": "حقيقة بيئة", + "factBodyHasDetail": "تحتوي تفاصيل", + "factBodySparseTitle": "تفتقر إلى بنية سلسلة الهجوم/إثبات المفهوم", + "factBodySparse": "غير مكتملة", + "factBodyReproducibleTitle": "تحتوي بنية قابلة لإعادة الإنتاج", + "factBodyReproducible": "قابلة لإعادة الإنتاج", + "factHintAttackSparse": "حقيقة سلسلة هجوم: املأ المتن بالكامل (الخطوات، HTTP/الأمر، دليل الاستجابة)؛ تجنّب الملاحظات الاستنتاجية فقط. يمكنك إدراج قالب سلسلة الهجوم.", + "factHintAttackReady": "حقيقة سلسلة هجوم: يُستخدم المتن لإعادة الإنتاج في التدقيق، حافظ على الطلب/الاستجابة الأصليين والتدفّق خطوة بخطوة.", + "factHintEnv": "حقيقة بيئة: ينبغي أن يتضمّن المتن مصدر الدليل؛ للنتائج/الاستغلال استخدم فئة finding|chain|exploit|poc.", + "confirmOverwriteBodyTemplate": "استبدال محتوى المتن الحالي بالقالب؟", + "loadProjectsFailed": "فشل تحميل المشاريع", + "restoreTitle": "الاستعادة كحقيقة مبدئية وإعادة فهرستها في اللوحة", + "restore": "استعادة", + "deprecateTitle": "وضع علامة كمهمَل", + "deprecate": "إهمال", + "editTitle": "تعديل الحقول", + "viewBodyTitle": "عرض المتن الكامل", + "details": "التفاصيل", + "deleteForeverTitle": "حذف نهائي", + "noProjects": "لا توجد مشاريع", + "noMatchingProjects": "لا توجد مشاريع مطابقة", + "pinned": "مثبَّت", + "archived": "مؤرشف", + "statsFacts": "{{count}} حقيقة", + "statsVulns": "{{count}} ثغرة", + "statsConversations": "{{count}} محادثة", + "statsSparse": "{{count}} غير مكتملة", + "projectNotFound": "المشروع غير موجود", + "updatedPrefix": "آخر تحديث {{time}}", + "descExpand": "عرض الكل", + "descCollapse": "عرض أقل", + "descriptionLengthHint": "للرجوع إليه من قِبل الفريق فقط — لا يقرؤه الوكيل. أبقِه موجزاً (4000 حرف كحدّ أقصى). ضع كل ما ينبغي أن يعرفه الوكيل في لوحة الحقائق.", + "noMatchingFacts": "لا توجد حقائق مطابقة، جرّب تعديل عوامل التصفية", + "noFacts": "لا توجد حقائق بعد. انقر على إضافة حقيقة أو دع الوكيل يكتب الحقائق تلقائياً", + "relatedVulnIdTitle": "مُعرّف الثغرة ذات الصلة", + "noBoundConversations": "لا توجد محادثات مرتبطة بعد؛ اختر هذا المشروع في المحادثة للربط", + "untitledConversation": "محادثة بلا عنوان", + "open": "فتح", + "unbindProjectTitle": "فصل المشروع", + "unbind": "فصل", + "unbindAssetConfirm": "فصل ”{{target}}“ عن هذا المشروع؟ لن يُحذف الأصل.", + "unbindAssetDone": "تم فصل الأصل عن المشروع", + "confirmUnbindConversation": "فصل هذه المحادثة عن المشروع الحالي؟", + "unbindFailed": "فشل الفصل", + "factMetaCategory": "الفئة: {{value}}", + "factMetaConfidence": "مستوى الثقة: {{value}}", + "factMetaUpdated": "آخر تحديث: {{time}}", + "factMetaRelatedVuln": "الثغرة ذات الصلة: {{value}}", + "factMetaSourceConversation": "محادثة المصدر: {{value}}", + "factMetaHasPrevious": "تحتوي نسخة سابقة", + "emptyBody": "(متن فارغ)", + "factSparseWarn": "تنتمي هذه الحقيقة إلى فئة سلسلة الهجوم/الاستغلال، لكن المتن يفتقر إلى بنية قابلة لإعادة الإنتاج (الخطوات، HTTP/الأمر، الطلب/الاستجابة، إلخ). عدّلها وأكملها لإعادة الإنتاج في التدقيق.", + "factPreviousMeta": "تمت الأرشفة في {{time}} · الملخّص: {{summary}} · مستوى الثقة: {{confidence}}", + "loadVulnerabilityListFailed": "فشل تحميل قائمة الثغرات", + "noVulnerabilitiesInProject": "لا توجد ثغرات في هذا المشروع بعد. أنشئ واحدة أولاً أو دع الوكيل يسجّلها.", + "promptLinkFactToVuln": "أدخل الفهرس لربط الحقيقة \"{{factKey}}\":\n\n{{lines}}", + "invalidIndex": "فهرس غير صالح", + "linkFailed": "فشل الربط", + "linkSuccess": "تم ربط الثغرة", + "promptConversationIdForVulnCreate": "مُعرّف المحادثة مطلوب لإنشاء ثغرة (يمكن أن يكون محادثة المصدر):", + "cancelledNoConversationId": "أُلغي: لم يُوفَّر conversation_id", + "createVulnerabilityFailed": "فشل إنشاء الثغرة", + "createVulnerabilityAndLinkSuccess": "تم إنشاء الثغرة وربطها: {{value}}", + "confirmDeprecateFact": "وضع علامة على الحقيقة {{factKey}} كمهمَلة؟", + "operationFailed": "فشلت العملية", + "confirmRestoreFact": "استعادة الحقيقة {{factKey}}؟ ستُعاد إلى فهرس اللوحة بحالة مبدئية.", + "noVulnerabilityRecords": "لا توجد سجلّات ثغرات في هذا المشروع", + "viewRelatedFactsTitle": "عرض الحقائق ذات الصلة", + "facts": "الحقائق", + "loadRelatedFactsFailed": "فشل تحميل الحقائق ذات الصلة", + "noFactsForVulnerability": "لا توجد حقائق ذات صلة بهذه الثغرة بعد. اربط ثغرة أو أنشئ مسوّدة ثغرة من تفاصيل الحقيقة.", + "promptChooseFactByIndex": "هذه الثغرة مرتبطة بـ {{count}} حقيقة. أدخل الفهرس للعرض:\n{{lines}}", + "enterProjectName": "الرجاء إدخال اسم المشروع", + "writePermissionDenied": "هذا الحساب للقراءة فقط ولا يمكنه إنشاء المشاريع أو تعديلها", + "saveFailed": "فشل الحفظ", + "invalidJson": "تنسيق JSON غير صالح", + "scopeNoteAuthorizedWebOnly": "مُصرَّح به لاختبار طبقة تطبيقات الويب فقط", + "invalidScopeJson": "نطاق JSON غير صالح، يُرجى إصلاحه أولاً أو النقر على تنسيق", + "saved": "تم الحفظ", + "confirmArchiveProject": "بعد الأرشفة، يُخفى هذا المشروع من القائمة النشطة افتراضياً. المتابعة؟", + "confirmRestoreProjectActive": "الاستعادة إلى الحالة النشطة؟", + "confirmDeleteProject": "حذف هذا المشروع؟ ستُحذف الحقائق وتُفصل المحادثات.", + "deleteFailed": "فشل الحذف", + "addFact": "إضافة حقيقة", + "saveFact": "حفظ الحقيقة", + "editFact": "تعديل الحقيقة", + "saveChanges": "حفظ التغييرات", + "customCategoryOption": "{{value}} (مخصّص)", + "selectProjectFirst": "الرجاء اختيار مشروع أولاً", + "loadFactFailed": "فشل تحميل الحقيقة", + "factKeySummaryRequired": "fact_key والملخّص مطلوبان", + "confirmSaveSparseFact": "تتعلّق هذه الحقيقة بسلسلة الهجوم/الاستغلال، لكن المتن لا يحتوي بنية قابلة لإعادة الإنتاج (الخطوات، HTTP/الأمر، الطلب/الاستجابة).\nهل تريد الحفظ على أيّ حال؟ يُوصى بإدراج قالب سلسلة الهجوم وملء إثبات المفهوم أولاً.", + "confirmDeleteFact": "حذف هذه الحقيقة؟", + "notUpdatedYet": "لم يُحدَّث بعد", + "clearStaleProjectBindingFailed": "فشل مسح ارتباط المشروع القديم", + "noProjectDescription": "لا يوجد ارتباط بمشروع", + "noProjectsClickCreate": "لا توجد مشاريع بعد، انقر على مشروع جديد أدناه", + "sharedFactBoard": "لوحة حقائق مشتركة", + "loadFailedRetry": "فشل التحميل، يُرجى إعادة المحاولة لاحقاً", + "projectBound": "تم ربط المشروع", + "projectUnbound": "تم فصل المشروع", + "updateProjectBindingFailed": "فشل تحديث ارتباط المشروع", + "basicInfoTitle": "المعلومات الأساسية", + "basicInfoHint": "يظهر الاسم والوصف في تفاصيل المشروع", + "settingsIntroTitle": "إعدادات المشروع", + "settingsIntroHint": "اضبط بيانات المشروع الوصفية وحدود تصريح الوكيل؛ تسري فوراً على المحادثات المرتبطة بعد الحفظ.", + "pinProject": "تثبيت المشروع (عرضه أولاً في القائمة)", + "pinProjectAction": "تثبيت المشروع", + "unpinProjectAction": "إلغاء تثبيت المشروع", + "pinFact": "تثبيت الحقيقة (إعطاؤها الأولوية في القائمة وفهرس اللوحة)", + "editDescriptionPlaceholder": "ملاحظات العميل/المهمّة، جهات الاتصال، التعاون…", + "scopeTitle": "نطاق الاختبار", + "scopeHint": "تنسيق JSON لحدود تصريح الوكيل والأصول المستهدفة", + "formatJson": "تنسيق", + "example": "مثال", + "scopeJsonLabel": "نطاق JSON", + "scopeFootnote": "يدعم الأهداف والاستثناءات والملاحظات وغيرها. الفارغ يعني عدم وجود حدّ للنطاق.", + "dangerZoneTitle": "منطقة الخطر", + "dangerZoneHint": "تُخفى المشاريع المؤرشفة ما لم يُفعَّل ”عرض المؤرشفة“؛ ويزيل الحذف جميع الحقائق نهائياً.", + "archiveRestore": "أرشفة / استعادة", + "archiveProject": "أرشفة", + "editProject": "تعديل", + "renameProject": "إعادة تسمية", + "restoreProjectActive": "الاستعادة إلى الحالة النشطة", + "projectActions": "إجراءات المشروع", + "deleteProject": "حذف المشروع", + "saveChangesHint": "انقر على حفظ لمزامنة التغييرات مع الخادم", + "saveSettings": "حفظ التغييرات", + "factModalSubtitle": "يُفهرس الملخّص في اللوحة؛ ويخزّن المتن سلسلة الهجوم وإثبات المفهوم لإعادة الإنتاج في التدقيق (منفصلاً عن سجلّات الثغرات).", + "relatedVulnIdLabel": "مُعرّف الثغرة ذات الصلة", + "optional": "اختياري", + "factDetails": "تفاصيل الحقيقة", + "previousVersion": "النسخة السابقة", + "currentVersion": "النسخة الحالية", + "linkVulnerability": "ربط ثغرة", + "createVulnerabilityDraft": "إنشاء مسوّدة ثغرة", + "generatedFromFact": "مُولّدة من حقيقة المشروع {{factKey}}" + }, + "chat": { + "toolExecBlocked": "تم حظر الأداة {{name}}", + "newChat": "محادثة جديدة", + "newTask": "مهمّة جديدة", + "toggleConversationPanel": "طيّ/توسيع قائمة المحادثات", + "searchHistory": "ابحث في السجلّ...", + "projectFolders": "المشاريع", + "projectFoldersLoadMoreRemaining": "تحميل المزيد، تبقّى {{count}} مشروع", + "projectPreviewLabel": "معلومات المشروع", + "projectPreviewStats": "{{total}} مهمّة · {{active}} مفتوحة", + "projectPreviewNoDescription": "لا يوجد وصف للمشروع", + "projectPreviewScope": "نطاق الاختبار: {{scope}}", + "projectPreviewEdit": "تعديل المشروع", + "conversationPreviewJustNow": "الآن", + "conversationPreviewMinutes": "{{count}} دقيقة", + "conversationPreviewHours": "{{count}} ساعة", + "conversationPreviewDays": "{{count}} يوم", + "conversationPreviewDateTime": "{{year}}-{{month}}-{{day}} {{hour}}:{{minute}}", + "conversationPreviewNoProject": "بلا مشروع", + "conversationPreviewDefaultMode": "افتراضي", + "conversationPreviewUnread": "تحديث غير مقروء", + "conversationPreviewViewed": "تمت المشاهدة", + "conversationPreviewConversation": "محادثة", + "returnToLatest": "الانتقال إلى أحدث رسالة", + "returnToLatestProcessDetail": "الانتقال إلى أحدث تكرار", + "taskProgressStep": "الخطوة {{current}} من {{total}}", + "taskProgressOpen": "عرض تقدّم المهمّة", + "taskProgressDetails": "تفاصيل تقدّم المهمّة", + "taskProgressUnnamed": "مهمّة بلا عنوان", + "completedUnread": "مكتملة، لم تُشاهَد", + "newConversationInProject": "ابدأ محادثة جديدة في هذا المشروع", + "newUnassignedConversation": "ابدأ محادثة جديدة بدون مشروع", + "conversationActions": "إجراءات المحادثة", + "renameConversationPrompt": "أدخل عنواناً جديداً:", + "renameConversationTitle": "إعادة تسمية المحادثة", + "renameConversationSubtitle": "سيُحدَّث الاسم في مجلّدات المشاريع والمحادثات الأخيرة", + "conversationTitleLabel": "اسم المحادثة", + "conversationTitlePlaceholder": "أدخل اسم محادثة", + "recentConversations": "المحادثات الأخيرة", + "toggleRecentConversations": "توسيع/طيّ المحادثات الأخيرة", + "filterByProject": "تصفية حسب المشروع", + "filterAllProjects": "جميع المشاريع", + "filterUnboundProjects": "غير مرتبطة", + "filterProjectSearch": "ابحث في المشاريع…", + "filterProjectSearchEmpty": "لا توجد مشاريع مطابقة", + "filterProjectSearchHint": "اكتب للبحث في المشاريع", + "filterProjectSearchMore": "اكتب للعثور على مزيد من المشاريع", + "filterProjectSearchLoading": "جارٍ البحث…", + "filterProjectSearchFailed": "فشل تحميل المشاريع. حاول مجدداً.", + "projectConversationsTitle": "{{name}} · المحادثات", + "unboundConversationsTitle": "المحادثات غير المرتبطة", + "noProjectConversations": "لا توجد محادثات في هذا المشروع", + "noUnboundConversations": "لا توجد محادثات غير مرتبطة", + "sortConversations": "فرز", + "sortByCreatedAt": "وقت الإنشاء", + "sortByUpdatedAt": "وقت التحديث", + "batchManage": "إدارة مجمّعة", + "paginationShow": "عرض {{start}}-{{end}} من {{total}}", + "paginationRange": "{{start}}-{{end}}/{{total}}", + "paginationTotal": "{{total}} إجمالاً", + "paginationPage": "{{page}}/{{total}}", + "paginationPerPage": "لكل صفحة", + "paginationPrev": "السابق", + "paginationNext": "التالي", + "attackChain": "سلسلة الهجوم", + "viewAttackChain": "عرض سلسلة الهجوم", + "selectRole": "اختر دوراً", + "defaultRole": "افتراضي", + "inputPlaceholder": "أدخل هدفاً أو أمراً… @ لاختيار الأدوات", + "selectFile": "اختر ملفاً", + "uploadFile": "رفع ملف (تحديد متعدّد أو سحب وإفلات)", + "readingAttachmentsDetail": "قراءة المرفق {{current}}/{{total}} · {{name}} · {{percent}}%", + "uploadingAttachmentsDetail": "رفع المرفقات · {{done}}/{{total}} مكتمل · {{percent}}% إجمالاً", + "waitingAttachmentsUpload": "بانتظار انتهاء رفع المرفقات…", + "attachmentsUploadIncomplete": "فشل رفع بعض المرفقات. أزل العناصر الفاشلة أو اختر الملفات مجدداً قبل الإرسال.", + "attachmentUploading": "جارٍ الرفع…", + "attachmentUploadFailed": "فشل", + "attachmentUploadAlert": "فشل الرفع: {{name}}", + "send": "إرسال", + "loadingTools": "جارٍ تحميل الأدوات...", + "noMatchTools": "لا توجد أدوات مطابقة", + "penetrationTestDetail": "تفاصيل تنفيذ المهمّة", + "expandDetail": "توسيع التفاصيل", + "turnElapsedRunning": "قيد المعالجة منذ {{duration}}", + "turnElapsedComplete": "استغرق {{duration}}", + "turnElapsedCancelled": "مُقاطَع · استغرق {{duration}}", + "turnElapsedTimeout": "انتهت المهلة · استغرق {{duration}}", + "turnElapsedFailed": "فشل · استغرق {{duration}}", + "turnDurationSeconds": "{{seconds}} ثانية", + "turnDurationMinutes": "{{minutes}} دقيقة {{seconds}} ثانية", + "turnDurationHours": "{{hours}} ساعة {{minutes}} دقيقة", + "turnProcessAria": "{{state}}؛ وسّع أو اطوِ تفاصيل التنفيذ", + "turnTokenUsageLabel": "{{tokens}} رمز", + "turnTokenUsageTitle": "استخدام الرموز (Tokens): {{total}} (الإدخال {{prompt}}، الإخراج {{completion}}، المخزّن {{cached}}، الاستدلال {{reasoning}}، {{calls}} استدعاء) · كتابة التخزين المؤقّت {{cacheWrite}}", + "turnNumber": "الجولة {{number}}", + "turnPending": "جارٍ المعالجة…", + "expandDetailLazyHint": "توسيع التفاصيل (يحمّل تفاصيل التكرار عند النقر)", + "loadingEarlierDetails": "جارٍ تحميل الإدخالات الأقدم…", + "loadingLaterDetails": "جارٍ تحميل الإدخالات الأحدث…", + "viewToolDetail": "عرض التفاصيل", + "collapseToolDetail": "طيّ", + "liveTimelinePruned": "تم طيّ أول {{count}} من تفاصيل العمليات المباشرة. اعرض السجلّ الكامل صفحةً بصفحة بعد اكتمال المهمّة.", + "liveTimelinePrunedSingleRound": "جولة الوكيل الرئيسي {{n}}", + "liveTimelinePrunedRoundRange": "جولات الوكيل الرئيسي {{from}}–{{to}}", + "toolExecutionsCount": "{{n}} عملية تشغيل أداة", + "collapseToolExecutions": "طيّ عمليات تشغيل الأدوات", + "toolExecutionDetailPending": "لم تتمّ مزامنة تفاصيل تنفيذ الأداة بعد. يُرجى المحاولة مجدداً بعد قليل.", + "noProcessDetail": "لا توجد تفاصيل عملية (قد يكون التنفيذ سريعاً جداً أو لا توجد أحداث مفصّلة)", + "copyMessageTitle": "نسخ الرسالة", + "deleteTurnTitle": "حذف هذه الجولة", + "deleteTurnConfirm": "حذف هذه الجولة بالكامل (رسالة المستخدم وردّ المساعد)؟ لا يمكن التراجع عن هذا. سيستخدم الردّ التالي الرسائل المتبقّية فقط؛ وستُمسح لقطات السياق المحفوظة.", + "deleteTurnFailed": "فشل حذف الجولة", + "noHistoryConversations": "لا يوجد سجلّ محادثات بعد", + "deleteConversationConfirm": "حذف هذه المحادثة؟ لا يمكن استرجاع رسائل المحادثة، لكن الثغرات المسجّلة ستبقى في مكتبة الثغرات.", + "renameFailed": "فشلت إعادة التسمية", + "downloadConversationFailed": "فشل تنزيل المحادثة", + "viewAttackChainSelectConv": "يُرجى اختيار محادثة لعرض سلسلة الهجوم", + "viewAttackChainCurrentConv": "عرض سلسلة هجوم المحادثة الحالية", + "executeFailed": "فشل التنفيذ", + "callOpenAIFailed": "فشل استدعاء OpenAI", + "systemReadyMessage": "النظام جاهز. يُرجى إدخال متطلّبات الاختبار، وسيُجري النظام تلقائياً الاختبارات الأمنية المقابلة.", + "projectWelcomeMessage": "المشروع الحالي: {{project}}. أدخل متطلّبات الاختبار وسيُجري النظام الاختبارات الأمنية المقابلة.", + "noProjectWelcomeMessage": "لم يُحدَّد أيّ مشروع حالياً. أدخل متطلّبات الاختبار وسيُجري النظام الاختبارات الأمنية المقابلة.", + "projectWelcomeTitlePrefix": "ما الذي ينبغي اختباره في ", + "projectWelcomeTitleSuffix": "؟", + "noProjectWelcomeTitle": "ما الذي ينبغي اختباره؟", + "welcomeSubtitle": "أدخل متطلّبات الاختبار وسيُجري النظام تلقائياً الاختبارات الأمنية المقابلة.", + "callNumber": "الاستدعاء رقم {{n}}", + "iterationRound": "التكرار {{n}}", + "einoOrchestratorRound": "المنسّق · الجولة {{n}}", + "einoPlanExecuteRound": "التخطيط والتنفيذ · الجولة {{n}} · {{phase}}", + "planExecuteStreamPlanner": "مخرجات التخطيط", + "planExecuteStreamExecutor": "مخرجات التنفيذ", + "planExecuteStreamReplanning": "مخرجات إعادة التخطيط", + "planExecuteStreamPhase": "مخرجات المرحلة", + "einoSubAgentStep": "الوكيل الفرعي {{agent}} · الخطوة {{n}}", + "aiThinking": "تفكير الذكاء الاصطناعي", + "reasoningChain": "عملية الاستدلال", + "planning": "التخطيط", + "assistantStreamPhase": "مخرجات المساعد", + "toolCallsDetected": "تم رصد {{count}} استدعاء أداة", + "callTool": "استدعاء الأداة: {{name}} ({{index}}/{{total}})", + "toolExecComplete": "اكتملت الأداة {{name}}", + "toolExecFailed": "فشلت الأداة {{name}}", + "knowledgeRetrieval": "استرجاع المعرفة", + "knowledgeRetrievalTag": "استرجاع المعرفة", + "error": "خطأ", + "streamNetworkErrorHint": "انقطع الاتصال ({{detail}}). قد تظلّ مهمّة طويلة قيد التشغيل على الخادم؛ تحقّق من المهامّ الجارية في الأعلى أو حدّث هذه المحادثة لاحقاً.", + "streamEndedWithoutDone": "انتهى الاتصال قبل تلقّي إشارة الاكتمال. قد تظلّ المهمّة قيد التشغيل على الخادم؛ تحقّق من المهامّ الجارية في الأعلى أو حدّث هذه المحادثة.", + "taskCancelled": "تم إلغاء المهمّة", + "userInterruptContinueTitle": "⏸️ مقاطعة المستخدم والمتابعة", + "unknownTool": "أداة غير معروفة", + "einoAgentReplyTitle": "ردّ الوكيل الفرعي", + "einoStreamErrorTitle": "⚠️ انقطع بثّ Eino ({{agent}})", + "einoStreamErrorMessage": "فشلت قراءة البثّ؛ سيعيد النظام المحاولة أو ينهيها وفقاً للسياسة.", + "einoRunRetryTitle": "🔁 إعادة محاولة خطأ عابر", + "einoModelRetryTitle": "🔁 إعادة محاولة استدعاء النموذج", + "einoModelFailoverTitle": "🔀 التبديل إلى نموذج احتياطي", + "einoUsageSummaryTitle": "ملخّص استخدام الرموز (Tokens)", + "einoUsageModelCalls": "استدعاءات النموذج", + "einoUsagePromptTokens": "رموز الإدخال", + "einoUsageCompletionTokens": "رموز الإخراج", + "einoUsageTotalTokens": "إجمالي الرموز", + "einoUsageCachedTokens": "الرموز المخزّنة مؤقّتاً", + "einoUsageReasoningTokens": "رموز الاستدلال", + "einoEmptyResponseContinueTitle": "🔁 الاستئناف التلقائي (لا يوجد نصّ من المساعد)", + "einoEmptyResponseContinueMessage": "انتهت الجلسة دون التقاط نصّ من المساعد؛ جارٍ الاستئناف من التتبّع…", + "einoRunRetryPlan": "تقدّم إعادة المحاولة: المحاولة {{attempt}}/{{maxAttempts}}، الانتظار {{backoffSec}} ث", + "einoRunRetryReasonKind": "نوع السبب", + "einoRunRetryErrorDetail": "تفاصيل الخطأ", + "einoRunRetryKind_rate_limit": "تجاوز حدّ المعدّل / عدد الطلبات كبير جداً", + "einoRunRetryKind_retryable_http": "خطأ HTTP قابل لإعادة المحاولة", + "einoRunRetryKind_upstream_server": "خطأ في الخادم الأعلى", + "einoRunRetryKind_http_error": "خطأ HTTP", + "einoRunRetryKind_upstream_busy": "الخادم الأعلى مشغول", + "einoRunRetryKind_network": "مشكلة في اتصال الشبكة", + "einoRunRetryKind_stream": "مشكلة في قراءة البثّ", + "einoRunRetryKind_transient": "مشكلة عابرة", + "iterationLimitReachedTitle": "⛔ تمّ بلوغ حدّ التكرار", + "iterationLimitReachedMessage": "تمّ بلوغ الحدّ الأقصى لعدد التكرارات؛ توقّف التكرار التلقائي.", + "einoPendingOrphanedTitle": "🧹 تسوية استدعاءات الأدوات", + "einoPendingOrphanedMessage": "تم رصد {{count}} استدعاء أداة غير مغلق؛ وُضعت علامة فشل عليها وأُنهيت تلقائياً.", + "noDescription": "لا يوجد وصف", + "noResponseData": "لا توجد بيانات استجابة", + "loading": "جارٍ التحميل...", + "loadFailed": "فشل التحميل: {{message}}", + "noAttackChainData": "لا توجد بيانات سلسلة هجوم", + "copyFailedManual": "فشل النسخ، يُرجى التحديد والنسخ يدوياً", + "searching": "جارٍ البحث...", + "loadFailedRetry": "فشل التحميل، يُرجى إعادة المحاولة", + "dataFormatError": "خطأ في تنسيق البيانات", + "progressInProgress": "اختبار الاختراق قيد التنفيذ...", + "executionFailed": "فشل التنفيذ", + "penetrationTestComplete": "اكتمل اختبار الاختراق", + "yesterday": "أمس", + "historyGroupToday": "اليوم", + "historyGroupLast7Days": "آخر 7 أيام", + "historyGroupEarlier": "أقدم", + "agentModeSelectAria": "اختر وضع تنفيذ المحادثة", + "agentModePanelTitle": "وضع المحادثة", + "agentModeEinoSingle": "Eino مفرد (ADK)", + "agentModeEinoSingleHint": "Eino ChatModelAgent + Runner مع أدوات MCP ‏(/api/eino-agent)", + "agentModeDeep": "عميق (DeepAgent)", + "agentModeDeepHint": "Eino DeepAgent للاختبارات الأمنية المعقّدة، وتفويض المهامّ متعدّد المراحل، والتركيب", + "agentModePlanExecuteLabel": "التخطيط والتنفيذ", + "agentModePlanExecuteHint": "التخطيط ← التنفيذ ← إعادة التخطيط (مُنفّذ واحد مع أدوات)", + "agentModeSupervisorLabel": "المشرف (توجيه الخبراء)", + "agentModeSupervisorHint": "سيناريوهات توجيه الخبراء: التوجيه عبر عدّة وكلاء فرعيين متخصّصين عن طريق التحويل", + "agentModeSingle": "وكيل مفرد", + "agentModeMulti": "تعدّد الوكلاء", + "agentModeSingleHint": "Eino ADK وكيل مفرد للمحادثة واستخدام الأدوات", + "agentModeMultiHint": "تنسيق Eino الجاهز: عميق للمهامّ المعقّدة، والتخطيط والتنفيذ للحلقات المنظّمة، والمشرف لتوجيه الخبراء", + "reasoningModeLabel": "استدلال النموذج", + "reasoningEffortLabel": "جهد الاستدلال", + "reasoningModeDefault": "استخدام الافتراضي للنظام", + "reasoningModeOff": "إيقاف", + "reasoningModeOn": "تشغيل", + "reasoningModeAuto": "تلقائي", + "reasoningEffortUnset": "غير محدَّد", + "reasoningCompactLabel": "الاستدلال", + "reasoningCompactAria": "فتح خيارات استدلال النموذج", + "reasoningPanelTitle": "استدلال النموذج", + "reasoningPanelHint": "تستخدمها طلبات Eino المفردة ومتعدّدة الوكلاء فقط؛ تُدمج مع الافتراضيات في الإعدادات.", + "sessionSettingsTitle": "إعدادات الجلسة", + "sessionSettingsAria": "فتح إعدادات الجلسة", + "sessionSettingsHint": "تؤثّر إعدادات التدخّل البشري في الرسائل المستقبلية فقط.", + "sessionShortcutAuditAgent": "مراجعة الوكيل", + "modelSettingsAria": "اختر قناة الذكاء الاصطناعي والنموذج وإعدادات الاستدلال", + "systemModelPickerTitle": "اختر نموذج النظام", + "systemModelField": "النموذج", + "systemModelLoading": "جارٍ جلب قائمة النماذج…", + "systemModelLoaded": "تم تحميل {count} نموذج", + "systemModelCurrent": "الحالي", + "systemModelSaving": "جارٍ الحفظ…", + "systemModelSaved": "تم الحفظ تلقائياً", + "reasoningSessionUpdated": "تم تحديث استدلال الجلسة", + "systemModelLoadFailed": "فشل جلب النماذج", + "systemModelSaveFailed": "فشل حفظ النموذج", + "systemModelApplyFailed": "فشل تطبيق النموذج", + "systemModelNeedApiKey": "اضبط مفتاح API في إعدادات النظام أولاً", + "systemModelRetry": "حاول مجدداً", + "sessionShortcutHuman": "موافقة بشرية", + "aiChannelLabel": "قناة الذكاء الاصطناعي", + "aiChannelDefault": "استخدام القناة الافتراضية", + "aiChannelDefaultShort": "القناة الافتراضية", + "reasoningSummaryFollow": "النظام", + "reasoningSummaryDash": "—", + "agentModeOrchPlanExecute": "تخطيط-تنفيذ", + "agentModeOrchSupervisor": "المشرف", + "hitlTitle": "التدخّل البشري (HITL)", + "hitlCardSubtitle": "الموافقات وقائمة السماح", + "hitlReviewerLabel": "المراجِع", + "hitlReviewerHuman": "موافقة بشرية", + "hitlReviewerAgent": "وكيل التدقيق", + "hitlReviewerHint": "بدّل بين الإنسان ووكيل التدقيق في أيّ وقت؛ تبقى القواعد وقائمة السماح كما هي. يمكنك الاختيار المسبق حتى عند إيقاف التدخّل البشري.", + "hitlConfigTitle": "إعداد وضع التعاون", + "hitlModeLabel": "الوضع", + "hitlModeOff": "إيقاف", + "hitlModeApproval": "الموافقة", + "hitlModeReviewEdit": "المراجعة والتعديل", + "hitlSensitiveTools": "الأدوات الحسّاسة (مفصولة بفواصل)", + "hitlWhitelistTools": "الأدوات المدرجة في قائمة السماح (تتخطّى الموافقة، مفصولة بفواصل)", + "hitlWhitelistPlaceholder": "مثال: read_file، grep أو أداة واحدة لكل سطر (تُدمج مع قائمة السماح العامة في الإعداد)", + "hitlWhitelistHint": "افصل بفواصل أو أسطر جديدة؛ تُعرض مدموجة مع قائمة السماح العامة في الإعداد.", + "hitlApply": "تطبيق", + "hitlApplyOkSync": "تم حفظ إعدادات التدخّل البشري ومزامنتها مع الخادم.", + "hitlApplyOkDefaultConfig": "تم حفظ إعدادات التدخّل البشري الافتراضية في config.yaml وتفعيلها.", + "hitlApplyOkWhitelistYaml": "تم دمج قائمة سماح الأدوات في config.yaml وتفعيلها. تُحفظ إعدادات الجلسة تلقائياً.", + "hitlApplyOkLocal": "تم الحفظ في هذا المتصفّح.", + "hitlApplyFail": "فشلت المزامنة مع الخادم", + "hitlTimeoutLabel": "حدّ انتظار الموافقة", + "hitlTimeoutOneMinute": "دقيقة واحدة", + "hitlTimeoutFiveMinutes": "5 دقائق", + "hitlTimeoutTenMinutes": "10 دقائق", + "hitlTimeoutUnlimited": "بلا حدّ", + "hitlTimeoutHint": "تُرفض الطلبات غير المُجاب عنها تلقائياً عند انتهاء الوقت؛ تعرض بطاقات الموافقة العدّ التنازلي.", + "hitlStatusOff": "التدخّل البشري: إيقاف", + "rolePanelTitle": "اختر دوراً", + "einoUsageCacheWriteTokens": "رموز كتابة التخزين المؤقّت" + }, + "toolGuard": { + "invalidTestResponse": "أعاد الخادم نتيجة اختبار غير صالحة", + "fieldTooLong": "أسماء القواعد محدودة بـ 200 بايت UTF-8؛ والأنماط والرسائل بـ 4096 بايت UTF-8 لكلّ منها. اختصر المحتوى وأعد المحاولة.", + "tooManyRules": "يمكنك ضبط ما يصل إلى 100 قاعدة حظر.", + "title": "حظر الاستدعاءات", + "description": "تحقّق من أسماء أدوات MCP ووسائطها قبل التنفيذ وأوقِف الاستدعاءات المطابِقة. لا تتجاوز هذه الفحوص موافقاتُ التدخّل البشري أو قوائمُ الأدوات المسموح بها.", + "reset": "تجاهل التغييرات", + "save": "حفظ وتطبيق", + "policyTitle": "سياسة الحظر", + "enableHint": "يتحقّق من المخاطر قبل تنفيذ الأداة. احفظ لتطبيق التغييرات.", + "enabled": "تفعيل حظر الاستدعاءات", + "scopeHint": "تفحص القواعد نصّ الاستدعاء المرئي فقط. ولا تحسم ملكية عناوين IP أو وجهات إعادة التوجيه أو محتويات الملفات التي تقرؤها الأدوات. ولا يمكن لقواعد النطاقات أن تغطّي كلّ وصول غير مباشر.", + "rulesTitle": "قواعد الحظر", + "rulesHint": "تُفحص القواعد المفعّلة بالترتيب؛ ويوفّر أول تطابق رسالة الحظر. تستخدم الأنماط صيغة RE2 ويتحقّق منها الخادم عند الحفظ أو الاختبار.", + "addRule": "إضافة قاعدة", + "addDialogTitle": "إضافة قاعدة حظر", + "addDialogHint": "اضبط قاعدتك وجرّب وسائط نموذجية قبل إضافتها إلى القائمة.", + "addToList": "إضافة إلى القائمة", + "addToListHint": "بعد إضافة القاعدة، اختر ”حفظ وتطبيق“ في أعلى الصفحة لتفعيلها.", + "cancelAdd": "إلغاء", + "closeDialog": "إغلاق مربّع حوار إضافة القاعدة", + "singleTestTitle": "اختبار هذه القاعدة", + "closeRuleTest": "إخفاء اختبار القاعدة", + "singleTestHint": "اختبر المسوّدة الحالية بصورة مستقلّة قبل الحفظ. لا تُنفَّذ أيّ أدوات.", + "testing": "جارٍ الاختبار…", + "testReadyHint": "انقر ”اختبار التطابق“ لعرض النتيجة هنا.", + "testResultLabel": "نتيجة الاختبار", + "testChanged": "تغيّر المحتوى. أعد تشغيل الاختبار.", + "ruleAdded": "تمت إضافة القاعدة إلى القائمة. احفظ لتطبيقها.", + "addingRule": "جارٍ التحقّق من القاعدة…", + "addFailed": "فشل إضافة القاعدة", + "messageHint": "تدعم الرسائل {match} (النص المطابَق) و{tool} (اسم الأداة) و{rule} (اسم القاعدة).", + "testTitle": "اختبار كل القواعد", + "testHint": "يتحقّق من إعداد الصفحة الحالي، بما في ذلك التغييرات غير المحفوظة. لا يُنفّذ هذا الاختبار أيّ أدوات.", + "test": "اختبار التطابق", + "testTool": "اسم الأداة", + "testArguments": "وسائط الأداة (كائن JSON)", + "loading": "جارٍ المعالجة…", + "unsaved": "تغييرات غير محفوظة", + "savedState": "مُزامَن مع الخادم", + "emptyRules": "لا توجد قواعد بعد. أضف قاعدة واحفظها لتفعيل فحوصها.", + "rule": "قاعدة", + "ruleEnabled": "مفعّلة", + "deleteRule": "حذف", + "ruleName": "اسم القاعدة", + "pattern": "النمط (RE2)", + "message": "رسالة الحظر", + "requestFailed": "فشل الطلب", + "invalidResponse": "أعاد الخادم إعدادًا غير صالح", + "loadFailed": "فشل تحميل إعداد الحظر", + "loadFirst": "حمّل إعداد الحظر أولًا", + "requiredFields": "أدخل اسمًا ونمطًا لكل قاعدة", + "saveSuccess": "تم حفظ إعداد حظر الاستدعاءات. ستستخدم استدعاءات الأدوات اللاحقة الإعداد الجديد.", + "saveConflict": "عدّل مستخدم آخر إعداد اعتراض الاستدعاءات. لم تُحفظ تغييراتك. حدّث الصفحة لتحميل أحدث إعداد وأعد المحاولة.", + "versionRequired": "إصدار إعداد اعتراض الاستدعاءات مفقود. حدّث الصفحة وأعد المحاولة.", + "saveFailed": "فشل الحفظ", + "defaultMessage": "تم اكتشاف {match}. حظرت القاعدة ”{rule}“ هذا الاستدعاء. راجع الهدف والعملية قبل إعادة المحاولة.", + "blocked": "تطابقت قاعدة: سيتم حظر هذا الاستدعاء", + "notBlocked": "لم تتطابق أيّ قاعدة مفعّلة", + "disabledResult": "حظر الاستدعاءات معطَّل: لن يحظر هذا الإعداد أيّ استدعاءات", + "matchedRule": "القاعدة المطابِقة", + "matchedText": "النص المطابَق", + "matchedMessage": "رسالة الحظر المُعادة", + "toolRequired": "أدخل اسم أداة لاختبارها", + "invalidArguments": "يجب أن تكون وسائط الأداة كائن JSON صالحًا", + "testFailed": "فشل الاختبار", + "listHint": "حدّد قاعدة لعرضها أو تعديلها. تُفحص القواعد بترتيب القائمة.", + "helpTitle": "صيغة القواعد ونطاق التطابق", + "testSummary": "تحقّق من الحظر الإجمالي بترتيب القواعد وحالات التفعيل الحالية", + "unnamedRule": "قاعدة بلا عنوان", + "expandRule": "توسيع القاعدة", + "collapseRule": "طيّ القاعدة", + "ruleOn": "مفعّلة", + "ruleOff": "معطَّلة", + "ruleModified": "مُعدَّلة", + "ruleNew": "جديدة", + "defaultPreview": "تستخدم رسالة الحظر الافتراضية عند التطابق", + "protectionOn": "حظر الاستدعاءات مفعّل", + "protectionOff": "حظر الاستدعاءات معطَّل", + "ruleCount": "{{enabled}} من {{total}} قاعدة مفعّلة", + "closeEditor": "إغلاق المحرّر", + "testEntry": "اختبار كل القواعد", + "validateRule": "اختبار هذه القاعدة", + "testAll": "اختبار كل القواعد", + "testScopeAll": "كل القواعد", + "testScopeSingle": "قاعدة واحدة: {{name}}", + "testSingleHint": "يختبر هذه القاعدة فقط، متجاهلًا المفاتيح العامة ومفاتيح القواعد. لا يحفظ ولا يُنفّذ أدوات.", + "testAllHint": "يختبر كل القواعد بترتيبها وحالات تفعيلها الحالية. لا يحفظ ولا يُنفّذ أدوات.", + "singleMatched": "تطابقت هذه القاعدة", + "singleNotMatched": "لم تتطابق هذه القاعدة", + "singleResultHint": "هذا اختبار لقاعدة واحدة. يعتمد الحظر الفعلي أيضًا على المفتاح العام والقواعد المفعّلة وترتيب التطابق." + }, + "hitl": { + "pageTitle": "موافقات التدخّل البشري (HITL)", + "pageReviewerLabel": "المراجِع الحالي", + "auditEngineLabel": "محرّك الموافقة", + "auditEngineOpenAI": "بروتوكول OpenAI", + "auditEngineJev": "TypeSafe Jev", + "colAuditEngine": "محرّك الموافقة", + "pageReviewerHint": "ينطبق على المحادثة المحدَّدة. وفي حال عدم وجود محادثة، يُحفظ في config.yaml بوصفه القيمة الافتراضية العامة للمحادثات الجديدة. يسري مفعوله فورًا.", + "pageReviewerSaved": "تم حفظ المراجِع.", + "whitelistLabel": "قائمة الأدوات المسموح بها (دون موافقة)", + "whitelistHint": "واحدة في كل سطر أو مفصولة بفواصل. تُحفظ في قائمة config.yaml العامة المسموح بها ويسري مفعولها فورًا (مُزامَنة مع الشريط الجانبي للمحادثة).", + "whitelistSaved": "تم حفظ قائمة المسموح بها.", + "whitelistSaveFailed": "فشل حفظ قائمة المسموح بها", + "strategyLabel": "استراتيجية التدقيق (الموجّه)", + "strategyHint": "تتخطّى الأدوات المسموح بها الموافقة. أما الأدوات الأخرى فيحكم عليها النموذج باستخدام هذا الموجّه عند اختيار وكيل التدقيق.", + "strategyTabApproval": "وضع الموافقة", + "strategyTabReviewEdit": "وضع المراجعة والتعديل", + "strategyHintApproval": "تتخطّى الأدوات المسموح بها الموافقة. وفي وضع الموافقة يكتفي وكيل التدقيق بالموافقة أو الرفض.", + "strategyHintJev": "TypeSafe Jev مفعّل: يُقيَّم نص الاستراتيجية المخصّص أدناه بوصفه أسئلة مُهيكلة. وتظلّ القواعد المدمجة للعمليات التدميرية حدًّا أدنى صارمًا. ولا يمكن لـ Jev إعادة كتابة الوسائط.", + "strategyHintReviewEdit": "في وضع المراجعة والتعديل، يجوز لوكيل التدقيق تضييق المعاملات عبر editedArguments قبل الموافقة؛ ويرفض إذا تعذّر تعديل المعاملات بأمان.", + "strategyReset": "إعادة التعيين إلى الافتراضي", + "strategySaved": "تم حفظ استراتيجية التدقيق.", + "strategySaveFailed": "فشل حفظ استراتيجية التدقيق", + "tabPending": "قيد الانتظار", + "tabLogs": "سجلّات التدقيق", + "tabStrategy": "استراتيجية التدقيق", + "tabWhitelist": "قائمة الأدوات المسموح بها", + "pendingTitle": "الموافقات قيد الانتظار", + "auditReviewing": "المراجعة التلقائية قيد التنفيذ", + "auditReviewEditing": "جارٍ المراجعة والتصحيح تلقائيًا", + "auditReviewExplanation": "يراجع هذا الطلبَ وكيلُ مراجعة مُوجَّه بعناية. ولن يُنفَّذ إلّا بعد الموافقة.", + "auditApproved": "وافق وكيل التدقيق", + "auditEditedApproved": "عدّل وكيل التدقيق المعاملات ووافق", + "auditRejected": "رفض وكيل التدقيق", + "waitingHumanApproval": "في انتظار موافقة بشرية", + "waitingHumanReview": "في انتظار مراجعة بشرية", + "humanApprovalExplanation": "يحتاج استدعاء الأداة هذا إلى تأكيدك قبل أن يُنفَّذ.", + "humanReviewExplanation": "راجع المعاملات وعدّلها عند الرغبة قبل السماح بالتنفيذ.", + "humanApproved": "مسموح به مرّة واحدة", + "humanEditedApproved": "تم تعديل المعاملات والسماح به", + "humanRejected": "رُفضت الموافقة البشرية", + "viewEditedArgs": "عرض المعاملات المُعدَّلة", + "reviewArgs": "مراجعة المعاملات (JSON)", + "commentOptional": "تعليق (اختياري)", + "commentPlaceholder": "مثلًا: السماح بأمر للقراءة فقط", + "reject": "رفض", + "allowOnce": "السماح مرّة واحدة", + "saveEditedAndAllow": "حفظ التعديلات والسماح", + "waitingApprovalShort": "في انتظار الموافقة", + "waitingApprovalCount": "في انتظار الموافقة {{count}}", + "approvalUrgencyUnlimited": "لا حدّ زمني للموافقة", + "approvalUrgencyMoreThanThree": "تنتهي صلاحية أقرب موافقة بعد أكثر من 3 دقائق", + "approvalUrgencyMoreThanFive": "تنتهي صلاحية أقرب موافقة بعد أكثر من 5 دقائق", + "approvalUrgencyThreeToFive": "تنتهي صلاحية أقرب موافقة خلال 3–5 دقائق", + "approvalUrgencyOneToThree": "تنتهي صلاحية أقرب موافقة خلال 1–3 دقائق", + "approvalUrgencyWithinOne": "تنتهي صلاحية أقرب موافقة خلال دقيقة واحدة", + "requestGeneric": "هل تسمح لـ CyberStrikeAI باستدعاء {{tool}}؟", + "requestVisitUrl": "هل تسمح لـ CyberStrikeAI بزيارة {{url}}؟", + "requestVisitLongUrl": "هل تسمح لـ CyberStrikeAI بزيارة هذا العنوان؟", + "requestModifyLongPath": "هل تسمح لـ CyberStrikeAI بتعديل هذا الملف؟", + "requestBrowser": "هل تسمح لـ CyberStrikeAI باستخدام المتصفّح؟", + "requestCommand": "هل تسمح لـ CyberStrikeAI بتشغيل هذا الأمر؟", + "requestFile": "هل تسمح لـ CyberStrikeAI بتعديل {{path}}؟", + "requestFiles": "هل تسمح لـ CyberStrikeAI بتعديل الملفات؟", + "toolTerminal": "الطرفية", + "toolFiles": "الملفات", + "viewRequestDetails": "عرض تفاصيل الطلب", + "editRequestDetails": "عرض معاملات الطلب أو تعديلها", + "addApprovalComment": "إضافة تعليق موافقة (اختياري)", + "timeoutAutoReject": "يرفض تلقائيًا عند انتهاء الصلاحية", + "timeoutUnlimited": "لا حدّ زمني", + "expiredAutoRejected": "انتهت مهلة الموافقة؛ جارٍ الرفض تلقائيًا…", + "taskClosedApprovalUnavailable": "انتهت المهمّة؛ الموافقة غير متاحة", + "taskInterrupted": "تمت مقاطعة المهمّة", + "interruptedApprovalCancelled": "تمت مقاطعة المهمّة؛ أُلغيت الموافقة", + "expiredRejected": "انتهت مهلة الموافقة ورُفضت", + "searchLabel": "بحث", + "searchPlaceholder": "الأداة، المحادثة، الحمولة، التعليق…", + "searchApply": "بحث", + "filterDecision": "القرار", + "filterDecidedBy": "المراجِع", + "filterAll": "الكل", + "decisionApprove": "موافقة", + "decisionReject": "رفض", + "reviewerHuman": "بشري", + "reviewerAgent": "وكيل التدقيق", + "reviewerSystem": "النظام", + "reviewerManual": "إدخال يدوي", + "logCreate": "سجلّ جديد", + "logModalTitle": "سجلّ التدقيق", + "logModalEdit": "تعديل سجلّ التدقيق", + "fieldConversation": "مُعرّف المحادثة", + "fieldTool": "اسم الأداة", + "fieldComment": "تعليق", + "fieldPayload": "الحمولة (JSON)", + "fieldUserMessage": "رسالة المستخدم", + "fieldThinking": "التفكير", + "fieldReasoning": "سلسلة الاستدلال", + "fieldPlanning": "التخطيط", + "colId": "المُعرّف", + "colTool": "الأداة", + "colConversation": "المحادثة", + "colDecision": "القرار", + "colDecidedBy": "المراجِع", + "colContext": "السياق", + "colTime": "الوقت", + "colActions": "الإجراءات", + "viewDetail": "التفاصيل", + "logModalView": "تفاصيل سجلّ التدقيق", + "fieldExecutionResult": "نتيجة التنفيذ", + "executionSuccess": "نجاح", + "executionFailed": "فشل", + "edit": "تعديل", + "delete": "حذف", + "logsEmpty": "لا توجد سجلّات تدقيق", + "logsEmptyHint": "تُنشأ السجلّات تلقائيًا عند الموافقة على موافقات التدخّل البشري أو رفضها.", + "pageInfo": "{{total}} إجمالًا", + "prevPage": "السابق", + "nextPage": "التالي", + "conversationRequired": "مُعرّف المحادثة مطلوب", + "toolRequired": "اسم الأداة مطلوب", + "saveFailed": "فشل الحفظ", + "deleteConfirm": "هل تريد حذف سجلّ التدقيق هذا؟", + "deleteFailed": "فشل الحذف", + "retentionHint": "تُحفظ سجلّات التدقيق لمدّة {{days}} يومًا، ثم تُحذف تلقائيًا.", + "selectedCount": "{{count}} محدَّد", + "selectAll": "تحديد الكل", + "deselectAll": "إلغاء تحديد الكل", + "batchDelete": "حذف جماعي", + "batchDeleteConfirm": "هل تريد حذف سجلّات التدقيق الـ {{count}} المحدَّدة؟ لا يمكن التراجع عن ذلك.", + "batchDeleteSuccess": "تم حذف {{count}} سجلّ تدقيق بنجاح", + "batchDeleteFailed": "فشل الحذف الجماعي", + "clearAll": "مسح الكل", + "clearAllConfirm": "هل تريد مسح كل سجلّات التدقيق الـ {{count}} المطابِقة لعوامل التصفية الحالية؟ لا يمكن التراجع عن ذلك.", + "clearAllConfirmNoFilter": "لم تُضبط أيّ عوامل تصفية. سيؤدّي هذا إلى مسح كل سجلّات التدقيق الـ {{count}}. لا يمكن التراجع عن ذلك. هل تريد المتابعة؟", + "clearAllSuccess": "تم مسح {{count}} سجلّ تدقيق", + "clearAllFailed": "فشل المسح", + "selectLogsFirst": "حدّد سجلّات التدقيق المراد حذفها أولًا", + "loading": "جارٍ التحميل...", + "emptyState": "لا توجد موافقات قيد الانتظار", + "dismiss": "تجاهل", + "conversationLabel": "المحادثة:", + "reviewEditHelp": "وضع المراجعة والتعديل: قدّم كائن JSON لتجاوز وسائط الأداة. مثال: {\"command\":\"ls -la\"}", + "approvalHelp": "وضع الموافقة: موافقة/رفض فقط، وتعديل الوسائط معطَّل.", + "commentHelp": "تعليق (اختياري): دوّن سبب الموافقة بإيجاز.", + "approve": "موافقة", + "loadFailed": "فشل التحميل", + "invalidJson": "وسائط JSON غير صالحة", + "submitFailedPrefix": "فشل الإرسال:" + }, + "progress": { + "callingAI": "جارٍ استدعاء نموذج الذكاء الاصطناعي...", + "callingTool": "جارٍ استدعاء الأداة: {{name}}", + "lastIterSummary": "التكرار الأخير: جارٍ إنشاء الملخّص والخطوات التالية...", + "summaryDone": "اكتمل الملخّص", + "generatingFinalReply": "جارٍ إنشاء الردّ النهائي...", + "maxIterSummary": "بلغ الحدّ الأقصى للتكرارات، جارٍ إنشاء الملخّص...", + "analyzingRequestShort": "جارٍ تحليل طلبك...", + "analyzingRequestPlanning": "جارٍ تحليل طلبك والتخطيط لاستراتيجية الاختبار...", + "startingEinoDeepAgent": "جارٍ بدء Eino DeepAgent...", + "startingEinoMultiAgent": "جارٍ بدء تعدّد الوكلاء في Eino...", + "einoAgent": "وكيل Eino: {{name}}", + "peAgentPlanner": "المخطّط", + "peAgentExecutor": "المنفّذ", + "peAgentReplanning": "معيد التخطيط" + }, + "timeline": { + "blocked": "محظور", + "params": "المعاملات:", + "executionResult": "نتيجة التنفيذ:", + "executionId": "معرّف التنفيذ:", + "noResult": "لا توجد نتيجة", + "running": "قيد التشغيل...", + "backgroundRunning": "قيد التشغيل في الخلفية", + "completed": "مكتمل", + "execFailed": "فشل التنفيذ", + "resultMissing": "سجلّ النتيجة مفقود", + "toolSucceeded": "نجح", + "toolFailed": "فشل" + }, + "tasks": { + "statusEndedWithFailures": "انتهت ({{count}} فشلت)", + "finishedProgressHint": "يحتسب التقدّم المهامّ المنتهية، بما في ذلك حالات الفشل والإلغاء؛ وهو ليس معدّل النجاح.", + "title": "إدارة المهامّ", + "stopTask": "إيقاف المهمّة", + "interruptModalTitle": "مقاطعة الخطوة الحالية", + "interruptReasonLabel": "ملاحظة المقاطعة", + "interruptModalHint": "عند تشغيل أداة: مثل \"إيقاف الأداة\" في مراقب MCP — يُوقَف ذلك الاستدعاء فقط ويستمرّ التشغيل؛ ويمكن دمج ملاحظتك في نتيجة الأداة (USER INTERRUPT NOTE). عند عدم تشغيل أيّ أداة (تفكير/بثّ النموذج فقط): يظلّ \"المقاطعة والمتابعة\" يعمل — تتوقّف المخرجات الحالية مؤقّتًا، وتُدمج ملاحظتك في السياق ويُستأنف التشغيل تلقائيًّا؛ ويُظهر مخطّط التقدّم الزمني إدخال \"مقاطعة ومتابعة من المستخدم\". استخدم هذا بدلاً من الإيقاف الكامل عندما تريد التوجيه فقط؛ واستخدم \"الإيقاف التامّ\" لإنهاء المهمّة بأكملها.", + "interruptReasonPlaceholder": "مثال: الأداة بطيئة جدًّا — تخطَّها ولخّص…", + "interruptReasonRequired": "يُرجى إدخال ملاحظة قصيرة ليتمكّن النموذج من المتابعة وفقًا لها.", + "interruptSubmitting": "جارٍ الإرسال...", + "interruptConfirmContinue": "المقاطعة والمتابعة", + "interruptHardStop": "الإيقاف التامّ", + "interruptModalClose": "إغلاق", + "userInterruptTimelineTitle": "ملاحظة مقاطعة المستخدم (مستمرّة)", + "collapseDetail": "طيّ التفاصيل", + "newTask": "مهمّة جديدة", + "autoRefresh": "تحديث تلقائي", + "historyHint": "تلميح: يتوفّر سجلّ المهامّ المكتملة. حدّد \"إظهار السجلّ\" لعرضه.", + "statusRunning": "قيد التشغيل", + "statusCleanupUnconfirmed": "الإلغاء عن بُعد غير مؤكَّد", + "statusCleaning": "جارٍ التنظيف", + "statusCleanupFailed": "فشل التنظيف (إعادة المحاولة)", + "statusCancelling": "جارٍ الإلغاء", + "statusFailed": "فشلت", + "statusTimeout": "انتهت المهلة", + "statusCancelled": "أُلغيت", + "statusCompleted": "اكتملت", + "historyBadge": "السجلّ", + "duration": "المدّة", + "completedAt": "اكتملت في", + "startedAt": "بدأت في", + "clickToCopy": "انقر للنسخ", + "unnamedTask": "مهمّة بلا اسم", + "unknown": "غير معروف", + "unknownTime": "وقت غير معروف", + "clearHistoryConfirm": "هل تريد مسح كلّ سجلّ المهامّ؟", + "cancelTaskFailed": "فشل إلغاء المهمّة", + "cancelFailed": "فشل الإلغاء", + "taskInfoNotSynced": "لم تتمّ مزامنة معلومات المهمّة بعد، يُرجى المحاولة لاحقًا.", + "loadActiveTasksFailed": "فشل تحميل المهامّ النشطة", + "cannotGetTaskStatus": "تعذّر الحصول على حالة المهمّة", + "copiedToast": "تم النسخ!", + "cancelling": "جارٍ الإلغاء...", + "enterTaskPrompt": "أدخل مهمّة واحدة على الأقلّ", + "noValidTask": "لا توجد مهامّ صالحة", + "createBatchQueueFailed": "فشل إنشاء قائمة انتظار المهامّ المجمّعة", + "noBatchQueues": "لا توجد حاليًّا قوائم انتظار للمهامّ المجمّعة", + "recentCompletedTasks": "المهامّ المكتملة مؤخّرًا (آخر 24 ساعة)", + "clearHistory": "مسح السجلّ", + "cancelTask": "إلغاء المهمّة", + "viewConversation": "عرض المحادثة", + "viewVulnerabilities": "عرض الثغرات", + "viewVulnerabilitiesQueueTitle": "عرض الثغرات: فتح الإدارة مصفّاةً على قائمة الانتظار هذه", + "runSingleTask": "تشغيل المهمّة", + "confirmRunSingleTask": "هل تريد تشغيل هذه المهمّة فقط؟ ستتوقّف قائمة الانتظار مؤقّتًا عند انتهائها ولن تتابع العناصر المعلّقة الأخرى.", + "runSingleTaskFailed": "فشل تشغيل المهمّة", + "runSingleTaskUnavailable": "غير متاح أثناء تشغيل قائمة الانتظار أو إحدى المهامّ", + "runSingleTaskUnavailableSelf": "هذه المهمّة قيد التشغيل", + "runSingleTaskUnavailableQueue": "قائمة الانتظار قيد التشغيل؛ أوقفها مؤقّتًا قبل تشغيل مهمّة أخرى على حدة", + "conversationIdLabel": "معرّف المحادثة", + "statusPending": "معلّقة", + "statusPaused": "موقوفة مؤقّتًا", + "statusCronCycleIdle": "اكتملت الدورة · حلقة مجدوَلة", + "statusCronRunning": "قيد التشغيل · قائمة انتظار cron", + "cronNextRunLine": "التشغيل التالي: {{time}}", + "cronRoundDoneProgressHint": "قائمة انتظار cron: انتهت المهامّ الفرعية؛ وتبدأ الدورة التالية حسب الجدول", + "cronRunningProgressHint": "هذه الدورة قيد التشغيل؛ وتتبع الدورة الكاملة التالية توقيت Cron / وقت التشغيل التالي", + "cronPendingScheduled": "مجدوَلة بـ cron · التالية {{time}}", + "cronPendingProgressNote": "ستبدأ حسب الجدول، أو انقر على \"بدء\" لتشغيل دورة الآن", + "cronRecurringCallout": "تبدأ قوائم انتظار cron دورةً جديدة في كلّ وقت مجدوَل. أوقف \"السماح بالتشغيل التلقائي عبر Cron\" لإيقاف التكرار.", + "confirmCancelTasks": "هل تريد إلغاء {{n}} مهمّة محدَّدة؟", + "batchCancelResultPartial": "الإلغاء المجمّع: نجح {{success}}، وفشل {{fail}}", + "batchCancelResultSuccess": "تم إلغاء {{n}} مهمّة بنجاح", + "taskCount": "{{count}} مهمّة", + "queueIdLabel": "معرّف قائمة الانتظار", + "createdTimeLabel": "أُنشئت في", + "totalLabel": "الإجمالي", + "pendingLabel": "معلّقة", + "runningLabel": "قيد التشغيل", + "completedLabel": "مكتملة", + "failedLabel": "فشلت", + "cancelledLabel": "أُلغيت", + "loadingTasks": "جارٍ التحميل...", + "loadFailedRetry": "فشل التحميل", + "loadTaskListFailed": "فشل تحميل قائمة المهامّ", + "getQueueDetailFailed": "فشل تحميل تفاصيل قائمة الانتظار", + "startBatchQueueFailed": "فشل بدء قائمة الانتظار المجمّعة", + "pauseQueueFailed": "فشل إيقاف قائمة الانتظار مؤقّتًا", + "pauseQueueConfirm": "هل تريد إيقاف قائمة الانتظار المجمّعة هذه مؤقّتًا؟ ستُوقَف المهمّة الحالية؛ وستبقى المهامّ المتبقّية معلّقة.", + "deleteQueueConfirm": "هل تريد حذف قائمة الانتظار المجمّعة هذه؟ لا يمكن التراجع عن ذلك.", + "deleteQueueFailed": "فشل حذف قائمة الانتظار المجمّعة", + "rerunQueueConfirm": "هل تريد إعادة تشغيل قائمة الانتظار المجمّعة هذه؟ ستُعاد جميع المهامّ إلى حالة \"معلّقة\" وتُنفَّذ من جديد.", + "rerunQueueFailed": "فشل إعادة تشغيل قائمة الانتظار المجمّعة", + "batchQueueTitle": "قائمة انتظار المهامّ المجمّعة", + "batchQueueUntitled": "قائمة انتظار بلا عنوان", + "resumeExecute": "استئناف", + "taskIncomplete": "معلومات المهمّة غير مكتملة", + "cannotGetTaskMessageInput": "تعذّر الحصول على إدخال رسالة المهمّة", + "taskMessageRequired": "رسالة المهمّة مطلوبة", + "saveTaskFailed": "فشل حفظ المهمّة", + "queueInfoMissing": "لم يتمّ العثور على معلومات قائمة الانتظار", + "addTaskFailed": "فشل إضافة المهمّة", + "confirmDeleteTask": "هل تريد حذف هذه المهمّة؟\n\nالمهمّة: {{message}}\n\nلا يمكن التراجع عن ذلك.", + "deleteTaskFailed": "فشل حذف المهمّة", + "paginationShow": "عرض {{start}}-{{end}} من {{total}} سجلّ", + "paginationPerPage": "لكلّ صفحة", + "paginationFirst": "الأولى", + "paginationPrev": "السابقة", + "paginationNext": "التالية", + "paginationLast": "الأخيرة", + "paginationPage": "صفحة {{current}} / {{total}}", + "deleteQueue": "حذف قائمة الانتظار", + "retry": "إعادة المحاولة", + "noMatchingTasks": "لا توجد مهامّ مطابِقة", + "updateTaskFailed": "فشل تحديث المهمّة", + "durationSeconds": "ث", + "durationMinutes": "د", + "durationHours": "س" + }, + "infoCollect": { + "enterFofaQuery": "أدخل صيغة استعلام FOFA", + "querying": "جارٍ الاستعلام...", + "queryFailed": "فشل الاستعلام", + "enterNaturalLanguage": "أدخل وصفًا بلغة طبيعية", + "cancelParse": "إلغاء التحليل", + "clickToCancelParse": "انقر لإلغاء تحليل الذكاء الاصطناعي", + "parseToFofa": "تحليل اللغة الطبيعية إلى استعلام FOFA", + "parseResultEmpty": "نتيجة التحليل فارغة: يُرجى إضافة/تعديل استعلام FOFA في النافذة المنبثقة", + "queryPlaceholder": "مثلًا: app=\"Apache\" && country=\"CN\"", + "selectAll": "تحديد الكل/لا شيء", + "selectRow": "تحديد الصفّ", + "copyTarget": "نسخ الهدف", + "sendToChat": "إرسال إلى المحادثة (قابل للتعديل؛ Ctrl/Cmd+نقر للإرسال مباشرةً)", + "noTargetToCopy": "لا يوجد هدف لنسخه", + "targetCopied": "تم نسخ الهدف", + "manualCopyHint": "فشل النسخ، يُرجى النسخ يدويًا: ", + "cannotInferTarget": "تعذّر استنتاج هدف الفحص من الصفّ (ضمّن host/ip/port/domain في الحقول)", + "noSendMessage": "sendMessage() غير موجودة، يُرجى تحديث الصفحة وإعادة المحاولة", + "filledToInput": "تمت التعبئة في حقل إدخال المحادثة، عدّل وأرسِل", + "noExportResult": "لا توجد نتائج للتصدير", + "xlsxNotLoaded": "لم تُحمَّل مكتبة XLSX، يُرجى تحديث الصفحة وإعادة المحاولة", + "noResults": "لا توجد نتائج", + "selectRowsFirst": "حدّد الصفوف المراد فحصها أولًا", + "noScanTarget": "لم يُستنتج أيّ هدف فحص من التحديد (ضمّن host/ip/port/domain في الحقول)", + "batchScanFailed": "فشل الفحص الجماعي", + "batchQueueCreated": "تم إنشاء قائمة انتظار الفحص الجماعي", + "field": "الحقل", + "cellValueLength": "{{count}} حرفًا", + "parsePending": "جارٍ تحليل الذكاء الاصطناعي...", + "parsePendingClickCancel": "جارٍ تحليل الذكاء الاصطناعي... (انقر الزرّ للإلغاء)", + "parseSlow": "يستغرق تحليل الذكاء الاصطناعي بعض الوقت، ما زالت المعالجة جارية…", + "parseDone": "اكتمل تحليل الذكاء الاصطناعي", + "parseCancelled": "أُلغي تحليل الذكاء الاصطناعي", + "parseFailed": "فشل تحليل الذكاء الاصطناعي: ", + "parseResultTitle": "نتيجة تحليل الذكاء الاصطناعي", + "naturalLanguageLabel": "اللغة الطبيعية", + "fofaQueryEditable": "استعلام FOFA (قابل للتعديل)", + "confirmBeforeQuery": "تأكّد من الصيغة والنطاق قبل تشغيل الاستعلام.", + "reminder": "تذكير", + "explanation": "الشرح", + "actions": "الإجراءات", + "batchScanTitle": "فحص FOFA الجماعي", + "queueCreatedSkipped": "تم إنشاء قائمة الانتظار (تم تخطّي {{n}} صفّ، بلا هدف)", + "createQueueFailed": "فشل إنشاء قائمة الانتظار الجماعية", + "loading": "جارٍ التحميل...", + "none": "لا شيء", + "truncated": "مقتطَع", + "resultsMeta": "الإجمالي {{total}} · هذه الصفحة {{count}} · page={{page}} · size={{size}}", + "providerReturnedFewer": "أعاد المزوّد {{count}}/{{expected}} صفّ", + "parseModalCancel": "إلغاء", + "parseModalApply": "تعبئة في الاستعلام", + "parseModalApplyRun": "تعبئة واستعلام" + }, + "assets": { + "countryExample": "الصين", + "provinceExample": "بكين", + "cityExample": "بكين", + "exportIP": "عنوان IP", + "exportVulnerabilityCount": "عدد الثغرات", + "overviewTitle": "نظرة عامة على الأصول", + "libraryTitle": "مكتبة الأصول", + "viewLibrary": "عرض مكتبة الأصول", + "totalAssets": "إجمالي الأصول", + "totalAssetsHint": "الأصول الخاضعة حاليًا للمراقبة المستمرة", + "ipCount": "عناوين IP", + "domainCount": "النطاقات", + "portCount": "المنافذ", + "recentCount": "المكتشفة خلال 7 أيام", + "recentWindow": "7 أيام", + "recentShare": "{{percent}}% من إجمالي الأصول الحالية", + "protocolDistribution": "توزيع البروتوكولات", + "protocolDistributionHint": "تركيبة التعرّض بحسب الخدمة المُحدَّدة", + "protocolKinds": "{{count}} بروتوكولات", + "topProtocol": "البروتوكول الأكثر شيوعًا", + "topProtocolShare": "حصّة البروتوكول الأكثر شيوعًا", + "protocolComposition": "تركيبة البروتوكولات", + "protocolRank": "الترتيب", + "protocolName": "البروتوكول", + "assetRatio": "حصّة الأصول", + "assetAmount": "العدد", + "ratio": "الحصّة", + "assetCountUnit": "{{count}} أصل", + "postureChanges": "تغيّرات الوضع الأمني", + "postureChangesHint": "تتبّع تغيّرات الأصول والمخاطر خلال الفترة المحدَّدة", + "periodSelection": "تحديد الفترة", + "days7": "7 أيام", + "days30": "30 يومًا", + "days90": "90 يومًا", + "assetTrend": "اتجاه تغيّر الأصول", + "assetTrendHint": "الاكتشافات الجديدة والأصول غير النشطة حاليًا", + "addedAssets": "الأصول المُضافة", + "inactiveAssets": "الأصول غير النشطة", + "riskTrend": "اتجاه اكتشاف المخاطر", + "riskTrendHint": "الثغرات الجديدة والنتائج الحرجة أو عالية الخطورة", + "discoveredRisks": "الثغرات الجديدة", + "highRisks": "حرجة وعالية", + "scanCoverage": "تغطية الفحص", + "scanCoverageHint": "تحديد الأصول غير المفحوصة أو المتأخّرة عن المراجعة", + "overallCoverage": "التغطية الإجمالية", + "scannedAssets": "الأصول المفحوصة", + "scannedAssetsHint": "تم فحصها مرّة واحدة على الأقل", + "recentlyScanned": "المُغطّاة خلال 30 يومًا", + "neverScanned": "لم تُفحص قطّ", + "neverScannedHint": "إعطاء الأولوية للفحص", + "staleScans": "لم تُفحص منذ أكثر من 30 يومًا", + "staleScansHint": "قد تكون نتائج الفحص قديمة", + "coverageOfTotal": "{{percent}}% من إجمالي الأصول", + "coverageMeta": "{{scanned}} / {{total}} مُغطّاة", + "addAsset": "+ إضافة أصل", + "bulkImport": "استيراد جماعي", + "bulkImportTitle": "استيراد جماعي للأصول", + "bulkImportSubtitle": "عبّئ القالب وارفعه، ثم راجع نتائج التحقّق قبل الاستيراد", + "downloadTemplate": "تنزيل القالب", + "downloadTemplateHint": "يُنصح باستخدام XLSX، أما CSV فمناسب للتصدير من أنظمة أخرى", + "downloadXlsx": "تنزيل قالب XLSX", + "downloadCsv": "تنزيل قالب CSV", + "uploadFile": "رفع ملف", + "uploadFileHint": "يدعم .xlsx و.csv بحدّ أقصى 100,000 صفّ و100 ميغابايت", + "chooseOrDropFile": "اختر ملفًا أو أسقطه هنا", + "fileNotSelected": "لم يتم اختيار ملف", + "dataPreview": "معاينة البيانات", + "rowNumber": "الصفّ", + "validationResult": "التحقّق", + "validationPassed": "صالح", + "importValidRows": "استيراد الصفوف الصالحة", + "importValidRowsCount": "استيراد {{count}} صفًّا صالحًا", + "importPreviewSummary": "{{total}} صفًّا: {{valid}} صالح، {{invalid}} بحاجة إلى مراجعة", + "importBackendRowError": "الصفّ {{row}} في Excel (الأصل الصالح المُرسَل رقم {{index}}): {{message}}", + "previewLimited": "عرض أوّل 100 صفّ؛ ستتم معالجة الصفوف الـ {{count}} جميعها", + "fileTypeInvalid": "يُدعم فقط ملفّا .xlsx و.csv", + "fileTooLarge": "يجب ألّا يتجاوز حجم الملف 100 ميغابايت", + "spreadsheetUnavailable": "تعذّر تحميل مكوّن الجدول؛ حدّث الصفحة وأعد المحاولة", + "fileParseFailed": "تعذّر تحليل الملف؛ تأكّد من أنه صالح وغير تالف", + "fileHasNoData": "لا يحتوي الملف على صفوف للاستيراد", + "headerInvalid": "لم يتم التعرّف على أي من أعمدة القالب؛ استخدم القالب الذي تم تنزيله", + "tooManyRows": "يجب ألّا تتجاوز البيانات {{count}} صفّ", + "importTargetRequired": "العنوان الهدف مفقود", + "importStatusInvalid": "يجب أن تكون الحالة active أو inactive", + "importProjectNotFound": "المشروع غير موجود أو يتعذّر الوصول إليه: {{project}}", + "importTagsInvalid": "استخدم 30 وسمًا كحدّ أقصى، بحدّ أقصى 64 حرفًا لكل وسم", + "importFieldTooLong": "الحقل {{field}} طويل جدًّا", + "duplicateFileRow": "مكرَّر مع الصفّ {{row}}", + "bulkImportDone": "اكتمل الاستيراد: {{created}} مُنشأ، {{updated}} مُحدَّث، {{skipped}} متخطّى", + "templateGuideTitle": "تعليمات الاستيراد الجماعي للأصول", + "templateGuideRequired": "مطلوب: target، أو واحد على الأقل من host / ip / domain", + "templateGuideTarget": "أمثلة target: https://example.com:443، example.com، 1.1.1.1:22", + "templateGuideProject": "يقبل project اسم مشروع قائم أو مُعرّفه؛ اتركه فارغًا لعدم ربطه بمشروع", + "templateGuideTags": "افصل الوسوم بفواصل؛ 30 وسمًا كحدّ أقصى", + "templateGuideStatus": "يقبل status القيمتين active / inactive والقيمة الافتراضية active", + "templateGuideLimit": "أدخل البيانات في ورقة Assets؛ حتى 100,000 صفّ", + "instructionsSheet": "التعليمات", + "addAssetTitle": "إضافة أصل", + "editAssetTitle": "تعديل أصل", + "editorSubtitle": "أدخل هدفًا واحدًا؛ تُحلَّل التفاصيل الأخرى تلقائيًا أو يمكن إضافتها لاحقًا", + "assetAddress": "عنوان الأصل", + "assetAddressHint": "يُقبل أي هدف غير فارغ؛ وتُحلَّل تلقائيًا عناوين URL والنطاقات وعناوين IP والمنافذ المُتعرَّف عليها", + "targetWhitespace": "لا يمكن أن يحتوي عنوان الأصل على مسافات", + "targetInvalid": "أدخل عنوان URL أو نطاقًا أو عنوان IP صالحًا", + "hostInvalid": "عنوان URL الكامل غير صالح أو يحتوي على بيانات اعتماد", + "ipInvalid": "عنوان IP غير صالح", + "domainInvalid": "نطاق غير صالح", + "protocolInvalid": "بروتوكول غير صالح", + "portInvalid": "يجب أن يكون المنفذ بين 1 و65535", + "connectionInfo": "الاتصال", + "connectionInfoHint": "مُحلَّل تلقائيًا من عنوان الأصل؛ عدّله حسب الحاجة", + "fingerprintInfo": "التعريف", + "fingerprintInfoHint": "أضف بصمات الصفحة والخدمة لتسهيل الاكتشاف", + "locationAndStatus": "الموقع والحالة", + "locationAndStatusHint": "سجّل موقع الأصل ومدى توفّره الحالي", + "hostUrl": "عنوان URL الكامل / المضيف", + "pageTitle": "عنوان الصفحة", + "serverProduct": "الخدمة / المنتج", + "tagsPlaceholder": "اضغط Enter أو الفاصلة للإضافة", + "tagsHint": "صنّف حسب البيئة أو العمل أو الملكية", + "removeTag": "إزالة الوسم {{tag}}", + "addAssetAction": "إضافة أصل", + "discardChanges": "هل تريد تجاهل التغييرات غير المحفوظة؟", + "createdSuccessfully": "تمت إضافة الأصل", + "updatedSuccessfully": "تم تحديث الأصل", + "duplicateMerged": "الأصل موجود مسبقًا؛ وتم دمج التفاصيل بأمان", + "assetSkipped": "لم يُحفظ الأصل؛ قد يكون موجودًا دون صلاحية التحديث", + "searchPlaceholder": "ابحث في المضيف أو IP أو النطاق أو العنوان أو الخدمة أو الوسم", + "allStatuses": "كل الحالات", + "allProjects": "كل المشاريع", + "unboundProject": "غير مرتبط", + "project": "المشروع", + "archived": "مؤرشَف", + "statusActive": "نشط", + "statusInactive": "غير نشط", + "target": "الهدف", + "service": "الخدمة", + "title": "العنوان / البصمة", + "location": "الموقع", + "country": "الدولة / المنطقة", + "province": "المحافظة / الولاية", + "city": "المدينة", + "source": "المصدر", + "lastSeen": "آخر ظهور", + "status": "الحالة", + "domain": "النطاق", + "port": "المنفذ", + "protocol": "البروتوكول", + "server": "بصمة الخادم", + "tags": "الوسوم (مفصولة بفواصل)", + "tagsLabel": "الوسوم", + "detailTitle": "تفاصيل الأصل", + "sourceQuery": "استعلام المصدر", + "firstSeen": "أول ظهور", + "totalMeta": "{{count}} أصل", + "targetRequired": "أدخل عنوان أصل", + "loadFailed": "فشل تحميل الأصول", + "saveFailed": "فشل حفظ الأصل", + "saved": "تم حفظ الأصل", + "deleteConfirm": "هل تريد حذف هذا الأصل؟", + "deleteFailed": "فشل حذف الأصل", + "importSelected": "استيراد المُحدَّد", + "importOne": "إضافة إلى مكتبة الأصول", + "selectFirst": "حدّد النتائج المراد استيرادها أولًا", + "noValidImportTarget": "لا تحتوي أيّ من النتائج المحدَّدة على هدف أصل صالح", + "importFailed": "فشل استيراد الأصول", + "importDone": "تم إنشاء {{created}}، وتحديث {{updated}}", + "importDoneWithInvalid": "تم إنشاء {{created}}، وتحديث {{updated}}، وتخطّي {{invalid}} نتيجة بلا أهداف صالحة", + "selectPage": "تحديد هذه الصفحة", + "selectAsset": "تحديد الأصل", + "selectedCount": "{{count}} محدَّد", + "clearSelection": "مسح التحديد", + "selectAllResults": "تحديد الكل ({{count}})", + "allResultsSelected": "تم تحديد كل الأصول المطابِقة ({{count}})", + "bindProject": "ربط مشروع", + "bindProjectTitle": "ربط مشروع", + "chooseProject": "اختر مشروعًا", + "selectProjectRequired": "اختر مشروعًا للربط", + "bindProjectCount": "تحديث {{count}} أصل", + "bindProjectHint": "سيتم ربط جميع الأصول المحدَّدة بهذا المشروع.", + "confirmBinding": "تأكيد", + "bindProjectDone": "تم ربط {{count}} أصل", + "bindProjectFailed": "فشل ربط المشروع", + "sendToChat": "إرسال إلى المحادثة", + "sendToChatShort": "فحص", + "createScanTask": "إنشاء مهامّ فحص", + "scanTitle": "فحص الأصول", + "scanAssetCount": "{{count}} أصل", + "userPrompt": "موجّه المستخدم", + "promptHint": "استخدم العناصر النائبة {{asset_id}} أو {{target}} أو {{host}} أو {{ip}} أو {{domain}} أو {{port}}. ينشئ وضع المهامّ مهمّة واحدة لكل أصل.", + "executeNow": "التشغيل فور الإنشاء", + "confirmSend": "إرسال", + "confirmCreate": "إنشاء المهامّ", + "scanCompletionInstruction": "توقّف عن الاستكشاف بمجرّد استيفاء المهمّة أعلاه، وأبلِغ بالأدلّة المتاحة. استدعِ complete_asset_scan(id={{asset_id}}) لتسجيل وقت فحص هذا الأصل والنتائج ذات الصلة. لا يوسّع تحديث هذا السجلّ نطاق الاستكشاف المطلوب.", + "defaultScanPrompt": "نفّذ فحصًا أمنيًا مُصرَّحًا به للهدف {{target}} (مُعرّف الأصل: {{asset_id}}). احفظ النتائج المؤكَّدة باستخدام record_vulnerability، ثم استدعِ complete_asset_scan(id={{asset_id}}) لتحديث وقت آخر فحص وعدد الثغرات ذات الصلة.", + "selectAssetsFirst": "حدّد الأصول أولًا", + "promptRequired": "أدخل موجّه مستخدم", + "scanSubmitFailed": "فشل إرسال الفحص", + "scanTaskLinkFailed": "تم إنشاء المهامّ، لكن فشل ربط الأصول", + "scanConversationTitle": "فحص الأصول: {{targets}}", + "scanQueueTitle": "فحص جماعي للأصول", + "lastScan": "آخر فحص", + "relatedVulnerabilities": "النتائج ذات الصلة", + "riskLevel": "المخاطر", + "riskCritical": "حرجة", + "riskHigh": "عالية", + "riskMedium": "متوسطة", + "riskLow": "منخفضة", + "riskInfo": "معلوماتية", + "riskNormal": "سليم", + "riskUnassessed": "غير مُقيَّمة", + "advancedFilters": "عوامل تصفية متقدّمة", + "resetFilters": "إعادة تعيين", + "allRisks": "كل مستويات المخاطر", + "minVulnerabilityCount": "الحدّ الأدنى للنتائج", + "scanState": "حالة الفحص", + "allScanStates": "الكل", + "neverScannedFilter": "لم تُفحص قطّ", + "overdue30": "لم تُفحص منذ 30 يومًا", + "overdue60": "لم تُفحص منذ 60 يومًا", + "overdue90": "لم تُفحص منذ 90 يومًا", + "scannedFilter": "مفحوصة", + "responsiblePerson": "المالك", + "department": "القسم", + "ownership": "الملكية", + "unassigned": "غير مُسنَد", + "businessSystem": "نظام العمل", + "environment": "البيئة", + "criticality": "الأهمية", + "allEnvironments": "كل البيئات", + "environmentProduction": "الإنتاج", + "environmentStaging": "التجهيز", + "environmentTesting": "الاختبار", + "environmentDevelopment": "التطوير", + "environmentOther": "أخرى", + "allCriticalities": "كل مستويات الأهمية", + "criticalityCritical": "حرجة للمهمة", + "criticalityHigh": "عالية", + "criticalityMedium": "متوسطة", + "criticalityLow": "منخفضة", + "firstSeenStart": "أول ظهور من", + "firstSeenEnd": "أول ظهور إلى", + "lastSeenStart": "آخر ظهور من", + "lastSeenEnd": "آخر ظهور إلى", + "sort": "فرز", + "sortLastSeenDesc": "آخر ظهور (الأحدث)", + "sortLastScanAsc": "الأكثر تأخّرًا في الفحص", + "sortLastScanDesc": "الأحدث فحصًا", + "sortRiskDesc": "المخاطر (من الأعلى إلى الأدنى)", + "sortVulnerabilityDesc": "النتائج (الأكثر أولًا)", + "sortFirstSeenDesc": "أول ظهور (الأحدث)", + "sortHostAsc": "اسم الهدف", + "sortPortAsc": "المنفذ", + "savedViews": "عروض التصفية المحفوظة", + "saveCurrentView": "حفظ العرض الحالي", + "deleteView": "حذف العرض", + "bulkEdit": "تعديل جماعي", + "moreActions": "المزيد", + "export": "تصدير", + "exportCsv": "تصدير CSV", + "exportXlsx": "تصدير XLSX", + "mergeDuplicates": "دمج المكرَّرات", + "mergeRequiresMultiple": "حدّد أصلين مكرَّرين على الأقل", + "batchDelete": "حذف جماعي…", + "responsibilityBusiness": "الملكية وسياق العمل", + "responsibilityBusinessHint": "استخدم الملكية وأولوية العمل لتوجيه العمليات وترتيب المخاطر" + }, + "vulnerability": { + "title": "إدارة الثغرات", + "addVuln": "إضافة ثغرة", + "editVuln": "تعديل ثغرة", + "loadFailed": "فشل تحميل الثغرات", + "deleteConfirm": "هل تريد حذف هذه الثغرة؟" + }, + "mcp": { + "monitorTitle": "مراقب حالة MCP", + "execStats": "إحصاءات التنفيذ", + "latestExecutions": "أحدث عمليات التنفيذ", + "toolSearch": "بحث الأدوات", + "toolSearchPlaceholder": "أدخِل اسم الأداة…", + "statusFilter": "تصفية الحالة", + "filterAll": "الكلّ", + "filterEnabled": "مفعّلة", + "filterDisabled": "معطّلة", + "selectedCount": "تمّ تحديد {{count}}", + "selectAll": "تحديد الكلّ", + "deselectAll": "إلغاء تحديد الكلّ", + "deleteSelected": "حذف مجمّع", + "deleteExecConfirm": "هل تريد حذف سجلّ التنفيذ هذا؟", + "batchDeleteFailed": "فشل الحذف المجمّع", + "managementTitle": "إدارة MCP", + "addExternal": "إضافة MCP خارجي", + "toolConfig": "إعدادات أدوات MCP", + "saveToolConfig": "حفظ إعدادات الأداة", + "alwaysVisibleLabel": "مثبّت", + "alwaysVisibleHint": "إبقاؤه ظاهرًا دائمًا في نتائج بحث الأدوات", + "alwaysVisibleBuiltinLabel": "افتراضي مدمج", + "alwaysVisibleBuiltinHint": "الأداة المدمجة في الخلفية مثبّتة افتراضيًا ولا يمكن تعطيلها", + "externalConfig": "إعدادات MCP الخارجي", + "loadingTools": "جارٍ تحميل الأدوات…", + "loadToolsTimeout": "انتهت مهلة تحميل الأدوات. قد يكون MCP الخارجي بطيئًا. انقر تحديث لإعادة المحاولة أو تحقّق من الاتصال.", + "loadToolsFailed": "فشل تحميل الأدوات", + "noTools": "لا توجد أدوات", + "externalBadge": "خارجي", + "externalFrom": "خارجي ({{name}})", + "externalToolFrom": "MCP خارجي - المصدر: {{name}}", + "clickToViewTools": "انقر لعرض الأدوات من {{name}}", + "filterBySource": "المصدر: {{name}}", + "clearSourceFilter": "مسح تصفية المصدر", + "noDescription": "لا يوجد وصف", + "paginationInfo": "{{start}}-{{end}} من {{total}} أداة", + "perPage": "لكلّ صفحة:", + "firstPage": "الأولى", + "prevPage": "السابقة", + "nextPage": "التالية", + "lastPage": "الأخيرة", + "pageInfo": "الصفحة {{page}} من {{total}}", + "currentPageEnabled": "المفعّلة في الصفحة الحالية", + "totalEnabled": "إجمالي المفعّلة", + "toolsConfigSaved": "تمّ حفظ إعدادات الأدوات!", + "saveToolsConfigFailed": "فشل حفظ إعدادات الأداة", + "getConfigFailed": "فشل جلب الإعدادات", + "noExternalMCP": "لم يُهيّأ أيّ MCP خارجي", + "clickToAddExternal": "انقر \"إضافة MCP خارجي\" للتهيئة", + "connected": "متّصل", + "connecting": "جارٍ الاتصال…", + "connectionFailed": "فشل الاتصال", + "disabled": "معطّل", + "disconnected": "غير متّصل", + "stopConnection": "إيقاف الاتصال", + "startConnection": "بدء الاتصال", + "stop": "إيقاف", + "start": "بدء", + "editConfig": "تعديل الإعدادات", + "deleteConfig": "حذف الإعدادات", + "transportMode": "النقل", + "toolCount": "عدد الأدوات", + "description": "الوصف", + "timeout": "المهلة", + "command": "الأمر", + "addExternalMCP": "إضافة MCP خارجي", + "editExternalMCP": "تعديل MCP خارجي", + "jsonEmpty": "لا يمكن أن يكون JSON فارغًا", + "jsonError": "خطأ في صيغة JSON", + "configMustBeObject": "خطأ في الإعدادات: يجب أن يكون كائن JSON مع name كمفتاح", + "configNeedOne": "خطأ في الإعدادات: يلزم عنصر إعداد واحد على الأقلّ", + "configNameEmpty": "خطأ في الإعدادات: لا يمكن أن يكون الاسم فارغًا", + "configMustBeObj": "خطأ في الإعدادات: يجب أن يكون \"{{name}}\" كائنًا", + "configNeedCommand": "خطأ في الإعدادات: يحتاج \"{{name}}\" إلى command (stdio) أو url (http/sse)", + "configStdioNeedCommand": "خطأ في الإعدادات: يحتاج وضع stdio لـ \"{{name}}\" إلى command", + "configHttpNeedUrl": "خطأ في الإعدادات: يحتاج وضع http لـ \"{{name}}\" إلى url", + "configSseNeedUrl": "خطأ في الإعدادات: يحتاج وضع sse لـ \"{{name}}\" إلى url", + "configEditMustContainName": "خطأ في الإعدادات: في وضع التعديل، يجب أن يحتوي JSON على اسم الإعداد \"{{name}}\"", + "saveSuccess": "تمّ الحفظ", + "deleteSuccess": "تمّ الحذف", + "deleteExternalConfirm": "هل تريد حذف MCP الخارجي \"{{name}}\"؟", + "operationFailed": "فشلت العملية", + "connectionFailedCheck": "فشل الاتصال. تحقّق من الإعدادات والشبكة.", + "connectionTimeout": "انتهت مهلة الاتصال. تحقّق من الإعدادات والشبكة.", + "totalCount": "الإجمالي", + "enabledCount": "مفعّلة", + "disabledCount": "معطّلة", + "connectedCount": "متّصلة", + "toolsCountValue": "🔧 {{count}} أداة", + "connectionErrorLabel": "خطأ في الاتصال:", + "secondsUnit": "ث", + "urlLabel": "URL", + "loadExternalMCPFailed": "فشل التحميل" + }, + "settings": { + "title": "إعدادات النظام", + "nav": { + "tokenUsage": "استخدام الرموز (Tokens)", + "basic": "أساسي", + "hitl": "التدخّل البشري", + "knowledge": "قاعدة المعرفة", + "c2": "C2", + "robots": "الروبوتات", + "terminal": "الطرفية", + "security": "الأمان", + "rbac": "صلاحيات المنصّة", + "audit": "سجلّات التدقيق", + "storage": "تنظيف التخزين", + "update": "التحديث بنقرة واحدة", + "infocollect": "إدارة الأصول" + }, + "infocollect": { + "title": "إدارة الأصول" + }, + "hitl": { + "title": "التدخّل البشري (HITL)", + "approvalTitle": "الموافقة", + "defaultReviewer": "المراجِع الافتراضي العام", + "defaultReviewerHint": "يُستخدَم عند عدم تحديد أي محادثة وللمحادثات الجديدة؛ ويمكن للشريط الجانبي في المحادثة تجاوزه.", + "auditModelTitle": "نموذج وكيل التدقيق", + "auditBackend": "محرّك الموافقة", + "auditBackendHint": "اختر واحدًا: نموذج محادثة متوافق مع OpenAI يُعيد JSON من الموجّه، أو TypeSafe Jev يتّخذ قرار سماح/حظر منظّمًا (دون تعديل الوسائط).", + "auditBackendOpenAI": "نموذج متوافق مع OpenAI", + "auditBackendTypeSafe": "TypeSafe Jev", + "auditModelReuseMain": "اتّباع إعدادات النموذج الرئيسي", + "auditModelBaseUrlPlaceholder": "اتركه فارغًا لإعادة استخدام الـ Base URL الرئيسي", + "auditModelApiKeyPlaceholder": "اتركه فارغًا لإعادة استخدام الـ API Key الرئيسي", + "auditModelName": "نموذج الموافقة", + "auditModelNamePlaceholder": "اتركه فارغًا لإعادة استخدام النموذج الرئيسي؛ ويُوصى بنموذج صغير", + "auditModelHint": "يُستخدَم فقط لموافقات وكيل التدقيق؛ ولا تستدعي الموافقة اليدوية أي نموذج.", + "auditModelTypeSafeHint": "يستخدم Jev نظام TypeSafe System One، وليس بروتوكول OpenAI. ومفتاح API مطلوب ولا يُعاد استخدامه من النموذج الرئيسي. اترك النموذج فارغًا لاستخدام jev-latest. وفي وضع المراجعة والتعديل يكتفي Jev بالسماح أو الحظر؛ ولن يُعيد كتابة الوسائط.", + "auditModelTypeSafeBaseUrlPlaceholder": "اتركه فارغًا لاستخدام https://api.typesafe.ai", + "auditModelTypeSafeApiKeyPlaceholder": "مفتاح TypeSafe API (مطلوب؛ لا يُعاد استخدامه من النموذج الرئيسي)", + "auditModelTypeSafeNamePlaceholder": "اتركه فارغًا لاستخدام jev-latest", + "testAuditModel": "اختبار نموذج التدقيق", + "testTypeSafeFillRequired": "أدخل مفتاح TypeSafe API أولًا", + "retentionDays": "أيّام الاحتفاظ بسجلّات التدقيق المحسومة", + "retentionDaysHint": "القيمة 0 تحتفظ بالسجلّات إلى الأبد؛ والترك فارغًا يستخدم الإعداد الافتراضي البالغ 90 يومًا.", + "toolWhitelist": "قائمة الأدوات المسموح بها دون موافقة", + "toolWhitelistPlaceholder": "read_file, list_dir, glob", + "toolWhitelistHint": "واحدة في كل سطر أو مفصولة بفواصل؛ وتتخطّى الأدوات المُدرجة في القائمة موافقة التدخّل البشري.", + "auditAgentTitle": "استراتيجية وكيل التدقيق", + "auditPromptApproval": "موجّه وضع الموافقة", + "auditPromptHint": "اتركه فارغًا لاستخدام الاستراتيجية الافتراضية للخلفية.", + "auditPromptTypeSafeHint": "المحرّك الحالي هو TypeSafe Jev: يُقيَّم نصّ الاستراتيجية المخصّص كأسئلة منظّمة. وتبقى القواعد المدمجة الخاصة بالعمليات التدميرية حدًّا أدنى صارمًا. ولا يستطيع Jev إعادة كتابة الوسائط.", + "auditPromptReviewEdit": "موجّه وضع المراجعة والتعديل", + "auditPromptReviewEditHint": "يمكن لوضع المراجعة والتعديل الموافقة مع تضييق editedArguments." + }, + "knowledge": { + "title": "قاعدة المعرفة" + }, + "c2": { + "title": "C2", + "sectionTitle": "C2 المدمج", + "enableLabel": "تفعيل C2 المدمج (المستمعون، الجلسات، الحمولات، أدوات MCP)", + "enableHint": "عند الإيقاف، لا يبدأ المستمعون ولا تُسجَّل أدوات C2 MCP؛ ويُخفى شريط C2 الجانبي — وهو مفيد لعمليات النشر المحلية فقط للمحادثة/المعرفة. انقر «تطبيق» للحفظ." + }, + "robots": { + "title": "إعدادات الروبوتات", + "description": "اضبط روبوتات WeChat (iLink) وWeCom وDingTalk وLark لتتمكّن من محادثة CyberStrikeAI على هاتفك دون فتح واجهة الويب.", + "managerTitle": "إدارة الروبوتات", + "managerDesc": "اختر نوع الروبوت أولًا، ثم اضبط الحقول التي تخصّ تلك المنصّة فقط.", + "newBot": "روبوت جديد", + "configure": "ضبط", + "statusNotConfigured": "غير مضبوط", + "statusConfigured": "مضبوط", + "statusEnabled": "مُفعّل", + "emptyTitle": "اختر روبوتًا لضبطه", + "emptyDesc": "اختر منصّة من الأعلى، أو استخدم «روبوت جديد» للاختيار حسب النوع.", + "createTitle": "روبوت جديد", + "createDesc": "اختر نوع روبوت لفتح إعداده المخصّص. يدعم هذا الإصدار إعدادًا مدمجًا واحدًا لكل منصّة.", + "wechat": { + "title": "WeChat / iLink", + "subtitle": "اربط حساب WeChat الشخصي عبر رمز QR وحادِث CyberStrikeAI على هاتفك", + "statusIdle": "غير مرتبط", + "statusBound": "متّصل", + "statusScanning": "جارٍ الربط…", + "step1": "إنشاء رمز QR", + "step2": "امسح في WeChat", + "step3": "تأكيد", + "enabled": "تفعيل روبوت WeChat", + "bindButton": "إنشاء رمز QR والربط", + "bindHint": "امسح بـ WeChat للتأكيد؛ وتُحفظ الإعدادات تلقائيًا.", + "qrLoading": "جارٍ إنشاء رمز QR…", + "verifyCodeLabel": "الرمز الظاهر على هاتفك (فقط إذا طلبه WeChat)", + "rebindButton": "إعادة الربط", + "boundBotId": "معرّف الروبوت المرتبط: ", + "verifyCodeSubmit": "إرسال", + "advanced": "إعدادات متقدّمة", + "baseUrl": "API Base URL", + "botType": "نوع الروبوت", + "botAgent": "وكيل الروبوت", + "ilinkBotId": "معرّف روبوت iLink (يُملأ بعد الربط)", + "boundSuccess": "تمّ الربط بنجاح. روبوت WeChat مُفعّل.", + "alreadyBound": "حساب WeChat هذا مرتبط بالفعل.", + "openLink": "رمز QR لا يظهر؟ افتح الرابط في WeChat على هاتفك" + }, + "wecom": { + "title": "WeCom", + "subtitle": "وضع استدعاء HTTP لروبوتات التطبيقات الداخلية للمؤسّسات", + "enabled": "تفعيل روبوت WeCom", + "token": "Token", + "tokenPlaceholder": "Token", + "encodingAesKey": "EncodingAESKey", + "encodingAesKeyPlaceholder": "EncodingAESKey (اتركه فارغًا للوضع العادي)", + "corpId": "CorpID", + "corpIdPlaceholder": "Corp ID", + "secret": "Secret", + "secretPlaceholder": "App Secret", + "agentId": "AgentID", + "agentIdPlaceholder": "App AgentId" + }, + "dingtalk": { + "title": "DingTalk", + "subtitle": "روبوت تطبيق داخلي للمؤسّسات يستخدم اتصال Stream", + "enabled": "تفعيل روبوت DingTalk", + "clientIdLabel": "Client ID (AppKey)", + "clientIdPlaceholder": "DingTalk App Key", + "clientSecretLabel": "Client Secret", + "clientSecretPlaceholder": "DingTalk App Secret", + "streamHint": "فعّل إمكانية الروبوت واضبط وصول البثّ في المنصّة المفتوحة." + }, + "lark": { + "title": "Lark", + "subtitle": "روبوت تطبيق للمؤسّسات للمحادثة المباشرة والإشارات في المجموعات", + "enabled": "تفعيل روبوت Lark", + "appIdLabel": "App ID", + "appIdPlaceholder": "Lark/Feishu App ID", + "appSecretLabel": "App Secret", + "appSecretPlaceholder": "Lark/Feishu App Secret", + "verifyTokenLabel": "Verify Token (اختياري)", + "verifyTokenPlaceholder": "Verification Token لاشتراك الأحداث" + }, + "telegram": { + "title": "Telegram", + "subtitle": "استقصاء طويل عبر Bot API؛ محادثة مباشرة وإشارة @ في المجموعات", + "enabled": "تفعيل روبوت Telegram", + "botToken": "Bot Token", + "botTokenHint": "يستقبل عبر استقصاء getUpdates الطويل؛ ولا حاجة إلى عنوان URL عام للاستدعاء", + "botUsername": "اسم مستخدم الروبوت (اختياري)", + "allowGroup": "السماح بمحادثة المجموعات (الردّ على @ فقط)" + }, + "slack": { + "title": "Slack", + "subtitle": "وضع Socket؛ ولا حاجة إلى عنوان URL عام للاستدعاء", + "enabled": "تفعيل روبوت Slack", + "botToken": "Bot Token (xoxb-)", + "appToken": "App-Level Token (xapp-)", + "appTokenHint": "يتطلّب نطاق connections:write لوضع Socket" + }, + "discord": { + "title": "Discord", + "subtitle": "WebSocket عبر Gateway؛ الرسائل المباشرة وإشارة @ في الخادم", + "enabled": "تفعيل روبوت Discord", + "botToken": "Bot Token", + "botTokenHint": "أنشئ الروبوت في Developer Portal؛ وفعّل Message Content Intent", + "allowGuild": "السماح بقنوات الخوادم (الردّ على @ فقط)" + }, + "qq": { + "title": "روبوت QQ", + "subtitle": "WebSocket عبر QQ Open Platform؛ C2C وإشارة @ في المجموعات", + "enabled": "تفعيل روبوت QQ", + "appId": "App ID", + "clientSecret": "Client Secret", + "secretHint": "من QQ Bot Open Platform؛ استخدم بيئة الاختبار قبل الإطلاق", + "sandbox": "بيئة الاختبار (Sandbox)" + } + }, + "apply": { + "button": "تطبيق الإعدادات", + "loadFailed": "فشل تحميل الإعدادات", + "fillRequired": "يرجى تعبئة جميع الحقول المطلوبة (المميّزة بـ *)", + "applyFailed": "فشل تطبيق الإعدادات", + "applySuccess": "تمّ تطبيق الإعدادات بنجاح!" + }, + "security": { + "changePassword": "تغيير كلمة المرور", + "fillPasswordHint": "أدخل كلمة المرور الحالية والجديدة بشكل صحيح. يجب ألّا تقلّ كلمة المرور الجديدة عن 8 أحرف، وأن تتطابق في الحقلين.", + "changePasswordFailed": "فشل تغيير كلمة المرور", + "passwordUpdated": "تمّ تحديث كلمة المرور. يرجى تسجيل الدخول مجدّدًا بكلمة المرور الجديدة." + } + }, + "auth": { + "sessionExpired": "انتهت الجلسة، يُرجى تسجيل الدخول مرّة أخرى", + "unauthorized": "غير مصرّح", + "forbidden": "صلاحيات غير كافية", + "requestFailed": "فشل الطلب", + "enterPassword": "يُرجى إدخال كلمة المرور", + "usernameRequired": "أدخل اسم مستخدم بطول 64 حرفًا كحدّ أقصى", + "loginFailedCheck": "فشل تسجيل الدخول، يُرجى التحقّق من اسم المستخدم أو كلمة المرور", + "loginFailedRetry": "فشل تسجيل الدخول، يُرجى المحاولة لاحقًا", + "loggedOut": "تمّ تسجيل الخروج" + }, + "knowledge": { + "title": "إدارة المعرفة", + "retrievalLogs": "سجلّ الاسترجاع", + "totalItems": "إجمالي العناصر", + "categories": "الفئات", + "addKnowledge": "إضافة معرفة", + "buildIndex": "بناء الفهرس", + "buildIndexConfirm": "هل تريد بناء المتّجهات لعناصر المعرفة التي لم تُفهرَس بعد؟", + "rebuildIndexFull": "إعادة بناء كاملة", + "rebuildIndexFullConfirm": "هل تريد إعادة فهرسة جميع عناصر المعرفة؟ قد يستغرق هذا بعض الوقت.", + "deleteItemConfirm": "هل تريد حذف عنصر المعرفة هذا؟", + "notEnabledTitle": "وظيفة قاعدة المعرفة غير مفعّلة", + "notEnabledHint": "يُرجى الانتقال إلى إعدادات النظام لتفعيل استرجاع المعرفة.", + "goToSettings": "الانتقال إلى الإعدادات" + }, + "roles": { + "title": "إدارة الأدوار", + "createRole": "إنشاء دور", + "searchPlaceholder": "البحث في الأدوار...", + "deleteConfirm": "هل تريد حذف هذا الدور؟", + "loadFailed": "فشل تحميل الأدوار", + "noDescription": "لا يوجد وصف", + "defaultRoleDescription": "دور افتراضي، دون موجّه مستخدم إضافي، يستخدم MCP الافتراضي", + "noMatchingRoles": "لا توجد أدوار مطابقة", + "noRoles": "لا توجد أدوار", + "enabled": "مفعّل", + "disabled": "معطّل", + "noDescriptionShort": "لا يوجد وصف" + }, + "skills": { + "title": "إدارة المهارات", + "monitorTitle": "مراقبة المهارات", + "createSkill": "إنشاء مهارة", + "callStats": "إحصائيات الاستدعاء", + "addSkill": "إضافة مهارة", + "editSkill": "تعديل مهارة", + "loadListFailed": "فشل تحميل قائمة المهارات", + "noSkills": "لا توجد مهارات. انقر \"إنشاء مهارة\" لإضافة أول مهارة.", + "noMatch": "لا توجد مهارات مطابقة", + "searchFailed": "فشل البحث", + "refreshed": "تم التحديث", + "loadDetailFailed": "فشل تحميل تفاصيل المهارة", + "viewFailed": "فشل عرض المهارة", + "saving": "جارٍ الحفظ...", + "saveFailed": "فشل حفظ المهارة", + "deleteFailed": "فشل حذف المهارة", + "loadStatsFailed": "فشل تحميل بيانات مراقبة المهارات", + "clearStatsConfirm": "هل تريد مسح جميع إحصائيات المهارات؟ لا يمكن التراجع عن هذا الإجراء.", + "statsCleared": "تم مسح إحصائيات المهارات", + "clearStatsFailed": "فشل مسح الإحصائيات", + "noDescription": "لا يوجد وصف", + "viewSkillTitle": "عرض المهارة: {{name}}", + "descriptionLabel": "الوصف:", + "pathLabel": "المسار:", + "modTimeLabel": "تاريخ التعديل:", + "contentLabel": "المحتوى:", + "cardVersion": "v{{version}}", + "cardScripts": "{{count}} نص برمجي", + "cardFiles": "{{count}} ملف", + "versionLabel": "الإصدار:", + "scriptsHeading": "النصوص البرمجية:", + "summaryHint": "(ملخّص — حمّل المحتوى الكامل عند الحاجة)", + "loadFullBody": "تحميل المحتوى الكامل", + "loadFullFailed": "فشل تحميل المحتوى الكامل للمهارة", + "nameRequired": "اسم المهارة مطلوب", + "contentRequired": "محتوى المهارة مطلوب", + "nameInvalid": "استخدم أحرفًا إنجليزية صغيرة وأرقامًا وشرطات فقط (قواعد تسمية مهارات الوكيل)", + "descriptionRequired": "الوصف مطلوب (يُكتب في مقدّمة ملف SKILL.md)", + "saveSuccess": "تم تحديث المهارة", + "createdSuccess": "تم إنشاء المهارة", + "deleteConfirm": "هل أنت متأكد من حذف المهارة \"{{name}}\"؟ لا يمكن التراجع عن هذا الإجراء.", + "deleteConfirmWithRoles": "هل أنت متأكد من حذف المهارة \"{{name}}\"؟\n\n⚠️ هذه المهارة مرتبطة حاليًا بعدد {{count}} من الأدوار:\n{{roles}}\n\nبعد الحذف، سيزيل النظام هذه المهارة تلقائيًا من تلك الأدوار.\n\nلا يمكن التراجع عن هذا الإجراء. هل تريد المتابعة؟", + "deleteSuccess": "تم حذف المهارة", + "deleteSuccessWithRoles": "تم حذف المهارة وإزالتها تلقائيًا من {{count}} من الأدوار: {{roles}}", + "loadFailedShort": "فشل التحميل", + "totalSkillsCount": "إجمالي المهارات", + "totalCallsCount": "إجمالي عدد الاستدعاءات", + "successfulCalls": "الاستدعاءات الناجحة", + "failedCalls": "الاستدعاءات الفاشلة", + "successRate": "معدّل النجاح", + "skillName": "اسم المهارة", + "totalCalls": "إجمالي الاستدعاءات", + "success": "نجاح", + "failure": "فشل", + "lastCallTime": "وقت آخر استدعاء", + "noCallRecords": "لا توجد سجلات استدعاء للمهارات بعد", + "loadStatsErrorShort": "فشل تحميل الإحصائيات", + "loadCallStatsError": "فشل تحميل إحصائيات الاستدعاء" + }, + "apiDocs": { + "pageTitle": "وثائق API - CyberStrikeAI", + "title": "وثائق API", + "subtitle": "وثائق API لمنصّة CyberStrikeAI مع اختبار عبر الإنترنت", + "authTitle": "مصادقة API", + "authAllNeedToken": "تتطلّب جميع نقاط نهاية API مصادقة بالرمز (Token).", + "authGetToken": "1. الحصول على الرمز (Token):", + "authGetTokenDesc": "بعد تسجيل الدخول في الواجهة الأمامية، يُحفَظ الرمز (Token) تلقائيًا. يمكنك أيضًا الحصول عليه عبر:", + "authUseToken": "2. استخدام الرمز (Token):", + "authUseTokenDesc": "أضِف ترويسة Authorization:", + "authTip": "💡 ستستخدم هذه الصفحة الرمز (Token) الخاصّ بجلستك تلقائيًا؛ لا حاجة لإدخاله يدويًا.", + "tokenDetected": "✓ تمّ اكتشاف الرمز (Token) - يمكنك اختبار نقاط نهاية API مباشرةً", + "tokenNotDetected": "⚠ لم يُكتشَف أيّ رمز (Token) - يُرجى تسجيل الدخول في الواجهة الأمامية أوّلًا، ثمّ تحديث هذه الصفحة. عند الاختبار، أضِف Authorization: Bearer token في ترويسة الطلب", + "sidebarGroupTitle": "مجموعات API", + "allApis": "جميع واجهات API", + "loading": "جارٍ التحميل…", + "loadingDesc": "جارٍ تحميل وثائق API", + "errorLoginRequired": "يلزم تسجيل الدخول لعرض وثائق API. يُرجى تسجيل الدخول في الواجهة الأمامية أوّلًا، ثمّ تحديث هذه الصفحة.", + "errorLoadSpec": "فشل تحميل مواصفات API: ", + "errorLoadFailed": "فشل تحميل وثائق API: ", + "errorSpecInvalid": "صيغة مواصفات API غير صالحة", + "loadFailed": "فشل التحميل", + "backToLogin": "العودة إلى تسجيل الدخول", + "noApis": "لا توجد واجهات API", + "noEndpointsInGroup": "لا توجد نقاط نهاية API في هذه المجموعة", + "sectionDescription": "الوصف", + "viewDetailDesc": "عرض التفاصيل", + "hideDetailDesc": "إخفاء التفاصيل", + "noDescription": "لا يوجد وصف", + "sectionParams": "المعاملات", + "paramName": "المعامل", + "type": "النوع", + "description": "الوصف", + "required": "مطلوب", + "optional": "اختياري", + "sectionRequestBody": "جسم الطلب", + "example": "مثال", + "exampleJson": "مثال JSON:", + "sectionResponse": "الاستجابة", + "testSection": "اختبار", + "requestBodyJson": "جسم الطلب (JSON)", + "queryParams": "معاملات الاستعلام:", + "sendRequest": "إرسال الطلب", + "copyCurl": "نسخ cURL", + "clearResult": "مسح النتيجة", + "copyCurlTitle": "نسخ أمر cURL", + "clearResultTitle": "مسح نتيجة الاختبار", + "sendingRequest": "جارٍ إرسال الطلب…", + "errorPathParamRequired": "معامل المسار {{name}} مطلوب", + "errorQueryParamRequired": "معامل الاستعلام {{name}} مطلوب", + "errorTokenRequired": "لم يُكتشَف أيّ رمز (Token). يُرجى تسجيل الدخول في الواجهة الأمامية والتحديث، أو أضِف Authorization: Bearer your_token في ترويسة الطلب", + "errorJsonInvalid": "جسم الطلب JSON غير صالح: ", + "requestFailed": "فشل الطلب: ", + "copied": "تمّ النسخ", + "curlCopied": "تمّ نسخ أمر curl إلى الحافظة!", + "copyFailedManual": "فشل النسخ، يُرجى النسخ يدويًا:\n\n", + "curlGenFailed": "فشل إنشاء أمر cURL: ", + "requestBodyPlaceholder": "أدخِل جسم الطلب بصيغة JSON", + "tags": { + "auth": "المصادقة", + "conversationManagement": "إدارة المحادثات", + "conversationInteraction": "التفاعل في المحادثة", + "batchTasks": "المهامّ المجمّعة", + "vulnerabilityManagement": "إدارة الثغرات", + "roleManagement": "إدارة الأدوار", + "skillsManagement": "إدارة المهارات", + "monitoring": "المراقبة", + "configManagement": "إدارة الإعدادات", + "externalMCPManagement": "إدارة MCP الخارجي", + "attackChain": "سلسلة الهجوم", + "knowledgeBase": "قاعدة المعرفة", + "mcp": "MCP", + "fofaRecon": "استطلاع FOFA", + "terminal": "الطرفية", + "webshellManagement": "إدارة WebShell", + "chatUploads": "مرفوعات المحادثة", + "robotIntegration": "تكامل الروبوت", + "markdownAgents": "وكلاء Markdown", + "projectManagement": "إدارة المشاريع", + "assetManagement": "إدارة الأصول" + }, + "summary": { + "login": "تسجيل دخول المستخدم", + "logout": "تسجيل خروج المستخدم", + "changePassword": "تغيير كلمة المرور", + "validateToken": "التحقّق من الرمز (Token)", + "createConversation": "إنشاء محادثة", + "listConversations": "سرد المحادثات", + "getConversationDetail": "جلب تفاصيل المحادثة", + "updateConversation": "تحديث المحادثة", + "deleteConversation": "حذف المحادثة", + "getConversationResult": "جلب نتيجة المحادثة", + "sendMessageNonStream": "إرسال رسالة والحصول على ردّ الذكاء الاصطناعي (بدون بثّ)", + "sendMessageStream": "إرسال رسالة والحصول على ردّ الذكاء الاصطناعي (بثّ)", + "cancelTask": "إلغاء المهمّة", + "listRunningTasks": "سرد المهامّ قيد التشغيل", + "listCompletedTasks": "سرد المهامّ المكتملة", + "createBatchQueue": "إنشاء طابور مهامّ مجمّعة", + "listBatchQueues": "سرد طوابير المهامّ المجمّعة", + "getBatchQueue": "جلب طابور المهامّ المجمّعة", + "deleteBatchQueue": "حذف طابور المهامّ المجمّعة", + "startBatchQueue": "بدء طابور المهامّ المجمّعة", + "pauseBatchQueue": "إيقاف طابور المهامّ المجمّعة مؤقّتًا", + "addTaskToQueue": "إضافة مهمّة إلى الطابور", + "sqlInjectionScan": "فحص حقن SQL", + "portScan": "فحص المنافذ", + "updateBatchTask": "تحديث المهمّة المجمّعة", + "deleteBatchTask": "حذف المهمّة المجمّعة", + "listVulnerabilities": "سرد الثغرات", + "createVulnerability": "إنشاء ثغرة", + "getVulnerabilityStats": "جلب إحصاءات الثغرات", + "getVulnerability": "جلب الثغرة", + "updateVulnerability": "تحديث الثغرة", + "deleteVulnerability": "حذف الثغرة", + "listRoles": "سرد الأدوار", + "createRole": "إنشاء دور", + "getRole": "جلب الدور", + "updateRole": "تحديث الدور", + "deleteRole": "حذف الدور", + "getAvailableSkills": "جلب قائمة المهارات المتاحة", + "listSkills": "سرد المهارات", + "createSkill": "إنشاء مهارة", + "getSkillStats": "جلب إحصاءات المهارة", + "clearSkillStats": "مسح إحصاءات المهارة", + "getSkill": "جلب المهارة", + "updateSkill": "تحديث المهارة", + "deleteSkill": "حذف المهارة", + "getBoundRoles": "جلب الأدوار المرتبطة", + "clearSkillStatsAlt": "مسح إحصاءات المهارة", + "getMonitorInfo": "جلب معلومات المراقبة", + "getExecutionRecords": "جلب سجلّات التنفيذ", + "deleteExecutionRecord": "حذف سجلّ التنفيذ", + "batchDeleteExecutionRecords": "حذف سجلّات التنفيذ بشكل مجمّع", + "getStats": "جلب الإحصاءات", + "getConfig": "جلب الإعدادات", + "updateConfig": "تحديث الإعدادات", + "getToolConfig": "جلب إعدادات الأداة", + "applyConfig": "تطبيق الإعدادات", + "listExternalMCP": "سرد MCP الخارجي", + "getExternalMCPStats": "جلب إحصاءات MCP الخارجي", + "getExternalMCP": "جلب MCP الخارجي", + "addOrUpdateExternalMCP": "إضافة أو تحديث MCP خارجي", + "stdioModeConfig": "إعدادات وضع stdio", + "sseModeConfig": "إعدادات وضع SSE", + "deleteExternalMCP": "حذف MCP الخارجي", + "startExternalMCP": "بدء MCP الخارجي", + "stopExternalMCP": "إيقاف MCP الخارجي", + "getAttackChain": "جلب سلسلة الهجوم", + "regenerateAttackChain": "إعادة توليد سلسلة الهجوم", + "pinConversation": "تثبيت المحادثة", + "getCategories": "جلب الفئات", + "listKnowledgeItems": "سرد عناصر المعرفة", + "createKnowledgeItem": "إنشاء عنصر معرفة", + "getKnowledgeItem": "جلب عنصر المعرفة", + "updateKnowledgeItem": "تحديث عنصر المعرفة", + "deleteKnowledgeItem": "حذف عنصر المعرفة", + "getIndexStatus": "جلب حالة الفهرس", + "startKnowledgeIndex": "بناء الفهرس", + "scanKnowledgeBase": "فحص قاعدة المعرفة", + "searchKnowledgeBase": "البحث في قاعدة المعرفة", + "basicSearch": "بحث أساسي", + "searchByRiskType": "البحث حسب نوع الخطر", + "getRetrievalLogs": "جلب سجلّات الاسترجاع", + "deleteRetrievalLog": "حذف سجلّ الاسترجاع", + "mcpEndpoint": "نقطة نهاية MCP", + "listAllTools": "سرد جميع الأدوات", + "invokeTool": "استدعاء أداة", + "initConnection": "تهيئة الاتصال", + "successResponse": "استجابة ناجحة", + "errorResponse": "استجابة خطأ", + "deleteConversationTurn": "حذف دور في المحادثة", + "getMessageProcessDetails": "جلب تفاصيل معالجة الرسالة", + "rerunBatchQueue": "إعادة تشغيل طابور المهامّ المجمّعة", + "updateBatchQueueMetadata": "تحديث بيانات الطابور الوصفية", + "updateBatchQueueSchedule": "تحديث جدولة الطابور", + "setBatchQueueScheduleEnabled": "تبديل الجدولة التلقائية عبر cron", + "fofaSearch": "بحث FOFA", + "fofaParse": "تحليل اللغة الطبيعية إلى صياغة FOFA", + "testOpenAI": "اختبار اتصال OpenAI API", + "terminalRun": "تشغيل أمر طرفية", + "terminalRunStream": "تشغيل أمر طرفية (بثّ)", + "terminalWS": "طرفية WebSocket", + "listWebshellConnections": "سرد اتصالات WebShell", + "createWebshellConnection": "إنشاء اتصال WebShell", + "updateWebshellConnection": "تحديث اتصال WebShell", + "deleteWebshellConnection": "حذف اتصال WebShell", + "getWebshellConnectionState": "جلب حالة الاتصال", + "saveWebshellConnectionState": "حفظ حالة الاتصال", + "getWebshellAIHistory": "جلب سجلّ محادثة الذكاء الاصطناعي", + "listWebshellAIConversations": "سرد محادثات الذكاء الاصطناعي", + "webshellExec": "تنفيذ أمر WebShell", + "webshellFileOp": "عملية ملفّ WebShell", + "listChatUploads": "سرد المرفوعات", + "exportChatUploads": "تصدير المرفوعات", + "uploadChatFile": "رفع ملفّ", + "deleteChatUpload": "حذف مرفوع", + "downloadChatUpload": "تنزيل مرفوع", + "getChatUploadContent": "جلب المحتوى النصّي للملفّ", + "putChatUploadContent": "كتابة المحتوى النصّي للملفّ", + "mkdirChatUpload": "إنشاء دليل مرفوعات", + "renameChatUpload": "إعادة تسمية المرفوع", + "wecomCallbackVerify": "التحقّق من ردّ نداء WeCom", + "wecomCallbackMessage": "ردّ نداء رسالة WeCom", + "dingtalkCallback": "ردّ نداء رسالة DingTalk", + "larkCallback": "ردّ نداء رسالة Lark", + "testRobot": "اختبار معالجة رسائل الروبوت", + "listMarkdownAgents": "سرد وكلاء Markdown", + "createMarkdownAgent": "إنشاء وكيل Markdown", + "getMarkdownAgent": "جلب تفاصيل وكيل Markdown", + "updateMarkdownAgent": "تحديث وكيل Markdown", + "deleteMarkdownAgent": "حذف وكيل Markdown", + "listSkillPackageFiles": "سرد ملفّات حزمة المهارة", + "getSkillPackageFile": "جلب محتوى ملفّ حزمة المهارة", + "putSkillPackageFile": "كتابة ملفّ حزمة المهارة", + "batchGetToolNames": "جلب أسماء الأدوات بشكل مجمّع", + "getKnowledgeStats": "جلب إحصاءات قاعدة المعرفة", + "listProjects": "سرد المشاريع", + "createProject": "إنشاء مشروع", + "getProject": "جلب المشروع", + "updateProject": "تحديث المشروع", + "deleteProject": "حذف المشروع", + "listProjectFacts": "سرد الحقائق أو جلب حقيقة حسب المفتاح", + "upsertProjectFact": "إنشاء/تحديث حقيقة", + "getProjectFactGraph": "جلب رسم مسار هجوم حقائق المشروع", + "listProjectFactEdges": "سرد جميع حوافّ حقائق المشروع", + "createProjectFactEdge": "إضافة حافّة حقيقة", + "deleteProjectFactEdge": "حذف حافّة حقيقة", + "promoteAttackChainToProject": "ترقية سلسلة هجوم المحادثة إلى رسم حقائق المشروع", + "importAssets": "استيراد الأصول بشكل مجمّع" + }, + "response": { + "getSuccess": "نجاح", + "unauthorized": "غير مُصرَّح", + "unauthorizedToken": "غير مُصرَّح، يلزم رمز (Token) صالح", + "createSuccess": "تمّ الإنشاء بنجاح", + "badRequest": "طلب غير صالح", + "conversationNotFound": "المحادثة غير موجودة", + "conversationOrResultNotFound": "المحادثة أو النتيجة غير موجودة", + "badRequestTaskEmpty": "طلب غير صالح (مثلًا المهمّة فارغة)", + "badRequestConfig": "طلب غير صالح (مثلًا إعدادات غير صالحة أو حقول مطلوبة مفقودة)", + "badRequestQueryEmpty": "طلب غير صالح (مثلًا الاستعلام فارغ)", + "methodNotAllowed": "الطريقة غير مسموح بها (POST فقط)", + "loginSuccess": "تمّ تسجيل الدخول بنجاح", + "invalidPassword": "كلمة مرور غير صالحة", + "logoutSuccess": "تمّ تسجيل الخروج بنجاح", + "passwordChanged": "تمّ تغيير كلمة المرور بنجاح", + "tokenValid": "الرمز (Token) صالح", + "tokenInvalid": "الرمز (Token) غير صالح أو منتهي الصلاحية", + "conversationCreated": "تمّ إنشاء المحادثة", + "internalError": "خطأ داخلي في الخادم", + "updateSuccess": "تمّ التحديث بنجاح", + "deleteSuccess": "تمّ الحذف بنجاح", + "queueNotFound": "الطابور غير موجود", + "startSuccess": "تمّ البدء بنجاح", + "pauseSuccess": "تمّ الإيقاف المؤقّت بنجاح", + "addSuccess": "تمّت الإضافة بنجاح", + "taskNotFound": "المهمّة غير موجودة", + "cancelSubmitted": "تمّ إرسال طلب الإلغاء", + "noRunningTask": "لم يُعثَر على مهمّة قيد التشغيل", + "messageSent": "تمّ إرسال الرسالة، وأُعيد ردّ الذكاء الاصطناعي", + "streamResponse": "استجابة بثّ (Server-Sent Events)", + "badRequestOrDeleteFailed": "طلب غير صالح أو فشل الحذف", + "paramError": "معاملات غير صالحة", + "onlyCompletedOrCancelledCanRerun": "يمكن إعادة تشغيل الطوابير المكتملة أو الملغاة فقط", + "badRequestOrQueueRunning": "طلب غير صالح أو الطابور قيد التشغيل", + "setSuccess": "تمّ الضبط بنجاح", + "searchSuccess": "تمّ البحث بنجاح", + "parseSuccess": "تمّ التحليل بنجاح", + "testResult": "نتيجة الاختبار", + "executionDone": "اكتمل التنفيذ", + "sseEventStream": "بثّ أحداث SSE", + "wsEstablished": "تمّ تأسيس اتصال WebSocket", + "fileDownload": "تنزيل الملفّ", + "fileNotFound": "الملفّ غير موجود", + "writeSuccess": "تمّت الكتابة بنجاح", + "renameSuccess": "تمّت إعادة التسمية بنجاح", + "wecomVerifySuccess": "نجح التحقّق، وأُعيد echostr بعد فكّ التشفير", + "processSuccess": "تمّت المعالجة بنجاح", + "agentNotFound": "الوكيل غير موجود", + "saveSuccess": "تمّ الحفظ بنجاح", + "operationResult": "نتيجة العملية", + "executionResult": "نتيجة التنفيذ", + "connectionNotFound": "الاتصال غير موجود", + "projectList": "قائمة المشاريع", + "projectDetail": "تفاصيل المشروع", + "projectFactList": "قائمة الحقائق أو حقيقة واحدة (قد تتضمّن link_counts / outgoing_links)", + "success": "نجاح", + "factGraphNodesEdges": "العُقد + الحوافّ", + "edgeList": "قائمة الحوافّ", + "edgeCreated": "تمّ إنشاء الحافّة", + "promoteAttackChainResult": "نتيجة الترقية (حقائق/حوافّ/رسم)", + "assetImportCompleted": "اكتمل الاستيراد", + "assetImportValidationFailed": "فشل التحقّق من عدد الأصول أو الحقول", + "assetImportForbidden": "صلاحية asset:write مفقودة أو لا يوجد وصول إلى المشروع المحدَّد", + "assetImportTransactionFailed": "فشلت معاملة الاستيراد" + } + }, + "mcpMonitor": { + "toolRowNoCompletedAriaLabel": "{{name}}، {{total}} استدعاء، لا توجد نتائج مكتملة؛ عرض سجلّات التنفيذ", + "rateExcludesBlocked": "يشمل معدّل النجاح الاستدعاءات الناجحة والفاشلة، ويستثني الاستدعاءات المحظورة والموقوفة", + "timelineBlockedLegend": "محظور", + "blockedCount": "محظور {{n}}", + "statusBlocked": "محظور", + "deselectAll": "إلغاء تحديد الكلّ", + "statusPending": "قيد الانتظار", + "statusQueued": "في الطابور", + "statusSuccess": "نجح", + "statusCompleted": "مكتمل", + "statusRunning": "قيد التشغيل", + "statusFailed": "فشل", + "statusHardTimeout": "انتهت المهلة", + "statusCancelled": "ملغى", + "statusOrphaned": "مُهمَل", + "terminateExecution": "إيقاف", + "loading": "جارٍ التحميل…", + "noStatsData": "لا توجد بيانات إحصائية", + "noExecutions": "لا توجد سجلّات تنفيذ", + "emptyHint": "ستظهر سجلّات التنفيذ هنا بعد استدعائك لأدوات MCP في المحادثة أو المهامّ", + "noRecordsWithFilter": "لا توجد سجلّات بالتصفية الحالية", + "paginationInfo": "عرض {{start}}-{{end}} من {{total}} سجلّ", + "perPageLabel": "لكلّ صفحة", + "firstPage": "الأولى", + "prevPage": "السابقة", + "nextPage": "التالية", + "lastPage": "الأخيرة", + "pageInfo": "الصفحة {{page}} من {{total}}", + "loadStatsError": "فشل تحميل الإحصاءات", + "loadExecutionsError": "فشل تحميل سجلّات التنفيذ", + "totalCalls": "إجمالي الاستدعاءات", + "successFailed": "ناجحة {{success}} / فاشلة {{failed}}", + "successRate": "معدّل النجاح", + "statsFromAllTools": "من جميع استدعاءات الأدوات", + "lastCall": "آخر استدعاء", + "lastRefreshTime": "آخر تحديث", + "noCallsYet": "لا توجد استدعاءات بعد", + "noCompletedYet": "لا توجد نتائج مكتملة بعد", + "unknownTool": "أداة غير معروفة", + "successFailedRate": "ناجحة {{success}} / فاشلة {{failed}} · معدّل نجاح {{rate}}%", + "topToolsTitle": "أعلى {{n}} أدوات حسب الاستدعاءات", + "toolRankingTitle": "ترتيب استدعاء الأدوات", + "toolStatsTitle": "إحصاءات الأدوات", + "toolStatsHint": "انقر على جزء من الشريط أو صفّ لتصفية السجلّات أدناه؛ مرِّر المؤشّر للإبراز", + "scopeCumulative": "كلّ الأوقات", + "scopeTimeline": "فترة الاتّجاه", + "filterActive": "مُصفّى: {{tool}}", + "kpiScopeNote": "الإجماليات مدى الحياة", + "columnCalls": "الاستدعاءات", + "columnShare": "الحصّة", + "columnSuccessRate": "معدّل النجاح", + "rankingSummary": "أعلى {{n}} {{pct}}% · {{total}} إجمالي الاستدعاءات", + "barVolumeLegend": "طول الشريط: حجم الاستدعاءات النسبي؛ أخضر/أحمر: حصّة النجاح مقابل الفشل", + "clickToFilterTool": "انقر على صفّ لتصفية السجلّات أدناه", + "toolRowAriaLabel": "{{name}}، {{total}} استدعاء، معدّل نجاح {{rate}}%. انقر لتصفية السجلّات.", + "successRateAria": "معدّل النجاح {{rate}}%", + "filterByToolTitle": "مُصفّى حسب: {{tool}}", + "clearToolFilter": "مسح تصفية الأداة", + "successCount": "ناجحة {{n}}", + "failedCount": "فاشلة {{n}}", + "neutralCount": "موقوفة {{n}}", + "rateHealthy": "يعمل بسلاسة", + "rateWarning": "رُصدت بعض حالات الفشل", + "rateCritical": "معدّل فشل مرتفع", + "statsSubtitle": "جرى التحديث {{time}} · {{count}} أداة", + "retentionHint": "تُحفَظ سجلّات التنفيذ لمدّة {{days}} يومًا، ثمّ تُحذَف تلقائيًا.", + "timelineTitle": "اتّجاه الاستدعاءات", + "timelineHint": "جميع الأدوات مجتمعة (غير مقسّمة حسب الأداة)", + "timelineRange24h": "24 ساعة", + "timelineRange7d": "7 أيّام", + "timelineRange30d": "30 يومًا", + "timelineSummary": "{{total}} استدعاء في النطاق · الذروة {{peak}}", + "timelineSparseHint": "معظم الفترات فارغة؛ الذروة {{peak}} استدعاء عند {{peakTime}}", + "timelineNoData": "لا توجد استدعاءات في هذه الفترة", + "timelineLoading": "جارٍ تحميل الاتّجاه…", + "timelineEmptyHint": "غيّر النطاق الزمني أو استدعِ أدوات MCP في المحادثة أو المهامّ", + "timelineLoadError": "فشل تحميل اتّجاه الاستدعاءات", + "timelineTotalLegend": "إجمالي الاستدعاءات", + "timelineFailedLegend": "فاشلة", + "timelineTooltip": "{{time}}: {{total}} استدعاء ({{failed}} فاشلة، {{blocked}} محظورة)", + "distTitle": "توزيع الاستدعاءات", + "distLegend": "مساحة الشريحة تُظهر حصّة جميع الاستدعاءات", + "distClickHint": "انقر على جزء من الشريط لتصفية السجلّات", + "distHeaderHint": "{{n}} إجمالي الاستدعاءات", + "distSegmentAria": "{{name}}، {{pct}}% من الاستدعاءات، {{calls}} مرّة", + "distOthersNoFilter": "لا يمكن تصفية الأدوات الأخرى بشكل فردي", + "distTotalCalls": "{{n}} إجمالي الاستدعاءات", + "distTop6Share": "حصّة أعلى {{n}} من جميع الاستدعاءات", + "distOthers": "أدوات أخرى", + "distCallsUnit": "{{n}} استدعاء", + "riskTitle": "تنبيهات الفشل", + "riskNone": "لا توجد حالات فشل حديثة", + "riskItem": "{{name}}: {{failed}} / {{total}} فاشلة", + "selectedToolTitle": "التصفية النشطة", + "selectedToolEmpty": "انقر على أداة على اليسار لتصفية السجلّات أدناه", + "selectedToolStats": "{{total}} استدعاء · {{success}} ناجحة · {{failed}} فاشلة · معدّل نجاح {{rate}}%", + "columnTool": "الأداة", + "columnStatus": "الحالة", + "columnStartTime": "وقت البدء", + "columnDuration": "المدّة", + "columnActions": "الإجراءات", + "viewDetail": "عرض التفاصيل", + "delete": "حذف", + "deleteExecTitle": "حذف سجلّ التنفيذ هذا", + "deleteExecConfirmSingle": "هل أنت متأكّد من حذف سجلّ التنفيذ هذا؟ لا يمكن التراجع عن ذلك.", + "deleteExecFailed": "فشل حذف سجلّ التنفيذ", + "execDeleted": "تمّ حذف سجلّ التنفيذ", + "selectExecFirst": "يُرجى تحديد سجلّ (سجلّات) التنفيذ المراد حذفها أوّلًا", + "batchDeleteConfirm": "هل أنت متأكّد من حذف {{count}} سجلّ (سجلّات) التنفيذ المحدَّدة؟ لا يمكن التراجع عن ذلك.", + "batchDeleteSuccess": "تمّ حذف {{count}} سجلّ (سجلّات) التنفيذ بنجاح", + "unknown": "غير معروف", + "durationSeconds": "{{n}} ثانية", + "durationMinutes": "{{minutes}} دقيقة {{seconds}} ثانية", + "durationMinutesOnly": "{{minutes}} دقيقة", + "durationHours": "{{hours}} ساعة {{minutes}} دقيقة", + "durationHoursOnly": "{{hours}} ساعة" + }, + "knowledgePage": { + "totalContent": "إجمالي المحتوى", + "categoryFilter": "تصفية الفئة", + "all": "الكلّ", + "searchPlaceholder": "البحث في المعرفة...", + "loading": "جارٍ التحميل..." + }, + "retrievalLogs": { + "totalRetrievals": "إجمالي عمليات الاسترجاع", + "successRetrievals": "ناجحة", + "successRate": "معدّل النجاح", + "retrievedItems": "العناصر المُسترجَعة", + "conversationId": "معرّف المحادثة", + "messageId": "معرّف الرسالة", + "filter": "تصفية", + "optionalConversation": "اختياري: التصفية حسب المحادثة", + "optionalMessage": "اختياري: التصفية حسب الرسالة", + "loading": "جارٍ التحميل...", + "noRecords": "لا توجد سجلّات استرجاع بعد", + "noQuery": "لا يوجد محتوى استعلام", + "itemsUnit": "عنصر", + "hasResults": "توجد نتائج", + "noResults": "لا توجد نتائج", + "clickToCopy": "انقر للنسخ", + "retrievalResult": "نتيجة الاسترجاع", + "foundCount": "تمّ العثور على {{count}} عنصر معرفة ذي صلة", + "foundUnknown": "تمّ العثور على معرفة ذات صلة (العدد غير معروف)", + "noMatch": "لا توجد عناصر معرفة مطابقة", + "retrievedItemsLabel": "عناصر المعرفة المُسترجَعة:", + "viewDetails": "عرض التفاصيل", + "loadError": "فشل تحميل سجلّات الاسترجاع", + "detailError": "تعذّر الحصول على تفاصيل الاسترجاع", + "deleteError": "فشل حذف سجلّ الاسترجاع", + "detailsTitle": "تفاصيل الاسترجاع", + "queryInfo": "معلومات الاستعلام", + "queryContent": "محتوى الاستعلام:", + "retrievalInfo": "معلومات الاسترجاع", + "riskType": "نوع الخطر", + "retrievalTime": "وقت الاسترجاع", + "noItemDetails": "لم يُعثر على تفاصيل عنصر المعرفة", + "noContentPreview": "لا توجد معاينة للمحتوى", + "untitled": "بلا عنوان", + "uncategorized": "غير مصنّف", + "relatedInfo": "معلومات ذات صلة", + "itemsCount": "{{count}} عنصر معرفة", + "deleteConfirm": "حذف سجلّ الاسترجاع هذا؟" + }, + "infoCollectPage": { + "title": "الاستطلاع", + "reset": "إعادة تعيين", + "confirm": "موافق", + "fofaQuerySyntax": "صيغة استعلام FOFA", + "naturalLanguage": "اللغة الطبيعية (يحلّلها الذكاء الاصطناعي إلى FOFA)", + "returnCount": "عدد النتائج المُعادة", + "pageNum": "الصفحة", + "returnFields": "الحقول المُعادة (مفصولة بفواصل)", + "queryResults": "نتائج الاستعلام", + "selectedRows": "{{count}} محدَّد", + "selectedRowsZero": "0 محدَّد", + "columns": "الأعمدة", + "exportCsv": "تصدير CSV", + "exportJson": "تصدير JSON", + "exportXlsx": "تصدير XLSX", + "batchScan": "فحص جماعي", + "showColumns": "إظهار الأعمدة", + "columnsPanelAll": "تحديد الكل", + "columnsPanelNone": "إلغاء تحديد الكل", + "columnsPanelClose": "إغلاق", + "formHint": "راجع مستندات FOFA للاطّلاع على صيغة الاستعلام؛ وهي تدعم && / || / ().", + "parseBtn": "تحليل بالذكاء الاصطناعي", + "parseHint": "ستُفتح النتيجة في نافذة منبثقة للتعديل قبل تشغيل الاستعلام.", + "minFields": "الحقول الدنيا", + "webCommon": "ويب شائعة", + "intelEnhanced": "استخبارات مُعزَّزة", + "presetApache": "Apache + الصين", + "presetLogin": "صفحة تسجيل الدخول + الصين", + "presetDomain": "حسب النطاق", + "presetIp": "حسب IP", + "queryPresetsAria": "إعدادات استعلام FOFA المسبقة", + "fieldsPresetsAria": "إعدادات حقول FOFA المسبقة", + "resultsToolbarAria": "شريط أدوات النتائج", + "fillExample": "تعبئة مثال", + "parseBtnTitle": "تحليل اللغة الطبيعية إلى استعلام FOFA", + "minFieldsTitle": "للتصدير السريع", + "webCommonTitle": "للتصفّح والتصفية", + "intelEnhancedTitle": "مزيد من البصمات/الاستخبارات", + "fullLabel": "كامل", + "nlPlaceholder": "مثلًا: مواقع Apache في ميزوري بالولايات المتحدة، العنوان يحتوي على Home", + "showHideColumns": "إظهار/إخفاء الأعمدة", + "exportCsvTitle": "تصدير النتائج بصيغة CSV (UTF-8)", + "exportJsonTitle": "تصدير النتائج بصيغة JSON", + "exportXlsxTitle": "تصدير النتائج بصيغة Excel", + "batchScanTitle": "إنشاء قائمة انتظار مهامّ جماعية من الصفوف المحدَّدة" + }, + "chatFilesPage": { + "title": "إدارة الملفات", + "intro": "تظهر هنا الملفات المرفوعة في المحادثة. اسحب الملفات إلى القائمة أدناه، أو انقر على «رفع» لاختيار الملفات (يُسمح بعدة ملفات). انقر على «نسخ المسار» لنسخ المسار المطلق على الخادم ولصقه في المحادثة كي يتمكن النموذج من الإشارة إلى الملف.", + "export": "تصدير النتائج الحالية", + "exportStarted": "بدأ تصدير الملفات الحالية", + "exportEmpty": "لا توجد ملفات للتصدير", + "upload": "رفع", + "conversationFilter": "معرّف المحادثة", + "conversationPlaceholder": "اتركه فارغًا لعرض الكل", + "projectFilter": "معرّف المشروع", + "projectPlaceholder": "اتركه فارغًا لعرض الكل", + "searchName": "اسم الملف", + "searchNamePlaceholder": "التصفية حسب اسم الملف", + "sourceFilter": "المصدر", + "sourceAll": "الكل", + "sourceUpload": "مرفوعات المحادثة", + "sourceReduction": "مخرجات الأدوات", + "sourceWorkspace": "ملفات مساحة العمل", + "sourceConversationArtifact": "منتجات المحادثة", + "groupBy": "التجميع حسب", + "groupNone": "بلا تجميع (قائمة مسطّحة)", + "groupByDate": "حسب التاريخ", + "groupByConversation": "حسب المحادثة", + "groupByProject": "حسب المشروع", + "groupByFolder": "حسب المجلد (التنقل بالمسار)", + "projectUnbound": "مشروع غير مرتبط", + "paginationInfo": "عرض {{start}}-{{end}} من {{total}} ملف", + "paginationPage": "الصفحة {{page}} من {{totalPages}}", + "pageSize": "لكل صفحة", + "prevPage": "السابق", + "nextPage": "التالي", + "browseRoot": "الملفات", + "treeUploadsRoot": "مرفوعات المحادثة", + "treeReductionRoot": "مخرجات الأدوات", + "treeWorkspaceRoot": "ملفات مساحة العمل", + "treeArtifactsRoot": "منتجات المحادثة", + "browseUp": "للأعلى", + "enterFolderTitle": "فتح المجلد", + "copyFolderPathTitle": "نسخ مسار المجلد", + "folderPathCopied": "تم نسخ مسار المجلد — الصقه في المحادثة عند الحاجة", + "folderEmpty": "هذا المجلد فارغ", + "confirmDeleteFolder": "حذف هذا المجلد وكل ما بداخله؟ لا يمكن التراجع عن ذلك.", + "folderRemovedStale": "لم يعد هذا المجلد موجودًا على الخادم؛ تم تحديث القائمة.", + "deleteFolderTitle": "حذف المجلد", + "uploadToFolderTitle": "رفع ملف إلى هذا المجلد", + "newFolderButton": "مجلد جديد", + "newFolderTitle": "مجلد جديد", + "newFolderLocation": "الموقع", + "newFolderNameLabel": "اسم المجلد", + "newFolderNamePlaceholder": "الاسم فقط، دون شرطات مائلة", + "mkdirOk": "تم إنشاء المجلد", + "mkdirExists": "يوجد ملف أو مجلد بهذا الاسم بالفعل", + "mkdirInvalidName": "اسم غير صالح: لا يمكن أن يكون فارغًا أو يحتوي على / أو \\، أو أن يكون . أو ..", + "colSubPath": "المجلد الفرعي", + "folderRoot": "(الجذر)", + "groupCount": "{{count}} ملف", + "convManual": "رفع يدوي", + "convNew": "محادثة جديدة", + "colDate": "التاريخ", + "colConversation": "المحادثة", + "colName": "الاسم", + "colSize": "الحجم", + "colModified": "تاريخ التعديل", + "colActions": "الإجراءات", + "copyPath": "نسخ المسار", + "copyPathTitle": "نسخ المسار المطلق على الخادم؛ الصقه في المحادثة للإشارة إلى هذا الملف", + "pathCopied": "تم نسخ المسار — الصقه في المحادثة", + "uploadOkHint": "تم الرفع. استخدم «نسخ المسار» لنسخ المسار المطلق.", + "uploadingFile": "جارٍ رفع {{name}} · {{percent}}%", + "moreActions": "المزيد: فتح المحادثة، تعديل، إعادة تسمية، حذف", + "download": "تنزيل", + "edit": "تعديل", + "rename": "إعادة تسمية", + "openChat": "فتح المحادثة", + "confirmDelete": "حذف هذا الملف؟", + "editTitle": "تعديل الملف", + "renameTitle": "إعادة تسمية", + "renameCurrentFile": "الملف الحالي", + "newFileName": "اسم الملف الجديد", + "empty": "لا توجد مرفوعات محادثة بعد", + "errorLoad": "فشل التحميل", + "editBinaryHint": "لا يمكن تعديل الملفات الثنائية (الصور، الأرشيفات، وغيرها) كنص هنا. استخدم «تنزيل» وافتحها محليًا.", + "editUnavailable": "غير متاح", + "editTooLarge": "يتجاوز الملف 2MB ولا يمكن تعديله هنا. نزّله وعدّله محليًا.", + "errorGeneric": "حدث خطأ ما. يُرجى المحاولة مرة أخرى." + }, + "vulnerabilityPage": { + "alertTitle": "تنبيهات الثغرات عبر الروبوت", + "alertDescription": "إرسال الثغرات المكتشفة حديثًا إلى الروبوتات المرتبطة بهذا الحساب، مع التصفية حسب الخطورة.", + "alertHint": "إرسال الثغرات المكتشفة حديثًا التي تبلغ هذه الخطورة أو أعلى إلى حسابات الروبوت المرتبطة بك.", + "alertConfiguredNotBound": "{{platforms}} مفعّل، لكن حساب الويب هذا غير مرتبط بهوية مستلم.", + "alertNoBinding": "لا يوجد روبوت استباقي مفعّل ومرتبط في آنٍ واحد. لا تزال إعداداتك محفوظة.", + "alertBindAction": "ربط حساب المستلم", + "alertMinimum": "الحد الأدنى للخطورة", + "alertEnabled": "تفعيل التنبيهات", + "alertSaved": "تم حفظ إعدادات تنبيهات الثغرات", + "alertSaveFailed": "فشل حفظ إعدادات تنبيهات الثغرات", + "statTotal": "الإجمالي", + "statClickAll": "عرض الكل (مسح تصفية الخطورة)", + "statClickFilter": "انقر للتصفية حسب هذه الخطورة؛ وانقر مرة أخرى للمسح", + "advancedFilters": "عوامل تصفية متقدمة", + "moreFilters": "المزيد من عوامل التصفية", + "applyFilters": "تطبيق", + "clearAdvanced": "مسح", + "clearAll": "إعادة تعيين الكل", + "activeFilters": "عوامل التصفية النشطة", + "chipRemove": "إزالة", + "filter": "تصفية", + "clear": "مسح", + "vulnId": "معرّف الثغرة", + "conversationId": "معرّف المحادثة", + "taskOrQueueId": "معرّف المهمّة / قائمة الانتظار", + "filterTaskOrQueue": "التصفية حسب معرّف المهمّة أو قائمة الانتظار", + "conversationTag": "وسم المحادثة", + "filterConversationTag": "التصفية حسب وسم المحادثة", + "taskTag": "وسم المهمّة", + "filterTaskTag": "التصفية حسب وسم المهمّة", + "severity": "الخطورة", + "status": "الحالة", + "statusOpen": "مفتوحة", + "statusConfirmed": "مؤكّدة", + "statusFixed": "مُصلَحة", + "statusFalsePositive": "إيجابية كاذبة", + "statusIgnored": "متجاهَلة", + "statusChangeLabel": "تغيير الحالة", + "statusUpdateFailed": "فشل تحديث الحالة", + "searchVulnId": "البحث بمعرّف الثغرة", + "searchKeyword": "البحث في العنوان والوصف والنوع والهدف…", + "searchKeywordShort": "كلمة مفتاحية", + "filterExactId": "معرّف الثغرة المطابق تمامًا", + "filterEnterHint": "اضغط Enter للتصفية", + "filterConversation": "التصفية حسب المحادثة", + "loading": "جارٍ التحميل...", + "loadListFailed": "فشل التحميل", + "noRecords": "لا توجد سجلات ثغرات", + "batchExport": "تصدير جماعي", + "batchDelete": "حذف جماعي", + "batchDeleteNoResults": "لا توجد ثغرات تطابق عوامل التصفية الحالية لحذفها", + "batchDeleteConfirm": "حذف {{count}} من سجلات الثغرات المطابقة لعوامل التصفية الحالية؟ لا يمكن التراجع عن ذلك.", + "batchDeleteConfirmAll": "لم يتم تعيين أي عوامل تصفية. سيؤدي هذا إلى حذف جميع سجلات الثغرات البالغ عددها {{count}}. لا يمكن التراجع عن ذلك. هل تريد المتابعة؟", + "batchDeleteSuccess": "تم حذف {{count}} من سجلات الثغرات بنجاح", + "batchDeleteFailed": "فشل الحذف الجماعي", + "downloadMarkdownTitle": "تنزيل Markdown", + "exportNoResults": "لا توجد ثغرات تطابق عوامل التصفية الحالية", + "exportStarted": "بدأ تنزيل {{count}} ملف", + "exportFailed": "فشل التصدير", + "saveRequiredFields": "يُرجى ملء العنوان والوصف والخطورة والنوع والهدف وخطوات إعادة الإنتاج والدليل/POC والأثر والمعالجة", + "saveFailed": "فشل الحفظ", + "fetchFailed": "فشل جلب الثغرة", + "deleteFailed": "فشل الحذف", + "detailVulnId": "معرّف الثغرة", + "detailType": "النوع", + "detailTarget": "الهدف", + "detailProject": "المشروع", + "projectUnbound": "بلا مشروع", + "allProjects": "جميع المشاريع", + "filterByProject": "التصفية حسب المشروع", + "projectBindHint": "بعد الربط، يمكن للوكلاء إدراج هذه النتيجة ضمن نطاق المشروع.", + "projectBindFailed": "فشل تحديث ربط المشروع", + "projectBindOk": "تم تحديث ربط المشروع", + "detailConversationId": "معرّف المحادثة", + "openConversation": "عرض المحادثة", + "detailTaskId": "معرّف المهمّة", + "detailTaskQueueId": "معرّف قائمة انتظار المهامّ", + "detailConversationTag": "وسم المحادثة", + "detailTaskTag": "وسم المهمّة", + "detailPreconditions": "الشروط المسبقة", + "detailReproductionSteps": "خطوات إعادة الإنتاج", + "detailEvidence": "الدليل / POC", + "detailImpact": "الأثر", + "detailRecommendation": "المعالجة", + "detailRetestNotes": "طريقة إعادة الاختبار", + "detailRetestLog": "سجلّ إعادة الاختبار", + "downloadOkTitle": "تم التنزيل", + "exportFailedMessage": "فشل التصدير", + "downloadFailed": "فشل التنزيل" + }, + "tasksPage": { + "statusFilter": "تصفية الحالة", + "statusPending": "معلّقة", + "statusPaused": "متوقّفة مؤقّتًا", + "statusCancelled": "ملغاة", + "searchQueuePlaceholder": "ابحث عن معرّف قائمة الانتظار أو العنوان أو وقت الإنشاء", + "searchKeywordPlaceholder": "أدخل كلمة مفتاحية..." + }, + "skillsPage": { + "clearStats": "مسح الإحصاءات", + "clearStatsTitle": "مسح جميع الإحصاءات", + "skillsCallStats": "إحصاءات استدعاء المهارات", + "searchPlaceholder": "البحث في المهارات...", + "overviewTotal": "المهارات", + "overviewCurrent": "هذه الصفحة", + "overviewFiles": "الملفّات", + "overviewScripts": "البرامج النصّية", + "loading": "جارٍ التحميل...", + "paginationShow": "عرض {{start}}-{{end}} من {{total}}", + "perPageLabel": "لكلّ صفحة", + "firstPage": "الأولى", + "prevPage": "السابقة", + "pageOf": "الصفحة {{current}} / {{total}}", + "nextPage": "التالية", + "lastPage": "الأخيرة" + }, + "agentsPage": { + "title": "إدارة الوكلاء", + "create": "وكيل جديد", + "hint": "الوكلاء هم ملفات .md ضمن agents_dir (المقدّمة + المحتوى بوصفه موجّه النظام). المنسّق هو منسّق Deep ولا يُدرج كوكيل فرعي للمهامّ.", + "dirLabel": "الدليل", + "loading": "جارٍ التحميل...", + "empty": "لا يوجد وكلاء فرعيون بصيغة Markdown بعد. انقر \"وكيل جديد\" لإنشاء واحد.", + "noDesc": "لا يوجد وصف", + "loadFailed": "فشل تحميل القائمة", + "loadOneFailed": "فشل تحميل الوكيل", + "createTitle": "وكيل جديد", + "editTitle": "تعديل الوكيل", + "filename": "اسم الملف (.md)", + "filenamePlaceholder": "مثال: code-reviewer.md", + "fieldRole": "النوع", + "roleSub": "وكيل فرعي", + "roleOrchestrator": "منسّق (Deep)", + "roleHint": "المنسّقون مخصّصون حسب الوضع: Deep ← orchestrator.md (أو ملف .md واحد مع kind: orchestrator)؛ plan_execute ← orchestrator-plan-execute.md؛ supervisor ← orchestrator-supervisor.md. واحد على الأكثر من كل نوع. المحتوى الفارغ يعود إلى multi_agent.orchestrator_instruction / orchestrator_instruction_plan_execute / orchestrator_instruction_supervisor أو الإعدادات الافتراضية المضمّنة (PE/SV لا يعيدان استخدام orchestrator_instruction الخاص بـ Deep).", + "badgeOrchestrator": "منسّق", + "badgeSub": "وكيل فرعي", + "filenameInvalid": "يجب أن ينتهي اسم الملف بـ .md وأن يستخدم الأحرف والأرقام و ._- فقط", + "fieldId": "معرّف الوكيل (اختياري؛ يُشتقّ من الاسم إذا تُرك فارغًا)", + "fieldName": "الاسم المعروض", + "namePlaceholder": "Code Reviewer", + "fieldDesc": "الوصف", + "descPlaceholder": "متى ينبغي للمنسّق أن يفوّض المهمّة إلى هذا الوكيل", + "fieldTools": "الأدوات (مفصولة بفواصل؛ نفس مفاتيح أدوات الدور)", + "fieldBindRole": "ربط دور (اختياري)", + "fieldMaxIter": "الحد الأقصى للتكرارات (0 = استخدام الإعدادات ← agent.max_iterations)", + "fieldInstruction": "موجّه النظام (محتوى Markdown)", + "instructionPlaceholder": "أنت وكيل متخصّص...", + "nameRequired": "الاسم المعروض مطلوب", + "instructionRequired": "محتوى موجّه النظام مطلوب", + "saveOk": "تم الحفظ", + "createOk": "تم الإنشاء", + "saveFailed": "فشل الحفظ", + "deleteConfirm": "حذف {{name}}؟", + "deleteOk": "تم الحذف", + "deleteFailed": "فشل الحذف" + }, + "settingsBasic": { + "basicTitle": "الإعدادات الأساسية", + "openaiConfig": "إعدادات قناة الذكاء الاصطناعي", + "aiChannelCurrent": "القناة الحالية", + "aiChannelSave": "حفظ التغييرات", + "aiChannelSetDefault": "تعيين كافتراضي", + "aiChannelNew": "جديدة", + "aiChannelCopy": "نسخ", + "aiChannelDelete": "حذف", + "aiChannelHint": "استخدم القائمة المنسدلة للتبديل بين القنوات المحفوظة. يكتب الحفظ إلى ai.channels؛ وتُستخدم القناة الافتراضية في المحادثات والمهامّ الجديدة التي ليس لها قناة محدَّدة.", + "aiChannelSavedList": "القنوات المحفوظة", + "aiChannelListAria": "قائمة قنوات الذكاء الاصطناعي", + "aiChannelEditing": "جارٍ التعديل", + "aiChannelFormContext": "تعديل القناة المحدَّدة", + "aiChannelFormContextHint": "يؤدّي حفظ النموذج إلى تحديث إعدادات هذه القناة.", + "aiChannelBulkProbe": "فحص الكل", + "aiChannelBulkProbeTitle": "التحقّق مما إذا كانت جميع القنوات المكتملة متاحة", + "aiChannelSelectAria": "تحديد {name}", + "aiChannelDefaultBadge": "افتراضية", + "aiChannelReady": "جاهزة", + "aiChannelReadyWithLatency": "جاهزة{latency}", + "aiChannelComplete": "مكتملة", + "aiChannelDraft": "غير مكتملة", + "aiChannelDefaultMeta": "القناة الافتراضية", + "aiChannelCustomMeta": "قناة مخصَّصة", + "aiChannelOpenAICompat": "متوافقة مع OpenAI", + "aiChannelModelMissing": "النموذج مفقود", + "aiChannelName": "اسم القناة", + "aiChannelConnectionSection": "الاتصال", + "aiChannelConnectionHint": "أكّد المزوّد ونقطة النهاية والمفتاح والنموذج أولاً. يستخدم اختبار الاتصال هذه القيم.", + "aiChannelModelSection": "النموذج والحدود", + "aiChannelModelHint": "يتحكّم اسم النموذج في التوجيه. وتتحكّم حدود الرموز (Tokens) في السياق ومخرجات الاستجابة الواحدة.", + "aiChannelLimitsSection": "الحدود", + "aiChannelLimitsHint": "تتحكّم حدود الرموز (Tokens) في نافذة السياق ومخرجات الاستجابة الواحدة.", + "aiChannelUntitled": "قناة جديدة", + "aiChannelDeleteConfirm": "هل تريد حذف قناة الذكاء الاصطناعي \"{name}\"؟", + "aiChannelSaved": "تم حفظ القناة", + "aiChannelDefaultSaved": "تم حفظ القناة الافتراضية", + "aiChannelCount": "تم حفظ {count} قناة", + "aiChannelSaving": "جارٍ حفظ القناة...", + "aiChannelNewUnsaved": "لم يتمّ حفظ القناة الجديدة بعد. املأ بياناتها ثمّ انقر على \"حفظ التغييرات\".", + "aiChannelCopyUnsaved": "لم يتمّ حفظ القناة المنسوخة بعد. راجعها ثمّ انقر على \"حفظ التغييرات\".", + "aiChannelDeleted": "تم حذف القناة", + "aiChannelProbeNoComplete": "لا توجد قناة مكتملة لفحصها. املأ Base URL وAPI Key والنموذج أولاً.", + "aiChannelProbing": "جارٍ فحص {count} قناة...", + "aiChannelProbeDone": "اكتمل الفحص: {ok}/{total} جاهزة", + "apiProvider": "مزوّد API", + "providerOpenAI": "OpenAI / واجهة API متوافقة مع OpenAI", + "providerClaude": "Claude (واجهة Anthropic Messages API)", + "visionProviderReuseOpenAI": "إعادة استخدام قناة الذكاء الاصطناعي الافتراضية", + "fofaConfig": "إعدادات FOFA", + "fofaConfigHint": "تُستخدم لاكتشاف الأصول واستيرادها؛ ويلزم مفتاح API فقط.", + "agentConfig": "إعدادات الوكيل", + "knowledgeConfig": "إعدادات قاعدة المعرفة", + "baseUrl": "Base URL", + "apiKey": "API Key", + "model": "النموذج", + "openaiBaseUrlPlaceholder": "https://api.openai.com/v1", + "openaiApiKeyPlaceholder": "أدخل مفتاح OpenAI API Key", + "modelPlaceholder": "gpt-4", + "fetchModels": "جلب القائمة", + "modelsListFetching": "جارٍ جلب قائمة النماذج...", + "modelsListSelectPlaceholder": "اختر نموذجًا", + "modelsListSuccess": "تم تحميل {count} نموذج — استخدم القائمة المنسدلة على اليمين، أو اكتب في حقل الإدخال", + "modelsListFailed": "فشل جلب قائمة النماذج", + "modelsListNeedApiKey": "يُرجى إدخال API Key أولاً", + "modelsListClaudeHint": "لا يدعم Claude جلب قائمة النماذج تلقائيًّا؛ أدخل اسم النموذج يدويًّا", + "maxTotalTokens": "أقصى عدد لرموز السياق", + "maxTotalTokensPlaceholder": "120000", + "maxTotalTokensHint": "مشترك بين ضغط الذاكرة وبناء سلسلة الهجوم. الافتراضي: 120000", + "maxCompletionTokens": "أقصى عدد لرموز المخرجات", + "maxCompletionTokensPlaceholder": "32768", + "maxCompletionTokensHint": "أقصى عدد للرموز (Tokens) في استجابة واحدة للنموذج. الافتراضي: 32768", + "openaiReasoningTitle": "إعدادات الاستدلال", + "openaiReasoningHint": "إعدادات الاستدلال الافتراضية لقناة الذكاء الاصطناعي هذه؛ ويمكن لإعدادات جلسة المحادثة تجاوزها.", + "openaiReasoningProfile": "ملف تعريف الإرسال", + "openaiReasoningAllowClient": "السماح لصفحة المحادثة بتجاوز خيارات الاستدلال", + "fofaBaseUrlPlaceholder": "https://fofa.info/api/v1/search/all (اختياري)", + "fofaBaseUrlHint": "اترك الحقل فارغًا لاستخدام الافتراضي.", + "fofaApiKeyPlaceholder": "أدخل مفتاح FOFA API Key", + "fofaApiKeyHint": "يُخزَّن في إعدادات الخادم (config.yaml) فقط.", + "maxIterations": "أقصى عدد للتكرارات", + "iterationsPlaceholder": "30", + "toolWaitTimeoutSeconds": "ثوانٍ انتظار الأداة", + "toolWaitTimeoutSecondsHint": "حدّ الانتظار لكلّ دورة للأدوات. عند بلوغه، يتلقّى الوكيل execution_id بينما يستمرّ العامل في العمل؛ استخدم wait_tool_execution لمواصلة الانتظار. القيمة 0 تنتظر حتى الاكتمال.", + "externalMcpConcurrencyServer": "حدّ التزامن لكلّ MCP", + "externalMcpConcurrencyServerHint": "عدد عمليات تشغيل الأدوات المتزامنة لكلّ خادم MCP خارجي. القيمة 0 تستخدم الافتراضي 2؛ والقيمة -1 تُعطّل الحدّ.", + "externalMcpConcurrencyTotal": "التزامن الإجمالي لـ MCP الخارجي", + "externalMcpConcurrencyTotalHint": "إجمالي عمليات تشغيل أدوات MCP الخارجية المتزامنة. القيمة 0 تستخدم الافتراضي 16؛ والقيمة -1 تُعطّل الحدّ.", + "externalMcpCircuitThreshold": "عتبة أعطال دائرة MCP", + "externalMcpCircuitThresholdHint": "تفتح الدائرة مؤقّتًا بعد هذا العدد من حالات الفشل المتتالية لخادم MCP واحد. القيمة 0 تستخدم الافتراضي 3؛ والقيمة -1 تُعطّل قطع الدائرة.", + "externalMcpCircuitCooldown": "ثوانٍ تهدئة دائرة MCP", + "externalMcpCircuitCooldownHint": "المدّة التي يبقى فيها الخادم في حالة فتح الدائرة. القيمة 0 تستخدم الافتراضي 60 ثانية.", + "enableMultiAgent": "تفعيل تعدّد الوكلاء بـ Eino", + "enableMultiAgentHint": "بعد التفعيل، يمكن لصفحة المحادثة استخدام وضع تعدّد الوكلاء؛ وتُضبط الوكلاء الفرعيون في multi_agent.sub_agents أو في الدليل agents/. ويُضبط التنسيق أدناه.", + "multiAgentOrchestration": "تنسيق تعدّد الوكلاء", + "multiAgentOrchestrationHint": "deep = DeepAgent + مهمّة؛ plan_execute = التخطيط / التنفيذ / إعادة التخطيط؛ supervisor = توجيه الخبراء (النقل، سيناريوهات محدَّدة). يسري المفعول بعد الحفظ والتطبيق.", + "multiAgentOrchDeep": "deep — DeepAgent (وكلاء فرعيون للمهامّ)", + "multiAgentOrchPlanExecute": "plan_execute — التخطيط / التنفيذ / إعادة التخطيط", + "multiAgentOrchSupervisor": "supervisor — توجيه الخبراء (النقل)", + "multiAgentPeLoop": "حدّ الحلقة الخارجية لـ plan_execute", + "multiAgentPeLoopPlaceholder": "القيمة 0 تستخدم افتراضي Eino (10)", + "multiAgentPeLoopHint": "خاصّ بـ plan_execute فقط؛ أقصى عدد لدورات التنفيذ↔إعادة التخطيط.", + "multiAgentRobotMode": "وضع المحادثة الافتراضي للبوتات", + "multiAgentRobotModeHint": "وضع التنفيذ لرسائل بوتات WeCom / DingTalk / Lark. تتطلّب أوضاع Deep / Plan-Execute / Supervisor تفعيل تعدّد الوكلاء.", + "agentAdvanced": "الإعدادات المتقدّمة", + "einoModelRetryMaxRetries": "حدّ إعادة محاولة نموذج Eino الأصلي", + "einoModelRetryMaxRetriesHint": "عدد محاولات ModelRetry الأصلية في ChatModelAgent. القيمة 0 تستخدم الافتراضي الخلفي 4. يشمل 429/5xx/اضطراب الشبكة ومخرجات النموذج الفارغة.", + "einoModelRetryMaxBackoffSec": "أقصى ثوانٍ للتراجع في نموذج Eino الأصلي", + "einoModelRetryMaxBackoffSecHint": "الحدّ الأعلى للتراجع الأسّي في إعادة محاولة النموذج الأصلية. القيمة 0 تستخدم الافتراضي الخلفي 30 ثانية.", + "einoModelFailoverChannels": "قنوات تجاوز الفشل لنموذج Eino", + "einoModelFailoverChannelsPlaceholder": "backup-openai\nbackup-claude", + "einoModelFailoverChannelsHint": "بعد استنفاد القناة الأساسية لمحاولاتها الأصلية، يتمّ التبديل عبر معرّفات ai.channels هذه بالترتيب. معرّف واحد في كلّ سطر؛ وتُقبل الفواصل أيضًا.", + "einoModelFailoverMaxRetries": "حدّ تبديل تجاوز الفشل لنموذج Eino", + "einoModelFailoverMaxRetriesHint": "القيمة 0 تجرّب جميع القنوات الاحتياطية المُعدّة. والقيمة الموجبة تحدّ عدد القنوات الاحتياطية التي يمكن تجربتها.", + "userLedgerMaxRunes": "الحدّ الإجمالي لأحرف سجلّ إدخال المستخدم", + "userLedgerMaxRunesHint": "الحدّ الإجمالي لسجلّ إدخال المستخدم الأصلي المُدرَج بعد الضغط؛ والقيمة 0 تستخدم الافتراضي الخلفي. لا تُقتطع الرسائل الأصلية المخزَّنة في قاعدة البيانات.", + "userLedgerEntryMaxRunes": "حدّ أحرف إدخال المستخدم لكلّ رسالة", + "userLedgerEntryMaxRunesHint": "الحدّ المرئي للنموذج لكلّ رسالة مستخدم في السجلّ؛ والقيمة 0 تستخدم الافتراضي الخلفي ولا تؤثّر في الأصول المخزَّنة في قاعدة البيانات.", + "latestUserMaxRunes": "حدّ أحرف أحدث إدخال للمستخدم", + "latestUserMaxRunesHint": "حدّ وقت التشغيل لرسالة المستخدم الحالية المُرسَلة إلى النموذج؛ ويُحفظ الإدخال المفرط في الحجم وتُدرَج معاينة منه فقط. القيمة 0 تستخدم الافتراضي الخلفي.", + "latestUserHeadRunes": "معاينة بداية أحدث إدخال للمستخدم", + "latestUserHeadRunesHint": "عدد أحرف البداية التي تبقى مرئية للنموذج في إدخال المستخدم الحالي المفرط في الحجم.", + "latestUserTailRunes": "معاينة نهاية أحدث إدخال للمستخدم", + "latestUserTailRunesHint": "عدد أحرف النهاية التي تبقى مرئية للنموذج في إدخال المستخدم الحالي المفرط في الحجم.", + "multiAgentBatchUse": "استخدام تعدّد الوكلاء في قوائم انتظار المهامّ المجمّعة", + "multiAgentBatchUseHint": "عند التفعيل، تُنفَّذ كلّ مهمّة فرعية عبر قائمة الانتظار في إدارة المهامّ من خلال Eino DeepAgent (يتطلّب تعدّد الوكلاء).", + "enableKnowledge": "تفعيل استرجاع المعرفة", + "knowledgeBasePath": "مسار قاعدة المعرفة", + "knowledgeBasePathPlaceholder": "knowledge_base", + "knowledgeBasePathHint": "نسبةً إلى دليل ملفّ الإعدادات", + "embeddingConfig": "إعدادات التضمين", + "provider": "المزوّد", + "embeddingBaseUrlPlaceholder": "اتركه فارغًا لاستخدام base_url الخاصّ بـ OpenAI", + "embeddingApiKeyPlaceholder": "اتركه فارغًا لاستخدام api_key الخاصّ بـ OpenAI", + "modelName": "اسم النموذج", + "embeddingModelPlaceholder": "text-embedding-v4", + "retrievalConfig": "إعدادات الاسترجاع", + "topK": "نتائج Top-K", + "topKPlaceholder": "5", + "topKHint": "عدد نتائج Top-K المطلوب إرجاعها", + "similarityThreshold": "عتبة التشابه", + "similarityPlaceholder": "0.7", + "similarityHint": "تُصفّى النتائج التي تقلّ عن هذه القيمة (0-1)", + "subIndexFilter": "مصفّي الفهرس الفرعي (اختياري)", + "subIndexFilterPlaceholder": "مثال: prod، يجب أن يطابق وسم sub_indexes للفهرسة", + "subIndexFilterHint": "الفراغ = بلا تصفية. عند التعيين، تظهر فقط الصفوف التي يحتوي sub_indexes فيها على هذا الوسم (ما زالت الصفوف القديمة ذات sub_indexes الفارغ مطابِقة).", + "ragPipelineHeader": "مسار RAG (MultiQuery + Rerank)", + "ragPipelineHint": "تكون MultiQuery وإعادة الترتيب مفعَّلتين دائمًا: إعادة صياغة الاستعلام بواسطة LLM ← جلب مسبق ودمج متّجهي ← إعادة ترتيب عبر HTTP ← إزالة التكرار واقتطاع الميزانية.", + "multiQueryMaxQueries": "حدّ تنويعات إعادة صياغة MultiQuery", + "multiQueryMaxQueriesPlaceholder": "4", + "multiQueryMaxQueriesHint": "أقصى عدد لتنويعات الاسترجاع المُولَّدة بواسطة LLM (بما في ذلك إعادة صياغة الاستعلام الأصلي). يُوصى بـ 3–4، والحدّ الأقصى 8.", + "rerankProvider": "مزوّد إعادة الترتيب", + "rerankProviderAuto": "تلقائي (الاستدلال من Base URL)", + "rerankProviderCohere": "واجهة API متوافقة مع Cohere", + "rerankProviderHint": "يستخدم DashScope نموذج gte-rerank؛ وتستخدم نقاط النهاية المتوافقة الأخرى /v1/rerank. اتركه فارغًا للاستدلال من Base URL أدناه.", + "rerankModel": "نموذج إعادة الترتيب (اختياري)", + "rerankModelPlaceholder": "فارغ: DashScope→gte-rerank، Cohere→rerank-multilingual-v3.0", + "rerankBaseUrl": "Base URL لإعادة الترتيب (اختياري)", + "rerankBaseUrlPlaceholder": "اتركه فارغًا لإعادة استخدام base_url الخاصّ بالتضمين / OpenAI", + "rerankApiKey": "API Key لإعادة الترتيب (اختياري)", + "rerankApiKeyPlaceholder": "اتركه فارغًا لإعادة استخدام api_key الخاصّ بالتضمين / OpenAI", + "rerankApiKeyHint": "عند فشل إعادة الترتيب، تعود النتائج إلى ترتيب الدمج؛ ويستمرّ البحث في العمل.", + "postRetrieveHeader": "ما بعد الاسترجاع (إزالة التكرار / الميزانية)", + "postRetrieveDedupeAuto": "تُزال تكرارات النتائج دائمًا حسب النصّ المُطبَّع (المتون بعد طيّ المسافات البيضاء). لا يلزم أيّ إعداد.", + "prefetchTopK": "المرشّحات المجلوبة مسبقًا (مرحلة المتّجهات)", + "prefetchTopKPlaceholder": "20", + "prefetchTopKHint": "عدد المرشّحات المتّجهية لكلّ تنويعة MultiQuery؛ والقيمة 0 تستخدم القيمة المدمجة max(top_k×4, 20) (بحدّ أقصى 200).", + "maxContextChars": "أقصى عدد للأحرف المُرجَعة (Unicode)", + "maxContextCharsPlaceholder": "0", + "maxContextCharsHint": "0 = بلا حدّ؛ تُبقي المقاطع كاملةً بترتيب الرتبة حتى تجاوز الميزانية.", + "maxContextTokens": "أقصى عدد للرموز المُرجَعة", + "maxContextTokensPlaceholder": "0", + "maxContextTokensHint": "0 = بلا حدّ؛ تقدير tiktoken (اسم نموذج التضمين، والاحتياطي cl100k_base).", + "indexConfig": "إعدادات الفهرس", + "chunkStrategy": "استراتيجية التقطيع", + "chunkStrategyMarkdownRecursive": "عناوين Markdown، ثم التقطيع التعاودي (مُوصى به)", + "chunkStrategyRecursive": "التقطيع التعاودي فقط", + "chunkStrategyHint": "يطابق مسارات نمط Eino: عناوين Markdown + التعاودي للمستندات؛ ويمكن للنصّ العادي استخدام التعاودي فقط.", + "requestTimeoutSeconds": "مهلة HTTP للتضمين (بالثواني)", + "requestTimeoutPlaceholder": "120", + "requestTimeoutHint": "القيمة 0 تستخدم المهلة الافتراضية 120 ثانية لعميل HTTP الخاصّ بالتضمين.", + "batchSize": "حجم دفعة التضمين", + "batchSizePlaceholder": "64", + "batchSizeHint": "أقصى عدد للنصوص في كلّ طلب تضمين (يجمّع مفهرس SQLite عمليات الكتابة وفقًا لذلك).", + "preferSourceFile": "تفضيل الملفّ المصدري على القرص عند الفهرسة (Eino FileLoader)", + "preferSourceFileHint": "عند التفعيل، يأتي المحتوى من file_path؛ ويعود إلى محتوى قاعدة البيانات إذا فشل التحميل.", + "subIndexes": "فهارس Eino الفرعية (مفصولة بفواصل)", + "subIndexesPlaceholder": "مثال: prod، knowledge", + "subIndexesHint": "تُمرَّر إلى indexer.WithSubIndexes؛ وتُخزَّن في العمود sub_indexes.", + "chunkSize": "حجم المقطع", + "chunkSizePlaceholder": "512", + "chunkSizeHint": "أقصى عدد للرموز (Tokens) في كلّ مقطع (الافتراضي 512)", + "chunkOverlap": "تداخل المقاطع", + "chunkOverlapPlaceholder": "50", + "chunkOverlapHint": "عدد الرموز (Tokens) المتداخلة بين المقاطع (الافتراضي 50)", + "maxChunksPerItem": "أقصى عدد للمقاطع لكلّ عنصر", + "maxChunksPlaceholder": "0", + "maxChunksHint": "أقصى عدد للمقاطع لكلّ عنصر معرفة (0 = بلا حدّ)", + "maxRpm": "أقصى عدد للطلبات في الدقيقة", + "maxRpmPlaceholder": "0", + "maxRpmHint": "أقصى عدد للطلبات في الدقيقة (0 = بلا حدّ)", + "rateLimitDelay": "تأخير تحديد المعدّل (مللي ثانية)", + "rateLimitPlaceholder": "300", + "rateLimitHint": "التأخير بين الطلبات (مللي ثانية)؛ 0 = بلا حدّ", + "maxRetries": "أقصى عدد لإعادة المحاولة", + "maxRetriesPlaceholder": "3", + "maxRetriesHint": "عدد إعادة المحاولة عند تحديد المعدّل أو خطأ الخادم", + "retryDelay": "تأخير إعادة المحاولة (مللي ثانية)", + "retryDelayPlaceholder": "1000", + "retryDelayHint": "التأخير بين محاولات إعادة المحاولة (مللي ثانية)", + "visionConfig": "تحليل الرؤية (analyze_image)", + "visionEnabled": "تفعيل أداة الرؤية analyze_image", + "visionEnabledHint": "تُسجَّل أداة MCP عند التفعيل؛ وتُرسَل الصور لاستدعاء VL واحد فقط؛ ويُبقي سياق الوكيل ملخّصات نصّية فقط. احفظ وطبّق ليسري المفعول.", + "visionBaseUrlPlaceholder": "اتركه فارغًا لإعادة استخدام Base URL الخاصّ بـ OpenAI", + "visionApiKeyPlaceholder": "اتركه فارغًا لإعادة استخدام API Key الخاصّ بـ OpenAI", + "visionModel": "نموذج الرؤية", + "visionModelPlaceholder": "qwen-vl-max", + "visionModelRequired": "اسم نموذج الرؤية مطلوب عند تفعيل الرؤية", + "visionAdvanced": "متقدّم: المعالجة المسبقة والحدود", + "visionMaxImageBytes": "أقصى حجم للملفّ الأصلي (بايت)", + "visionMaxDimension": "أقصى عدد لبكسلات الحافة الطويلة", + "visionJpegQuality": "جودة JPEG", + "visionMaxPayloadBytes": "أقصى حمولة لواجهة API (بايت)", + "visionSkipPreprocessBytes": "التمرير المباشر دون هذا الحدّ (بايت)", + "visionSkipPreprocessHint": "0 = ضغط JPEG دائمًا؛ ويجب أيضًا أن يكون ضمن حدود الحافة الطويلة والحمولة.", + "visionDetail": "تفاصيل الصورة", + "visionTimeout": "المهلة (بالثواني)", + "visionTestFillRequired": "أدخل نموذج الرؤية وتأكّد من توفّر API Key (أو أعد استخدام OpenAI)", + "testConnection": "اختبار الاتصال", + "testFillRequired": "يُرجى ملء Base URL وAPI Key والنموذج أولاً", + "testing": "جارٍ اختبار الاتصال...", + "testSuccess": "نجح الاتصال", + "testFailed": "فشل الاتصال", + "testError": "خطأ في الاختبار", + "testErrorInvalidApiKey": "مفتاح API Key غير صالح أو غير مصرَّح به. تحقّق من صحّة المفتاح.", + "testErrorUnauthorized": "فشلت المصادقة. تحقّق من API Key.", + "testErrorForbidden": "رُفض الطلب. تحقّق من أذونات الحساب أو الوصول إلى النموذج.", + "testErrorModelUnavailable": "النموذج غير متاح أو اسم النموذج غير صحيح.", + "testErrorBaseUrl": "عنوان Base URL غير متاح. تحقّق من صحّة نقطة النهاية." + }, + "settingsTerminal": { + "title": "الطرفية", + "description": "نفّذ الأوامر على الخادم للتشغيل والتصحيح. تُنفَّذ الأوامر على الخادم؛ تجنّب العمليات الحسّاسة أو التدميرية.", + "terminalTab": "الطرفية {{n}}", + "welcomeLine": "طرفية CyberStrikeAI — جلسة صدفة حقيقية؛ اكتب الأوامر مباشرةً. Ctrl+L لمسح الشاشة", + "sessionClosed": "[أُغلقت الجلسة]", + "connectionError": "[خطأ في اتصال الطرفية]", + "connectFailed": "[تعذّر الاتصال بخدمة الطرفية: {{msg}}]", + "closeTabTitle": "إغلاق", + "containerClickTitle": "انقر هنا، ثم اكتب الأوامر", + "xtermNotLoaded": "فشل تحميل xterm.js. أعد تحميل الصفحة أو تحقّق من شبكتك.", + "close": "×", + "newTerminal": "+" + }, + "settingsStorage": { + "title": "تنظيف التخزين", + "subtitle": "افحص واستعد مساحة القرص التي تستهلكها عناصر وقت التشغيل (مساحات عمل الوكلاء، وفائض مخرجات الأدوات، وعناصر C2، وملفّات المحادثة المرفوعة). الحذف لا رجعة فيه - عاين أولاً.", + "diskUsed": "القرص المستخدم", + "runtimeTotal": "بصمة وقت التشغيل", + "reclaimable": "قابل للاسترداد", + "inodes": "استخدام الـ inode", + "inodesHint": "قد تستنفد عمليات الفحص الـ inodes مع أعداد هائلة من الملفّات الصغيرة", + "refresh": "إعادة الفحص", + "preview": "معاينة القابل للاسترداد", + "cleanNow": "التنظيف الآن", + "colCategory": "الفئة", + "colRoot": "الدليل", + "colUnits": "العناصر", + "colSize": "الحجم", + "colReclaimable": "قابل للاسترداد", + "colRetention": "أيّام الاحتفاظ", + "colEnabled": "مفعَّل", + "loading": "جارٍ التحميل...", + "policyTitle": "سياسة التنظيف التلقائي", + "autoClean": "تفعيل التنظيف الخلفي المجدوَل", + "intervalMinutes": "الفاصل الزمني (بالدقائق)", + "orphanGraceDays": "مهلة اليتامى (بالأيّام)", + "activeGraceHours": "حماية الجلسات النشطة (بالساعات)", + "policyHint": "الاحتفاظ بقيمة 0 يُعطّل التنظيف المبني على العمر، لكن تظلّ الأدلّة التي اختفت محادثتها بالفعل قابلة للاسترداد. ولا تُنظَّف الجلسات ذات النشاط الحديث أبدًا. التنظيف التلقائي معطَّل افتراضيًّا، لذا لا يحذف الترقية أيّ بيانات موجودة أبدًا.", + "save": "حفظ السياسة", + "loadMetaFailed": "فشل تحميل سياسة التنظيف", + "loadStatusFailed": "فشل تحميل استخدام التخزين", + "cleanupFailed": "فشل طلب التنظيف", + "unavailable": "غير متاح", + "free": "متاح", + "itemsUnit": "عناصر", + "daysUnit": "أيّام", + "noCategories": "لا توجد فئات قابلة للتنظيف", + "orphanSuffix": "بما في ذلك اليتامى", + "skippedActive": "تم تخطّي النشطة", + "defaultRetention": "افتراضي", + "unused": "غير مستخدم", + "scannedAt": "تم الفحص في", + "previewTitle": "معاينة (لم يُحذف شيء)", + "cleanDone": "اكتمل التنظيف", + "nothingToClean": "لا يوجد حاليًّا ما يطابق سياسة التنظيف.", + "skippedActiveNote": "الجلسات المتخطّاة ذات النشاط الحديث: ", + "failed": "فشلت العملية", + "confirmClean": "يحذف هذا نهائيًّا نحو {size} ({count} عنصر) من ملفّات وقت التشغيل ولا يمكن التراجع عنه. شغّل \"معاينة القابل للاسترداد\" أولاً. هل تريد المتابعة؟", + "saveFailed": "فشل الحفظ", + "saved": "تم حفظ سياسة التنظيف", + "cat.workspace": "مساحة عمل الوكيل", + "catHint.workspace": "عناصر التنزيل والفحص الخاصّة بالوكيل (tmp/workspace)، مقسَّمة لكلّ مشروع/محادثة.", + "cat.reduction": "فائض مخرجات الأدوات", + "catHint.reduction": "ملفّات الفائض المقتطعة لمخرجات الأدوات المفرطة في الحجم (tmp/reduction)؛ ملفّ واحد لكلّ تنفيذ، مُشتقّة بالكامل.", + "cat.conversation_artifacts": "عناصر المحادثة", + "catHint.conversation_artifacts": "نصوص التلخيص وسجلّ إدخال المستخدم المفرط في الحجم (data/conversation_artifacts).", + "cat.plantask": "لوحات مهامّ التخطيط", + "catHint.plantask": "لوحات التخطيط لتعدّد الوكلاء بـ Eino (skills/.eino/plantask)، مُشتقّة بالكامل.", + "cat.c2_artifacts": "عناصر C2", + "catHint.c2_artifacts": "لقطات شاشة C2 والملفّات المرفوعة والمدفوعة والحمولات المُولَّدة (tmp/c2). يؤدّي تنظيف الحمولات إلى إبطال روابط تنزيلها.", + "cat.chat_uploads": "ملفّات المحادثة المرفوعة", + "catHint.chat_uploads": "الملفّات المرفوعة في المحادثات (chat_uploads//).", + "cat.workflow_checkpoints": "نقاط تفتيش سير العمل", + "catHint.workflow_checkpoints": "نقاط تفتيش تشغيل سير العمل (data/workflow-checkpoints)، تُستخدم فقط لاستئناف عمليات التشغيل المتوقّفة.", + "cat.diagnostic_logs": "سجلّات التشخيص", + "catHint.diagnostic_logs": "سجلّات التشخيص ذات التدوير اليومي (log/diagnostic-*.log)." + }, + "settingsAudit": { + "title": "سجلّات التدقيق", + "description": "إجراءات مسؤول المنصّة (تسجيل الدخول، والإعدادات، والحذف). لا تُسجَّل محتويات المحادثة، ولا عمليات التشغيل لكلّ أمر في الطرفية/WebShell، ولا استدعاءات الأدوات الفردية.", + "filterCategory": "الفئة", + "filterAction": "الإجراء", + "filterEvent": "نوع الحدث", + "filterAllCategories": "كلّ الفئات", + "filterAllActions": "كلّ الإجراءات", + "filterCascadeHint": "اختر فئة للتصفية حسب الإجراء", + "filterResult": "النتيجة", + "pageSize": "لكلّ صفحة", + "statTotal": "الإجمالي المصفّى", + "statSuccess": "نجاح", + "statFailures": "حالات الفشل", + "statRecent7d": "آخر 7 أيّام", + "retentionHint": "تُحفظ سجلّات التدقيق لمدّة {{days}} يومًا، ثم تُمحى تلقائيًّا.", + "disabledHint": "تسجيل التدقيق معطَّل؛ ولا تُكتب الإجراءات الجديدة.", + "filterSince": "من", + "filterUntil": "إلى", + "filterTimeZone": "المنطقة الزمنية: {{tz}} (تستخدم التصفية التوقيت المحلّي لمتصفّحك)", + "datetimePlaceholder": "اختر التاريخ والوقت", + "timePresets": "نطاق سريع", + "preset15m": "آخر 15 دقيقة", + "preset1h": "آخر ساعة", + "preset24h": "آخر 24 ساعة", + "preset7d": "آخر 7 أيّام", + "presetToday": "اليوم", + "pickerHour": "الساعة", + "pickerMinute": "الدقيقة", + "pickerClear": "مسح", + "pickerToday": "اليوم", + "pickerConfirm": "موافق", + "filterQuery": "كلمة مفتاحية", + "filterQueryPlaceholder": "الرسالة / معرّف المورد / الإجراء", + "colTime": "الوقت", + "colMessage": "الرسالة", + "colCategory": "الفئة", + "colAction": "الإجراء", + "colResult": "النتيجة", + "colIp": "IP", + "colResource": "معرّف المورد", + "cat": { + "auth": "المصادقة", + "config": "الإعدادات", + "terminal": "الطرفية", + "c2": "C2", + "webshell": "WebShell", + "knowledge": "المعرفة", + "conversation": "المحادثة", + "vulnerability": "الثغرة", + "asset": "الأصل", + "externalMcp": "MCP الخارجي", + "task": "المهامّ", + "tool": "الأدوات", + "file": "الملفّات", + "hitl": "التدخّل البشري (HITL)", + "role": "الأدوار", + "skill": "المهارات", + "agent": "الوكلاء الفرعيون" + }, + "act": { + "login": "تسجيل الدخول", + "logout": "تسجيل الخروج", + "login_failed": "فشل تسجيل الدخول", + "password_change": "تغيير كلمة المرور", + "change_password": "تغيير كلمة المرور", + "apply": "تطبيق الإعدادات", + "update": "تحديث", + "exec": "تنفيذ في الطرفية", + "exec_stream": "بثّ الطرفية", + "listener_create": "إنشاء مستمِع", + "listener_delete": "حذف مستمِع", + "listener_start": "بدء مستمِع", + "listener_stop": "إيقاف مستمِع", + "session_delete": "حذف جلسة", + "task_create": "إنشاء مهمّة", + "task_cancel": "إلغاء مهمّة", + "task_delete": "حذف مهمّة", + "connection_create": "إنشاء اتصال", + "connection_delete": "حذف اتصال", + "item_delete": "حذف عنصر معرفة", + "index_build": "بناء فهرس المعرفة", + "index_rebuild_full": "إعادة بناء فهرس المعرفة بالكامل", + "delete": "حذف", + "delete_turn": "حذف دورة", + "create": "إنشاء", + "upsert": "إدراج/تحديث MCP خارجي", + "create_queue": "إنشاء قائمة انتظار مجمّعة", + "start_queue": "بدء قائمة انتظار مجمّعة", + "delete_queue": "حذف قائمة انتظار مجمّعة", + "pause_queue": "إيقاف قائمة انتظار مجمّعة مؤقّتًا", + "rerun_queue": "إعادة تشغيل قائمة انتظار مجمّعة", + "delete_batch_task": "حذف مهمّة فرعية مجمّعة", + "execution_delete": "حذف تنفيذ", + "execution_delete_batch": "حذف عمليات التنفيذ (مجمّع)", + "upload": "رفع", + "decision": "قرار التدخّل البشري", + "markdown_create": "إنشاء وكيل فرعي", + "markdown_update": "تحديث وكيل فرعي", + "markdown_delete": "حذف وكيل فرعي" + }, + "openResource": "فتح المورد المرتبط", + "openResourceChat": "فتح المورد المرتبط (محادثة)", + "resourceIdLabel": "معرّف المورد", + "resourceRemoved": "(لم يعد المورد موجودًا)", + "filterAll": "الكل", + "filterBtn": "تصفية", + "resetBtn": "إعادة تعيين", + "exportBtn": "تصدير", + "exportJson": "تصدير JSON", + "export": "تصدير JSON", + "exportCsv": "تصدير CSV", + "exportDone": "اكتمل التصدير", + "loading": "جارٍ التحميل...", + "empty": "لا توجد سجلّات تدقيق", + "result": { + "success": "نجاح", + "failure": "فشل" + }, + "msg": { + "auth": { + "login": "تم تسجيل الدخول بنجاح", + "login_failed": "فشل تسجيل الدخول: كلمة المرور غير صحيحة", + "logout": "تم تسجيل الخروج", + "change_password": "تم تغيير كلمة مرور تسجيل الدخول", + "change_password_failed": "فشل تغيير كلمة المرور: كلمة المرور الحالية غير صحيحة" + }, + "config": { + "apply": "تم تطبيق الإعدادات", + "update": "تم تحديث الإعدادات في الذاكرة", + "apply_fail_kb_init": "فشل تطبيق الإعدادات: تهيئة قاعدة المعرفة", + "apply_fail_kb_reinit": "فشل تطبيق الإعدادات: إعادة تهيئة قاعدة المعرفة", + "apply_fail_c2": "فشل تطبيق الإعدادات: C2" + }, + "conversation": { + "create": "تم إنشاء المحادثة", + "delete": "تم حذف المحادثة", + "delete_turn": "تم حذف دورة المحادثة" + }, + "c2": { + "listener_create": "تم إنشاء مستمِع C2", + "listener_delete": "تم حذف مستمِع C2", + "listener_start": "تم بدء مستمِع C2", + "listener_stop": "تم إيقاف مستمِع C2", + "session_delete": "تم حذف جلسة C2", + "task_create": "تم إنشاء مهمّة C2", + "task_cancel": "تم إلغاء مهمّة C2", + "task_delete": "تم حذف مهامّ C2 (مجمّع)" + }, + "webshell": { + "connection_create": "تم إنشاء اتصال WebShell", + "connection_delete": "تم حذف اتصال WebShell" + }, + "knowledge": { + "item_delete": "تم حذف عنصر المعرفة", + "index_build": "تم بناء فهرس المعرفة", + "index_rebuild_full": "تم إعادة بناء فهرس المعرفة بالكامل" + }, + "vulnerability": { + "create": "تم إنشاء سجلّ الثغرة", + "update": "تم تحديث سجلّ الثغرة", + "delete": "تم حذف سجلّ الثغرة", + "delete_batch": "تم حذف سجلّات الثغرات (مجمّع)" + }, + "external_mcp": { + "upsert": "تم تحديث إعدادات MCP الخارجي", + "delete": "تم حذف إعدادات MCP الخارجي" + }, + "task": { + "create_queue": "تم إنشاء قائمة انتظار المهامّ المجمّعة", + "start_queue": "تم بدء قائمة انتظار المهامّ المجمّعة", + "delete_queue": "تم حذف قائمة انتظار المهامّ المجمّعة", + "pause_queue": "تم إيقاف قائمة انتظار المهامّ المجمّعة مؤقّتًا", + "rerun_queue": "تم إعادة تشغيل قائمة انتظار المهامّ المجمّعة", + "delete_batch_task": "تم حذف المهمّة الفرعية المجمّعة" + }, + "tool": { + "execution_delete": "تم حذف سجلّ تنفيذ الأداة", + "execution_delete_batch": "تم حذف سجلّات تنفيذ الأدوات (مجمّع)" + }, + "file": { + "upload": "تم رفع مرفق المحادثة", + "delete": "تم حذف مرفق المحادثة" + }, + "hitl": { + "decision": "قرار موافقة التدخّل البشري" + }, + "role": { + "create": "تم إنشاء الدور", + "update": "تم تحديث الدور", + "delete": "تم حذف الدور" + }, + "skill": { + "create": "تم إنشاء المهارة", + "update": "تم تحديث المهارة", + "delete": "تم حذف المهارة" + }, + "agent": { + "markdown_create": "تم إنشاء وكيل فرعي بصيغة Markdown", + "markdown_update": "تم تحديث وكيل فرعي بصيغة Markdown", + "markdown_delete": "تم حذف وكيل فرعي بصيغة Markdown" + } + }, + "paginationShow": "{{start}}-{{end}} من {{total}}", + "detailTitle": "تفاصيل التدقيق", + "detailTime": "الوقت", + "detailCategory": "الفئة", + "detailResult": "النتيجة", + "detailMessage": "الرسالة", + "detailSession": "الجلسة" + }, + "settingsSecurity": { + "currentRequired": "أدخل كلمة المرور الحالية", + "newTooShort": "يجب أن تحتوي كلمة المرور الجديدة على 8 أحرف على الأقلّ", + "confirmMismatch": "كلمتا المرور الجديدتان غير متطابقتين", + "changePasswordTitle": "تغيير كلمة المرور", + "changePasswordDesc": "بعد تغيير كلمة المرور، سجّل الدخول مجدّدًا بكلمة المرور الجديدة.", + "currentPassword": "كلمة المرور الحالية", + "currentPasswordPlaceholder": "أدخل كلمة المرور الحالية", + "newPassword": "كلمة المرور الجديدة", + "newPasswordPlaceholder": "كلمة المرور الجديدة (8 أحرف على الأقلّ)", + "confirmPassword": "تأكيد كلمة المرور الجديدة", + "confirmPasswordPlaceholder": "أدخل كلمة المرور الجديدة مرّة أخرى", + "clear": "مسح", + "changePasswordBtn": "تغيير كلمة المرور" + }, + "settingsRobotsExtra": { + "botCommandsTitle": "تعليمات أوامر الروبوت", + "botCommandsEntryDesc": "اعرض أوامر الروبوت الخاصة بالهوية والتفويض والمحادثات والأدوار والأوضاع والمشاريع وتشخيصات السلامة.", + "viewAllCommands": "عرض جميع الأوامر", + "botCommandsDesc": "يمكنك إرسال الأوامر التالية في المحادثة (مع دعم الصينية والإنجليزية):", + "botCmdCategoryGeneral": "عامّ", + "botCmdCategoryIdentity": "الهوية والتفويض", + "botCmdCategoryConversation": "المحادثة", + "botCmdCategoryRole": "الدور", + "botCmdCategoryMode": "الوضع", + "botCmdCategoryDiagnostics": "التشخيصات والسلامة", + "botCmdCategoryProject": "المشروع", + "botCmdHelp": "عرض هذه المساعدة", + "botCmdIdentity": "عرض المُرسِل في المنصّة ووضع التفويض وهوية RBAC الفعّالة", + "botCmdBindUser": "ربط هوية المنصّة هذه بمستخدم RBAC (في وضع ربط المستخدم فقط)", + "botCmdUnbindUser": "إزالة ربط هوية المنصّة هذه (في وضع ربط المستخدم فقط؛ ويلزم التأكيد)", + "botAuthHint": "لا يقبل وضع حساب الخدمة أوامر الربط أو إلغاء الربط؛ ولا يُقبل سوى المُرسِلين المُدرجين في القائمة المسموح بها والمضبوطين.", + "botCmdList": "سرد المحادثات", + "botCmdSwitch": "التبديل إلى محادثة", + "botCmdNew": "بدء محادثة جديدة", + "botCmdClear": "مسح السياق", + "botCmdStatus": "عرض المحادثة والوضع والدور والمشروع الحالي", + "botCmdTask": "عرض حالة المهمّة الحالية والوقت المنقضي", + "botCmdRename": "إعادة تسمية المحادثة الحالية", + "botCmdPermissions": "عرض صلاحيات العمل الفعّالة", + "botCmdDoctor": "التحقّق من حالة الإعدادات الأساسية", + "botCmdConfirmation": "تأكيد إجراء محفوف بالمخاطر أو إلغاؤه", + "botCmdStop": "إيقاف المهمّة الجارية", + "botCmdRoles": "سرد الأدوار", + "botCmdRole": "تبديل الدور", + "botCmdModes": "سرد أوضاع المحادثة والاختيار الحالي", + "botCmdMode": "تبديل وضع المحادثة", + "botCmdDelete": "حذف المحادثة (يلزم التأكيد)", + "botCmdVersion": "عرض الإصدار", + "botCmdProjects": "سرد المشاريع", + "botCmdNewProject": "إنشاء مشروع وربط المحادثة الحالية", + "botCmdBindProject": "ربط المحادثة الحالية بمشروع", + "botCmdUnbindProject": "إلغاء ربط المشروع عن المحادثة الحالية", + "botCommandsFooter": "وإلّا، يُرسَل النصّ إلى الذكاء الاصطناعي ضمن صلاحيات RBAC الحالية للمستخدم المرتبط أو حساب الخدمة." + }, + "mcpDetailModal": { + "blockReason": "سبب الحظر", + "title": "تفاصيل استدعاء الأداة", + "execInfo": "معلومات التنفيذ", + "tool": "الأداة", + "status": "الحالة", + "statusPending": "قيد الانتظار", + "statusRunning": "قيد التشغيل", + "statusCompleted": "مكتمل", + "statusFailed": "فشل", + "statusCancelled": "ملغى", + "unknown": "غير معروف", + "getDetailFailed": "فشل جلب التفاصيل", + "runningNoResponseYet": "لا يوجد مُخرَج بعد؛ قد تكون الأداة لا تزال قيد التشغيل. إذا تعلّقت، استخدم \"إيقاف الأداة\" أدناه لإنهاء هذا الاستدعاء فقط.", + "abortTitle": "التحكّم في التنفيذ", + "abortHint": "يوقف هذا الاستدعاء للأداة فقط. تستمرّ المحادثة / المهمّة متعدّدة الخطوات (بخلاف إيقاف المهمّة بأكملها).", + "abortBtn": "إيقاف الأداة", + "abortConfirm": "هل تريد إيقاف استدعاء الأداة هذا؟ لن تُلغى المحادثة أو المهمّة التكرارية بشكل عامّ.", + "abortSuccess": "طُلب الإلغاء؛ ستُحدَّث الحالة عند عودة الأداة.", + "abortFailed": "فشل إيقاف الأداة", + "abortNoteModalTitle": "إيقاف الأداة مع ملاحظة", + "abortNoteModalHint": "اختياري: سبب إيقافك أو الطريقة التي ينبغي أن يتابع بها النموذج. يرى النموذج أيّ مُخرَج من الأداة أوّلًا، ثمّ كتلة موسومة (USER INTERRUPT NOTE — وليست مُخرَج أداة خامًا)، ثمّ نصّك. اتركه فارغًا للإيقاف البسيط.", + "abortNoteLabel": "ملاحظة (اختياري)", + "abortNotePlaceholder": "مثلًا: المُخرَج كافٍ—تخطَّ الانتظار وتابِع…", + "abortNoteSubmit": "إيقاف الأداة", + "abortNoteClose": "إلغاء", + "execSuccessNoContent": "نجح التنفيذ دون محتوى قابل للعرض.", + "time": "الوقت", + "executionId": "معرّف التنفيذ", + "requestParams": "معاملات الطلب", + "copyJson": "نسخ JSON", + "responseResult": "الاستجابة", + "copyContent": "نسخ المحتوى", + "correctInfo": "معلومات التصحيح", + "rawResponse": "الاستجابة الخام", + "errorInfo": "معلومات الخطأ", + "copyError": "نسخ الخطأ", + "contentTruncated": "… (العرض مقتطع؛ استخدم read_file على المسار في persisted-output للحصول على الملفّ الكامل)" + }, + "attackChainModal": { + "title": "سلسلة الهجوم", + "regenerate": "إعادة التوليد", + "regenerateTitle": "إعادة توليد سلسلة الهجوم (تتضمّن أحدث محادثة)", + "exportPng": "تصدير PNG", + "exportSvg": "تصدير SVG", + "refreshTitle": "تحديث سلسلة الهجوم الحالية", + "nodesEdges": "العُقد: {{nodes}} | الحوافّ: {{edges}}", + "searchPlaceholder": "البحث في العُقد…", + "allTypes": "جميع الأنواع", + "target": "الهدف", + "action": "الإجراء", + "vulnerability": "الثغرة", + "allRisks": "جميع المخاطر", + "highRisk": "عالٍ (80-100)", + "mediumHighRisk": "متوسّط-عالٍ (60-79)", + "mediumRisk": "متوسّط (40-59)", + "lowRisk": "منخفض (0-39)", + "resetFilter": "إعادة تعيين التصفية", + "loading": "جارٍ التحميل…", + "riskLevel": "مستوى الخطر", + "lineMeaning": "معنى الخطّ", + "blueLine": "أزرق: الإجراء يكتشف ثغرة", + "redLine": "أحمر: يُمكّن/يُسهم", + "grayLine": "رمادي: الترتيب المنطقي", + "nodeDetails": "تفاصيل العُقدة", + "closeDetails": "إغلاق التفاصيل", + "contextTitle": "ملخّصات السياق وأدلّة الإثبات ({{count}})", + "contextNotice": "الملخّصات المولّدة من النظام من تتبّع المحادثة الحالية قد تكون أحدث من هذا الرسم. وهي تدعم الاستذكار والاسترجاع، ولا تمنح تصريحًا جديدًا للمستخدم ولا تتحقّق من أدلّة الأدوات. استخدم مسارات اللقطات في الملخّصات لتحديد موقع الدليل الأصلي.", + "contextSummary": "ملخّص السياق {{number}}", + "contextCopy": "نسخ الملخّص الكامل", + "contextCopied": "تمّ النسخ", + "contextCopyFailed": "فشل النسخ. حدّد النصّ لنسخه يدويًا.", + "regenerateFailed": "فشلت إعادة التوليد: {{message}}" + }, + "externalMcpModal": { + "configJson": "إعداد JSON", + "formatLabel": "التنسيق:", + "formatDesc": "كائن JSON؛ المفتاح = اسم الإعداد، والقيمة = الإعداد. استخدم زرّي البدء/الإيقاف للتحكّم في الحالة. يدعم صيغة متغيّرات البيئة ${VAR} و ${VAR:-default}.", + "configExample": "مثال الإعداد:", + "stdioMode": "وضع stdio:", + "httpMode": "وضع HTTP:", + "sseMode": "وضع SSE:", + "placeholder": "{\n \"my-server\": {\n \"command\": \"python3\",\n \"args\": [\"${HOME}/mcp/server.py\"],\n \"env\": { \"API_KEY\": \"${API_KEY}\" },\n \"timeout\": 300\n }\n}", + "exampleStdio": "{\n \"my-server\": {\n \"command\": \"python3\",\n \"args\": [\"${HOME}/mcp/server.py\"],\n \"env\": { \"API_KEY\": \"${API_KEY}\", \"LOG_LEVEL\": \"${LOG_LEVEL:-INFO}\" },\n \"timeout\": 300\n }\n}", + "exampleHttp": "{\n \"remote-mcp\": {\n \"type\": \"http\",\n \"url\": \"https://mcp.example.com/mcp\",\n \"headers\": { \"Authorization\": \"Bearer ${MCP_TOKEN}\" }\n }\n}", + "exampleSse": "{\n \"sse-mcp\": {\n \"type\": \"sse\",\n \"url\": \"http://127.0.0.1:8081/mcp/sse\"\n }\n}", + "exampleDescription": "وصف المثال", + "formatJson": "تنسيق JSON", + "loadExample": "تحميل مثال" + }, + "skillModal": { + "addSkill": "إضافة مهارة", + "editSkill": "تعديل المهارة", + "skillName": "اسم المهارة", + "skillNamePlaceholder": "مثلًا sql-injection-testing", + "skillNameHint": "أحرف صغيرة وأرقام وشرطات (اسم Agent Skills)", + "description": "الوصف", + "descriptionPlaceholder": "وصف قصير", + "descriptionHint": "يُربَط بحقل description في SKILL.md YAML (عند إنشاء/تعديل SKILL.md)", + "packageFiles": "ملفّات الحزمة", + "packageFilesHint": "انقر على ملفّ لتعديله؛ المجلّدات عبارة عن تسميات فقط ولا يمكن فتحها", + "folderHint": "مجلّد (غير قابل للتعديل)", + "clickToEdit": "انقر لتعديل هذا الملفّ", + "editingFile": "جارٍ التعديل", + "newFile": "ملفّ جديد", + "newFilePlaceholder": "مسار نسبي، مثلًا FORMS.md أو scripts/extra.sh", + "newFilePathRequired": "أدخِل مسارًا للملفّ الجديد", + "newFilePathInvalid": "مسار غير صالح (بدون .. أو مسارات مطلقة)", + "noPackageFiles": "لا توجد ملفّات مدرجة", + "unsavedSwitch": "لديك تغييرات غير محفوظة. هل تريد تبديل الملفّ على أيّ حال؟", + "saveDescriptionFirst": "احفظ الوصف في SKILL.md قبل التبديل إلى ملفّ آخر", + "contentLabel": "المحتوى", + "contentPlaceholder": "عدّل الملفّ المحدَّد…", + "contentPlaceholderAdd": "جسم SKILL.md فقط (تُولَّد المقدّمة تلقائيًا)…", + "bodyHintEdit": "بالنسبة إلى SKILL.md هذا هو الجسم فقط (بدون ترويسة ---)؛ يدمج الحفظ مع name/description في SKILL.md قياسي.", + "contentHintAdd": "يُنشئ SKILL.md قياسيًا (مقدّمة YAML + جسم). أسقِط مجلّدات المهارات مفتوحة المصدر في skills/ كما هي.", + "contentHint": "راجع صيغة Claude Agent Skills" + }, + "knowledgeItemModal": { + "addKnowledge": "إضافة معرفة", + "editKnowledge": "تعديل المعرفة", + "category": "الفئة (نوع المخاطر)", + "categoryPlaceholder": "مثال: حقن SQL", + "title": "العنوان", + "titlePlaceholder": "عنوان عنصر المعرفة", + "contentLabel": "المحتوى (Markdown)", + "contentPlaceholder": "أدخل المحتوى بتنسيق Markdown..." + }, + "batchManageModal": { + "title": "إدارة المحادثات · {{count}} إجمالاً", + "searchPlaceholder": "ابحث في السجلّ", + "conversationName": "اسم المحادثة", + "project": "المشروع", + "noProject": "بلا مشروع", + "unknownProject": "مشروع غير معروف", + "filterByProject": "تصفية حسب المشروع", + "lastTime": "آخر نشاط", + "action": "الإجراء", + "selectAll": "تحديد الكل", + "deleteSelected": "حذف المحدّد", + "confirmDeleteNone": "يُرجى تحديد محادثة واحدة على الأقلّ للحذف", + "confirmDeleteN": "حذف {{count}} محادثة محدّدة؟ لا يمكن استرجاع رسائل المحادثة، لكن الثغرات المسجّلة ستبقى في مكتبة الثغرات.", + "deleteFailed": "فشل الحذف", + "unnamedConversation": "محادثة بلا اسم" + }, + "contextMenu": { + "viewAttackChain": "عرض سلسلة الهجوم", + "viewVulnerabilities": "عرض الثغرات", + "downloadMarkdown": "تنزيل Markdown", + "downloadMarkdownSummary": "ملخّص", + "downloadMarkdownFull": "كامل", + "rename": "إعادة تسمية", + "pinConversation": "تثبيت المحادثة", + "unpinConversation": "إلغاء التثبيت", + "batchManage": "إدارة مجمّعة", + "deleteConversation": "حذف المحادثة" + }, + "batchImportModal": { + "hitlInherit": "استخدام الإعدادات العامة", + "hitlOff": "إيقاف الموافقة", + "hitlHuman": "موافقة بشرية", + "hitlAgent": "موافقة الوكيل", + "hitlReviewEdit": "مراجعة الوكيل وتعديله", + "hitlPolicy": "الموافقة على الأدوات", + "hitlHint": "ينطبق على جميع المهامّ في قائمة الانتظار هذه، بما فيها المهامّ المُضافة لاحقًا. ويستخدم القائمة المسموح بها العامة واستراتيجية التدقيق والمهلة الزمنية.", + "hitlSubtaskHint": "تستخدم المهامّ الجديدة إعداد الموافقة على الأدوات الخاص بقائمة الانتظار. غيّره في تفاصيل قائمة الانتظار.", + "title": "مهمّة جديدة", + "queueTitle": "عنوان قائمة الانتظار", + "queueTitlePlaceholder": "أدخل عنوان قائمة الانتظار (اختياري، للتعريف والتصفية)", + "queueTitleHint": "عيّن عنوانًا لقائمة انتظار المهامّ الدفعية لتسهيل العثور عليها وإدارتها لاحقًا.", + "role": "الدور", + "defaultRole": "افتراضي", + "roleHint": "اختر دورًا؛ وستُنفَّذ جميع المهامّ باستخدام إعدادات ذلك الدور (الموجّه والأدوات).", + "project": "المشروع", + "projectNone": "(غير مرتبط)", + "projectHint": "يمكنك اختياريًا ربط قائمة الانتظار هذه بمشروع؛ اتركها فارغة للإبقاء عليها غير مرتبطة.", + "agentMode": "وضع الوكيل", + "agentModeSingle": "وكيل مفرد (Eino ADK)", + "agentModeMulti": "تعدّد الوكلاء (Eino)", + "agentModeHint": "مثل المحادثة: Eino بوكيل مفرد (ADK)، أو Deep / Plan-Execute / Supervisor (تتطلّب الثلاثة الأخيرة multi_agent.enabled).", + "concurrency": "التزامن", + "concurrencyHint": "عدد المهامّ الفرعية التي تُنفَّذ بالتوازي (1-8). القيمة الافتراضية 1 تسلسلية؛ استخدم 1-2 للمهامّ كثيفة الفحص.", + "scheduleMode": "وضع الجدولة", + "scheduleModeManual": "يدوي", + "scheduleModeCron": "تعبير Cron", + "scheduleModeHint": "الوضع اليدوي للتشغيل لمرّة واحدة؛ وCron للتشغيل المتكرّر. تحقّق من المهامّ يدويًا أولًا.", + "cronExpr": "تعبير Cron", + "cronExprPlaceholder": "مثال: 0 */2 * * * (التشغيل كل ساعتين)", + "cronExprHint": "استخدم صيغة Cron القياسية بخمسة حقول: الدقيقة الساعة اليوم الشهر يوم الأسبوع. مثال: `0 2 * * *` يُشغَّل عند 02:00 يوميًا.", + "cronExprRequired": "يرجى إدخال تعبير Cron عند اختيار جدولة Cron", + "cronExprInvalid": "صيغة تعبير Cron غير صالحة. يجب أن يحتوي على 5 حقول (الدقيقة الساعة اليوم الشهر يوم الأسبوع)، مثال: 0 */2 * * *", + "executeNow": "التشغيل فور الإنشاء", + "executeNowHint": "الوضع الافتراضي هو الإيقاف. وعند التعطيل، تبقى قائمة الانتظار معلّقة ويمكن بدؤها يدويًا لاحقًا.", + "tasksList": "قائمة المهامّ (مهمّة واحدة في كل سطر)", + "tasksListPlaceholder": "أدخل قائمة المهامّ، واحدة في كل سطر", + "tasksListPlaceholderExample": "أدخل قائمة المهامّ، واحدة في كل سطر، على سبيل المثال:\nافحص المنافذ المفتوحة لـ 192.168.1.1\nتحقّق مما إذا كان https://example.com عرضة لحقن SQL\nعدّد النطاقات الفرعية لـ example.com", + "tasksListHint": "أدخل أمر مهمّة واحدًا في كل سطر؛ وينفّذها النظام عبر مجمّع تزامن. وتُتجاهَل الأسطر الفارغة.", + "tasksListHintFull": "تلميح: أدخل أمر مهمّة واحدًا في كل سطر؛ وينفّذها النظام عبر مجمّع تزامن. وتُتجاهَل الأسطر الفارغة.", + "createQueue": "إنشاء قائمة الانتظار" + }, + "batchQueueDetailModal": { + "title": "تفاصيل قائمة الانتظار المجمّعة", + "addTask": "إضافة مهمّة", + "startExecute": "بدء", + "startExecuteNow": "التشغيل الآن (دورة واحدة)", + "startExecuteNowConfirm": "هذه قائمة انتظار بتوقيت Cron. سيؤدّي النقر على \"بدء\" إلى تشغيل الدورة الحالية فورًا بدلاً من انتظار وقت Cron التالي. هل تريد المتابعة؟", + "pauseQueue": "إيقاف قائمة الانتظار مؤقّتًا", + "rerunQueue": "إعادة التشغيل", + "deleteQueue": "حذف قائمة الانتظار", + "queueTitle": "عنوان المهمّة", + "role": "الدور", + "defaultRole": "افتراضي", + "queueId": "معرّف قائمة الانتظار", + "status": "الحالة", + "createdAt": "أُنشئت في", + "startedAt": "بدأت في", + "nextRunAt": "التشغيل التالي في", + "scheduleCronAuto": "السماح بالتشغيل التلقائي عبر Cron", + "scheduleCronAutoHint": "عند الإيقاف، يُحتفظ بتعبير cron لكن لن تعمل قائمة الانتظار حسب الجدول؛ استخدم \"بدء\" للتشغيل يدويًّا.", + "editSchedule": "تعديل الجدول", + "editScheduleTitle": "تعديل إعدادات الجدول", + "editScheduleSuccess": "تم تحديث الجدول", + "editScheduleError": "فشل تحديث الجدول", + "editMetadata": "تعديل المعلومات", + "lastScheduleTriggerAt": "آخر تشغيل مجدوَل", + "lastScheduleError": "آخر خطأ في الجدول", + "lastRunError": "ملخّص آخر فشل في التشغيل", + "cronSchedulePausedBadge": "الجدول موقوف مؤقّتًا", + "scheduleToggleFailed": "فشل تحديث مفتاح تبديل الجدول", + "completedAt": "اكتملت في", + "taskTotal": "إجمالي المهامّ", + "concurrency": "التزامن", + "concurrencyInvalid": "يجب أن يكون التزامن عددًا صحيحًا من 1 إلى 8", + "concurrencyEditHint": "انقر للتعديل. لا يمكن التغيير أثناء تشغيل قائمة الانتظار.", + "taskList": "قائمة المهامّ", + "startLabel": "البدء", + "completeLabel": "الاكتمال", + "errorLabel": "خطأ", + "resultLabel": "النتيجة", + "technicalDetails": "التفاصيل التقنية (المعرّف، الأوقات، الجدول)" + }, + "editBatchTaskModal": { + "title": "تعديل المهمّة", + "taskMessage": "رسالة المهمّة", + "taskMessagePlaceholder": "أدخل رسالة المهمّة" + }, + "addBatchTaskModal": { + "title": "إضافة مهمّة", + "taskMessage": "رسالة المهمّة", + "taskMessagePlaceholder": "أدخل رسالة المهمّة", + "add": "إضافة" + }, + "vulnerabilityModal": { + "project": "المشروع", + "projectNone": "(غير مرتبط)", + "projectHint": "تظهر النتائج المرتبطة في list_vulnerabilities لذلك المشروع؛ اتركه فارغًا لاستنتاجه من المحادثة متى أمكن.", + "conversationId": "معرّف المحادثة", + "conversationIdPlaceholder": "اختياري: أدخل معرّف محادثة موجودة، أو اتركه فارغًا لنتيجة مستقلة", + "conversationTag": "وسم المحادثة", + "conversationTagPlaceholder": "مثال: المهمة A، التقرير الأسبوعي", + "taskTag": "وسم المهمّة", + "taskTagPlaceholder": "مثال: فحص دفعة الربع الثاني، إعادة الاختبار", + "title": "العنوان", + "titlePlaceholder": "/api/login عرضة لحقن SQL", + "description": "الوصف", + "descriptionPlaceholder": "صِف الملخص ونقطة التفعيل والسلوك غير الطبيعي الملاحظ وسبب قابلية الاستغلال.", + "severity": "الخطورة", + "pleaseSelect": "يُرجى الاختيار", + "severityCritical": "حرجة", + "severityHigh": "عالية", + "severityMedium": "متوسطة", + "severityLow": "منخفضة", + "severityInfo": "معلوماتية", + "status": "الحالة", + "statusOpen": "مفتوحة", + "statusConfirmed": "مؤكّدة", + "statusFixed": "مُصلَحة", + "statusFalsePositive": "إيجابية كاذبة", + "statusIgnored": "متجاهَلة", + "type": "نوع الثغرة", + "typePlaceholder": "مثال: حقن SQL، XSS، CSRF", + "target": "الهدف", + "targetPlaceholder": "كن محددًا: URL، IP:port، مسار نقطة النهاية، واسم المعامل.", + "preconditions": "الشروط المسبقة", + "preconditionsPlaceholder": "حالة تسجيل الدخول، الصلاحيات، الحساب، الترويسات/ملفات تعريف الارتباط، البيانات المطلوبة، البيئة/الإصدار؛ اكتب «لا يوجد» إن لم يلزم.", + "reproductionSteps": "خطوات إعادة الإنتاج", + "reproductionStepsPlaceholder": "رقّم الخطوات وضمّنها نقطة الدخول والمعامل والحمولة والأمر ونقطة الملاحظة.", + "evidence": "الدليل / POC", + "evidencePlaceholder": "الطلب/الاستجابة الخام، أمر curl/الأداة، ملاحظات لقطات الشاشة، السجلات، سجلات DNSLog/الاستدعاء العكسي، نتائج قاعدة البيانات، مسارات الملفات، الطوابع الزمنية، وغير ذلك.", + "impact": "الأثر", + "impactPlaceholder": "صِف الأثر الفعلي الذي تم التحقق منه، مثل البيانات التي يمكن قراءتها أو تغييرها.", + "recommendation": "التوصية", + "recommendationPlaceholder": "اكتب الإصلاح المحدد ومعايير إعادة الاختبار.", + "retestNotes": "طريقة إعادة الاختبار", + "retestNotesPlaceholder": "كيفية التحقق من الإصلاح، بما في ذلك رمز الحالة المتوقع أو رسالة الخطأ أو نتيجة التحكم في الوصول." + }, + "vulnerabilityMd": { + "headingBasic": "المعلومات الأساسية", + "labelId": "معرّف الثغرة", + "labelSeverity": "الخطورة", + "labelStatus": "الحالة", + "labelType": "النوع", + "labelTarget": "الهدف", + "labelConversationId": "معرّف المحادثة", + "labelTaskId": "معرّف المهمّة", + "labelTaskQueueId": "معرّف قائمة انتظار المهامّ", + "labelConversationTag": "وسم المحادثة", + "labelTaskTag": "وسم المهمّة", + "labelCreated": "تاريخ الإنشاء", + "labelUpdated": "تاريخ التحديث", + "headingDescription": "الوصف", + "headingPreconditions": "الشروط المسبقة", + "headingReproductionSteps": "خطوات إعادة الإنتاج", + "headingEvidence": "الدليل / POC", + "headingImpact": "الأثر", + "headingRecommendation": "المعالجة", + "headingRetestNotes": "طريقة إعادة الاختبار", + "headingRetestLog": "سجلّ إعادة الاختبار" + }, + "roleModal": { + "addRole": "إضافة دور", + "editRole": "تعديل الدور", + "roleName": "اسم الدور", + "roleNamePlaceholder": "أدخل اسم الدور", + "roleDescription": "وصف الدور", + "roleDescriptionPlaceholder": "أدخل وصف الدور", + "roleIcon": "أيقونة الدور", + "roleIconPlaceholder": "أدخل رمزًا تعبيريًا، مثل 🏆", + "roleIconHint": "الرمز التعبيري المعروض في محدد الأدوار.", + "userPrompt": "موجّه المستخدم", + "userPromptPlaceholder": "يُضاف قبل رسالة المستخدم...", + "userPromptHint": "يُضاف هذا الموجّه قبل رسالة المستخدم لتوجيه الذكاء الاصطناعي، ولا يغيّر موجّه النظام.", + "relatedTools": "الأدوات المرتبطة (اختياري)", + "defaultRoleToolsTitle": "يستخدم الدور الافتراضي جميع الأدوات", + "defaultRoleToolsDesc": "يستخدم الدور الافتراضي جميع الأدوات المفعّلة في إدارة MCP.", + "searchToolsPlaceholder": "البحث في الأدوات...", + "loadingTools": "جارٍ تحميل الأدوات...", + "relatedToolsHint": "استخدم «مرتبطة / غير مرتبطة» أعلاه للتصفية حسب خانات اختيار هذا الدور. أما التفعيل/التعطيل على مستوى MCP فيتم في إدارة MCP.", + "enableRole": "تفعيل هذا الدور", + "selectAll": "تحديد الكل", + "deselectAll": "إلغاء تحديد الكل", + "roleNameRequired": "اسم الدور مطلوب", + "roleNotFound": "لم يُعثر على الدور", + "firstRoleNoToolsHint": "الدور الأول الذي لم تُحدَّد له أدوات سيستخدم جميع الأدوات افتراضيًا.", + "filterRoleAll": "الكل", + "filterRoleOn": "مرتبطة بالدور", + "filterRoleOff": "غير مرتبطة", + "statsPageLinked": "المحدد في هذه الصفحة: {{current}} / {{total}}", + "statsPageLinkedTitle": "التحديد = ربط الأداة بهذا الدور؛ لا علاقة له بتفعيل/تعطيل MCP", + "statsRoleLinked": "المحدد للدور: {{current}} / {{max}}", + "statsRoleLinkedTitle": "البسط: الأدوات المحددة (المفعّلة في MCP فقط). المقام: عدد الأدوات المفعّلة في MCP (مطابق لتصفية إدارة MCP)", + "statsRoleLinkedNoMax": "المحدد للدور: {{current}} (غيّر التصفية إلى الكل، دون بحث، وحمّل مرة واحدة لمزامنة الحد الأقصى)", + "statsRoleLinkedNoMaxTitle": "إجمالي الأدوات المفعّلة في MCP غير مخزّن مؤقتًا بعد", + "statsRoleUsesAll": "السياسة: جميع الأدوات المفعّلة في MCP ({{mcpOn}}) · {{all}} إجمالًا في الكتالوج (بما فيها المعطّلة في MCP)", + "statsRoleUsesAllTitle": "يطابق عدد «المفعّلة» في إدارة MCP؛ لا توجد قائمة أدوات صريحة", + "statsListScopeAll": "القائمة: الكل {{n}}", + "statsListScopeRoleOn": "القائمة: المرتبطة بهذا الدور {{n}}", + "statsListScopeRoleOff": "القائمة: غير المرتبطة بهذا الدور {{n}}", + "usingAllEnabledTools": "(استخدام جميع الأدوات المفعّلة)", + "loadToolsFailed": "فشل تحميل الأدوات", + "cannotDeleteDefaultRole": "لا يمكن حذف الدور الافتراضي", + "usingAllTools": "استخدام جميع الأدوات", + "andNMore": " و{{count}} أخرى", + "toolsLabel": "الأدوات:", + "noTools": "لا توجد أدوات", + "paginationShow": "{{start}}-{{end}} من {{total}} أداة", + "paginationSearch": " (البحث: \"{{keyword}}\")", + "firstPage": "الأولى", + "prevPage": "السابق", + "pageOf": "الصفحة {{page}} / {{total}}", + "nextPage": "التالي", + "lastPage": "الأخيرة", + "mcpDisabledBadge": "معطّلة في MCP", + "mcpDisabledBadgeTitle": "معطّلة في إدارة MCP؛ التحديد يعبّر فقط عن الارتباط بالدور، ففعّلها في MCP لتشغيلها", + "roleFilterOnBanner": "هذه الأدوات محددة ومرتبطة بهذا الدور (بشكل مستقل عن التفعيل على مستوى MCP).", + "roleFilterOffBanner": "هذه الأدوات غير محددة وغير مرتبطة بهذا الدور.", + "checkboxLinkTitle": "حدّد لربط هذه الأداة بهذا الدور", + "bindWorkflow": "ربط سير عمل", + "bindWorkflowHint": "عند اختيار سير عمل، تشغّله المحادثات مع هذا الدور تلقائيًا؛ وتُضبط حقول سير العمل بحرية في JSON المخطط.", + "workflowPolicy": "سياسة تشغيل سير العمل", + "workflowPolicyAuto": "تشغيل تلقائي", + "workflowPolicyOff": "إيقاف", + "noWorkflowBind": "بلا سير عمل", + "workflowDisabledSuffix": " (معطّل)", + "workflowMissing": "مفقود أو غير متاح" + }, + "workflows": { + "title": "مسارات العمل", + "newGraph": "سير عمل جديد", + "processLibrary": "مسارات العمل", + "nodeLibrary": "مكتبة العُقد", + "emptyList": "لا توجد مسارات عمل بعد", + "statusInvalid": "سير عمل غير صالح", + "statusEnabled": "مفعّل", + "statusDisabled": "معطّل", + "metaId": "المعرّف", + "metaName": "الاسم", + "metaDescription": "الوصف", + "metaEnabled": "مفعّل", + "metaIdHint": "لا يمكن تغييره بعد الإنشاء؛ يُستخدم لواجهة API ولربط الأدوار", + "metaModalTitle": "تفاصيل سير العمل", + "editMeta": "تعديل", + "untitled": "سير عمل بلا عنوان", + "toggleEnabled": "تفعيل/تعطيل", + "metaEnabledHint": "لا يمكن للأدوار تشغيل مسارات العمل المعطّلة تلقائيًا", + "enabledUpdated": "تم تحديث حالة التفعيل", + "enabledUpdateFailed": "فشل تحديث حالة التفعيل", + "namePlaceholder": "فحص ويب أساسي", + "descriptionPlaceholder": "اختياري", + "toolbarLabel": "أدوات تحرير سير العمل", + "canvasTools": "أدوات اللوحة", + "moreActions": "المزيد", + "deleteWorkflow": "حذف سير العمل", + "ai": { + "open": "إنشاء باللغة الطبيعية", + "title": "إنشاء سير عمل باللغة الطبيعية", + "subtitle": "يُنشئ الذكاء الاصطناعي مسودة قابلة للتعديل، ولن يحفظها أو يشغّلها تلقائيًا.", + "promptLabel": "طلب سير العمل", + "promptPlaceholder": "مثال: مراقبة النطاقات الفرعية والشهادات والصفحات المكشوفة لنطاق ما باستمرار، ثم إنشاء تقرير عند ظهور أصول جديدة", + "examplesLabel": "أمثلة على الطلبات", + "exampleDomain": "مراقبة النطاق", + "exampleReport": "الموافقة على التقرير", + "exampleTriage": "فرز الأصول", + "includeObjective": "إنشاء إعدادات الهدف أيضًا", + "allowSchedule": "السماح باقتراحات التشغيل المجدول", + "allowHighRisk": "السماح بمسودات العُقد عالية الخطورة", + "allowFallback": "السماح ببديل حتمي عند فشل الذكاء الاصطناعي", + "promptRequired": "صِف طلب سير العمل أولًا.", + "generateFailed": "فشل الإنشاء", + "generatedWithIssues": "لا تزال المسودة تحتوي على مشكلات في التحقق. عدّل الطلب وحاول مرة أخرى.", + "generate": "إنشاء مسودة", + "generating": "جارٍ الإنشاء…", + "apply": "عرض على اللوحة", + "rendering": "جارٍ العرض…", + "applied": "تم إنشاء مسودة سير العمل. راجعها قبل الحفظ.", + "canvasRendering": "يعمل الذكاء الاصطناعي على تكوين اللوحة…", + "generatorAI": "قناة الذكاء الاصطناعي", + "generatorLLM": "مُنشأ بواسطة النموذج", + "generatorServer": "مُنشئ المسودات على الخادم", + "generatorFallback": "بديل حتمي", + "generatorUnknown": "مُنشئ غير معروف", + "llmTitle": "أُنشئ باستخدام قناة الذكاء الاصطناعي الافتراضية للمنصة وتم التحقق من بنيته", + "serverTitle": "أُنشئ على الخادم وتم تدقيق بنيته", + "fallbackTitle": "تم استخدام البديل الحتمي", + "serverFallbackNotice": "الإنشاء بواسطة النموذج غير متاح: {{reason}}", + "preview": "معاينة المسار", + "resultNodes": "العُقد", + "resultEdges": "الحواف", + "resultRepairs": "جولات الإصلاح", + "resultCapabilities": "قدرات العُقد", + "resultTools": "قدرات الأدوات", + "resultRisk": "الخطورة", + "resultSaveState": "قابل للحفظ", + "yes": "نعم", + "no": "لا", + "missingFields": "إعدادات ناقصة", + "riskWarnings": "تحذيرات الخطورة", + "assumptions": "الافتراضات", + "capabilityTrace": "سلسلة أدوات القدرات", + "nodeGenerated": "مُنشأ بالذكاء الاصطناعي", + "nodeNeedsInput": "يتطلّب إدخالًا", + "riskLow": "منخفضة", + "riskMedium": "متوسطة", + "riskHigh": "عالية", + "steps": { + "understand": "فهم الطلب", + "match": "مطابقة الأدوات", + "draft": "إنشاء العُقد", + "audit": "تدقيق الأمان" + }, + "examples": { + "domainMonitor": "مراقبة النطاقات الفرعية والشهادات والصفحات المكشوفة لنطاق ما باستمرار، ثم إنشاء تقرير عند ظهور أصول جديدة", + "reportReview": "جمع معلومات التهديدات يوميًا، وإنشاء تقرير، وإرساله إلى المسؤول الأمني للموافقة عليه", + "assetTriage": "استخراج الأصول عالية الخطورة من نتائج الفحص، وإزالة التكرار منها، وتقديم اقتراحات للمعالجة" + } + }, + "audit": { + "title": "تدقيق المسودة", + "ready": "لم يُعثر على عناصر مانعة في هذه المسودة", + "review": "راجِع قبل الحفظ أو التشغيل", + "aiNodes": "عُقد الذكاء الاصطناعي", + "needsInput": "يتطلّب إدخالًا", + "highRisk": "خطورة عالية", + "validation": "مشكلات البنية", + "nodeIssues": "ملاحظات العُقد", + "validationIssues": "فحوصات البنية", + "saveConfirm": "تحتوي هذه المسودة على إعدادات ناقصة أو عُقد عالية الخطورة. هل تريد الحفظ كمسودة على أي حال؟" + }, + "package": { + "importLocal": "استيراد حزمة محلية", + "export": "تصدير", + "title": "استيراد حزمة محلية", + "preflightHint": "راجِع محتويات الحزمة والتعارضات قبل الموافقة على الاستيراد", + "safeBadge": "فحص مسبق آمن", + "stepInspection": "الرفع والفحص المسبق", + "stepInspectionHint": "التحقق من التنسيق والمحتوى والإصدار", + "stepImport": "حل التعارض والاستيراد", + "stepImportHint": "اختر استراتيجية وأكمل الاستيراد", + "stepsLabel": "خطوات الاستيراد", + "packageEyebrow": "حزمة سير عمل محلية", + "packageTitle": "اختر حزمة لاستيرادها", + "decisionEyebrow": "استراتيجية الاستيراد", + "pending": "قيد الانتظار", + "ready": "جاهز", + "footerNote": "تبقى مسارات العمل الحالية دون تغيير حتى تؤكّد", + "selectFile": "اختيار حزمة محلية", + "dropTitle": "أفلِت حزمة محلية هنا", + "dropOr": "أو", + "removeFile": "إزالة الملف", + "noFile": "لم يتم اختيار ملف", + "fileHint": "حزمة .csapkg.zip واحدة فقط، بحجم أقصى 10 MiB.", + "inspect": "الرفع والفحص المسبق", + "inspecting": "جارٍ تنفيذ الفحص المسبق الآمن…", + "resolutionTitle": "طريقة الحل", + "confirmImport": "تأكيد الاستيراد", + "overwriteTitle": "استبدال سير العمل المحلي؟", + "overwriteWarning": "سيؤدي هذا إلى استبدال الاسم والوصف والمخطط وحالة التفعيل. التراجع غير متاح في هذا الإصدار.", + "overwriteCheck": "أؤكّد استبدال سير العمل المحلي الحالي", + "overwriteConfirm": "استبدال واستيراد", + "errors": { + "fileRequired": "اختر حزمة سير عمل محلية قبل الفحص المسبق.", + "fileTooLarge": "يتجاوز الملف الحد البالغ 10 MiB. اختر حزمة محلية أصغر.", + "invalidArchive": "الحزمة المحلية ليست أرشيف Zip صالحًا ولا يمكن فحصها مسبقًا.", + "unsupportedFormat": "تنسيق هذه الحزمة المحلية أو إصدارها غير مدعوم.", + "invalidManifest": "بيان الحزمة المحلية غير صالح ولا يمكن فحصها مسبقًا.", + "checksumMismatch": "فشل التحقق من الحزمة المحلية؛ قد يكون الملف تالفًا أو معدّلًا.", + "multipleWorkflows": "يدعم هذا الإصدار الحزم المحلية التي تحتوي على سير عمل واحد فقط.", + "workflowInvalid": "تعريف سير العمل في الحزمة المحلية غير صالح ولا يمكن فحصه مسبقًا.", + "inspectionNotFound": "سجلّ الفحص المسبق لم يعد موجودًا. ارفع الحزمة وافحصها مسبقًا مرة أخرى.", + "inspectionExpired": "انتهت صلاحية الفحص المسبق. ارفع الحزمة وافحصها مسبقًا مرة أخرى.", + "inspectionConsumed": "تم استيراد هذا الفحص المسبق بالفعل ولا يمكن إعادة استخدامه.", + "conflictChanged": "تغيّر سير العمل المحلي بعد الفحص المسبق. أعِد الفحص المسبق قبل الاستيراد.", + "idConflict": "طريقة الحل المحددة لم تعد تطابق حالة التعارض الحالية. أعِد الفحص المسبق.", + "invalidAction": "طريقة حل الاستيراد غير صالحة. اخترها مرة أخرى.", + "invalidRenameId": "أدخل معرّفًا جديدًا متاحًا لسير العمل.", + "overwriteConfirmationRequired": "أكّد الاستبدال قبل استبدال سير العمل المحلي.", + "idempotencyKeyRequired": "يتعذّر على المتصفح إنشاء معرّف لطلب الاستيراد. حدّث الصفحة وحاول مرة أخرى.", + "idempotencyKeyReused": "حالة طلب الاستيراد غير صالحة. أعِد الفحص المسبق ثم حاول مرة أخرى.", + "importFailed": "فشل الاستيراد؛ لم يتم تغيير سير العمل المحلي.", + "exportFailed": "فشل التصدير. حاول مرة أخرى لاحقًا.", + "workflowNotFound": "لم يُعثر على سير العمل ولا يمكن تصديره.", + "generic": "لم تكتمل العملية. حاول مرة أخرى لاحقًا.", + "serviceUnavailable": "خدمة الاستيراد غير جاهزة. حدّث الصفحة وحاول مرة أخرى." + }, + "conflict": { + "identical": "تم العثور على سير عمل محلي بنفس المعرّف والمحتوى؛ سيتم تخطي الاستيراد.", + "idConflict": "تم العثور على سير عمل محلي بنفس المعرّف لكن بمحتوى مختلف. سيتم الاحتفاظ بالنسخة المحلية افتراضيًا.", + "none": "لم يُعثر على سير عمل محلي بنفس المعرّف. يمكن إنشاء سير عمل جديد." + }, + "summary": { + "title": "نتيجة الفحص المسبق", + "passed": "اجتاز الفحوصات", + "workflowName": "اسم سير العمل", + "unnamedWorkflow": "سير عمل بلا عنوان", + "sourceId": "معرّف سير العمل المصدر", + "sourceRevision": "مراجعة المصدر", + "graphSize": "حجم المخطط", + "graphSizeValue": "{{nodes}} عقدة · {{edges}} حافة", + "contentHash": "تجزئة المحتوى", + "expiresAt": "انتهاء صلاحية الفحص المسبق" + }, + "resolution": { + "needInspection": "اختر حزمة محلية وأكمل الفحص المسبق أولًا.", + "createTitle": "الإنشاء آمن", + "createHint": "سيُنشئ الاستيراد سير عمل محليًا جديدًا.", + "identicalTitle": "لا حاجة إلى نسخة مكررة", + "identicalHint": "المحتوى نفسه موجود بالفعل. لا حاجة إلى استيراد مكرر.", + "keepExisting": "الاحتفاظ بالنسخة المحلية", + "keepExistingHint": "لن يتغير سير العمل المحلي الحالي؛ ويُحتفظ بسجلّ الاستيراد.", + "revealRiskChoices": "تغيير طريقة الحل", + "overwrite": "استبدال النسخة المحلية", + "overwriteHint": "استبدال الاسم والوصف والمخطط وحالة التفعيل؛ يلزم تأكيد ثانٍ.", + "rename": "الحفظ بمعرّف جديد", + "renameHint": "الاحتفاظ بسير العمل المحلي الحالي وإنشاء نسخة بمعرّف جديد.", + "newWorkflowId": "معرّف سير العمل الجديد", + "newWorkflowIdPlaceholder": "مثال: web-scan-basic-copy", + "continueOverwrite": "متابعة إلى تأكيد الاستبدال", + "confirmRename": "تأكيد الحفظ بمعرّف جديد", + "confirmCreate": "تأكيد الإنشاء", + "confirmSkip": "تأكيد تخطي الاستيراد", + "confirmKeep": "تأكيد الاحتفاظ بالنسخة المحلية", + "confirmComplete": "تأكيد الإكمال" + }, + "result": { + "created": "اكتمل الاستيراد: تم إنشاء سير عمل محلي جديد.", + "overwritten": "اكتمل الاستيراد: تم استبدال سير العمل المحلي.", + "renamed": "اكتمل الاستيراد: تم إنشاء سير عمل محلي بالمعرّف الجديد.", + "keptExisting": "تم الاحتفاظ بسير العمل المحلي؛ لم تُجرَ أي تغييرات.", + "skippedIdentical": "المحتوى المحلي نفسه موجود بالفعل؛ تم تخطي الاستيراد.", + "complete": "اكتمل الاستيراد.", + "skippedAction": "تم تخطي الاستيراد", + "keptAction": "تم الاحتفاظ بالنسخة المحلية", + "completedAction": "اكتمل الاستيراد" + }, + "exportSelectSaved": "اختر سير عمل محفوظًا قبل التصدير." + }, + "connect": "ربط", + "connecting": "جارٍ الربط", + "deleteSelected": "حذف المحدد", + "autoLayout": "تخطيط تلقائي", + "dryRun": "تشغيل تجريبي", + "canvasEmpty": "اسحب العُقد من اليسار إلى اللوحة، أو أنشئ سير عمل باللغة الطبيعية", + "properties": "الخصائص", + "nodeProperties": "خصائص العقدة", + "edgeProperties": "خصائص الحافة", + "deleteNode": "حذف العقدة", + "deleteEdge": "حذف الحافة", + "propertyEmpty": "حدّد عقدة أو حافة لتعديل خصائصها", + "propLabel": "الاسم", + "propType": "النوع", + "customFields": "حقول مخصّصة", + "addField": "إضافة حقل", + "noCustomFields": "لا توجد حقول مخصّصة", + "nodes": { + "start": "البداية", + "tool": "أداة", + "agent": "الوكيل", + "condition": "شرط", + "hitl": "موافقة", + "output": "المخرجات", + "end": "النهاية", + "default": "عقدة" + }, + "edges": { + "yes": "نعم", + "no": "لا" + }, + "config": { + "inputKeys": "متغيرات الإدخال", + "mcpTool": "أداة MCP", + "selectTool": "اختر أداة", + "searchTool": "البحث في الأدوات…", + "noToolsFound": "لا توجد أدوات مطابقة", + "noToolsAvailable": "لا توجد أدوات متاحة", + "toolDisabled": " (معطّلة)", + "argumentsTemplate": "قالب الوسائط", + "argumentsStatic": "وسائط الأداة (JSON)", + "timeoutSeconds": "المهلة (بالثواني)", + "optional": "اختياري", + "agentMode": "وضع الوكيل", + "inputSource": "مصدر الإدخال", + "inputBinding": "ربط حقل الإدخال", + "inputBindingHint": "from = مصدر البيانات، field = اسم الحقل (مثل output، message)", + "nodeInstruction": "تعليمات العقدة", + "instructionPlaceholder": "صِف ما يجب أن تنجزه هذه العقدة", + "outputKey": "اسم متغير المخرجات", + "conditionExpression": "تعبير الشرط", + "conditionHint": "تحسب العقدة القيمة matched (true/false)؛ وتحدد الحواف الصادرة الفروع: الحافة الأولى هي \"نعم\" والثانية هي \"لا\". يمكنك أيضًا كتابة {{previous.matched}} == \"true\" على الحافة.", + "conditionGuideTitle": "الصيغة المتاحة", + "conditionGuideVars": "المتغيرات: {{previous.output}}، {{previous.matched}}، {{inputs.message}}، {{outputs.name}}، {{nodeId.output}}", + "conditionGuideOps": "العوامل: ==, !=, >, >=, <, <=, contains, matches, &&, ||", + "conditionGuideJson": "الحقول المتداخلة: jsonpath(value, \"$.status\") أو jq(value, \".severity\")", + "edgeCondition": "شرط الحافة", + "edgeBranch": "فرع الشرط", + "selectBranch": "اختر", + "edgeConditionHintCondition": "{{previous.matched}} == \"true\" (نعم) أو == \"false\" (لا)", + "edgeConditionHintExample": "مثال: {{previous.output}} == \"ok\"", + "edgeBranchHint": "الحافة الأولى من عقدة الشرط تكون افتراضيًا فرع \"نعم\" والثانية فرع \"لا\"؛ ويمكنك تخصيص الشروط هنا.", + "joinStrategy": "استراتيجية الدمج", + "joinStrategyHint": "عند دخول عدة عُقد سابقة إلى هذه العقدة، اختر طريقة بناء previous: دمج الكل، أو الأخيرة حسب ترتيب اللوحة، أو أول قيمة غير فارغة، أو الفشل الفوري.", + "hitlPrompt": "نص طلب الموافقة", + "hitlPromptPlaceholder": "وافق للمتابعة", + "hitlReviewer": "المراجِع", + "hitlInteractiveHint": "يتوقف التشغيل مؤقتًا عند هذه العقدة؛ وافق أو ارفض عبر API أو لوحة المراقبة للمتابعة.", + "promptBinding": "ربط حقل نص الطلب", + "promptBindingHint": "عندما يكون نص الطلب فارغًا، يُقرأ نص الموافقة من الحقل المرتبط", + "outputSource": "مصدر المتغير", + "sourceBinding": "ربط حقل المخرجات", + "sourceBindingHint": "كتابة قيمة الحقل المرتبط في متغير المخرجات؛ القيمة الثابتة أدناه لها الأولوية عند تعيينها", + "staticValue": "قيمة مخرجات ثابتة", + "resultBinding": "ربط ملخص النهاية", + "resultBindingHint": "الحقل المعروض في ملخص عقدة النهاية", + "endTemplate": "قالب ملخص النهاية" + }, + "defaultHitlPrompt": "يُرجى الموافقة على متابعة هذه الخطوة من عدمها", + "nodeFallback": "العقدة {{n}}", + "loadFailed": "فشل تحميل مسارات العمل", + "saveFailed": "فشل حفظ سير العمل", + "dryRunPrompt": "رسالة الإدخال للتشغيل التجريبي", + "dryRunPromptHint": "تُمرَّر هذه الرسالة إلى عقدة البداية بوصفها إدخال message.", + "dryRunFailed": "فشل التشغيل التجريبي", + "dryRunDone": "اكتمل التشغيل التجريبي", + "dryRunTrace": "تتبّع التشغيل التجريبي", + "dryRunNoTrace": "لا يوجد تتبّع", + "deleteFailed": "فشل حذف سير العمل", + "saved": "تم حفظ سير العمل", + "deleted": "تم حذف سير العمل", + "idNameRequired": "معرّف سير العمل واسمه مطلوبان", + "selectToDelete": "اختر سير عمل لحذفه", + "confirmDelete": "حذف سير العمل {{id}}؟", + "duplicateEdge": "توجد حافة بالفعل بين هاتين العقدتين", + "connectModeOn": "وضع الربط: انقر على العقدة المصدر ثم العقدة الهدف", + "connectModeOff": "تم الخروج من وضع الربط", + "validation": { + "needStart": "يلزم وجود عقدة بداية واحدة على الأقل", + "needOutput": "يلزم وجود عقدة مخرجات واحدة على الأقل", + "edgeSelfLoop": "لا يمكن أن تشير الحافة {{id}} إلى نفسها", + "edgeSourceMissing": "العقدة المصدر للحافة {{id}} غير موجودة", + "edgeTargetMissing": "العقدة الهدف للحافة {{id}} غير موجودة", + "startIncoming": "يجب ألا تحتوي عقدة البداية {{label}} على حواف واردة", + "outputOutgoing": "يجب ألا تحتوي عقدة المخرجات {{label}} على حواف صادرة", + "toolNeedsMcp": "تتطلّب عقدة الأداة {{label}} أداة MCP", + "conditionNeedsExpr": "تتطلّب عقدة الشرط {{label}} تعبير شرط", + "conditionNeedsOutEdge": "تحتاج عقدة الشرط {{label}} إلى حافة صادرة واحدة على الأقل (فرع نعم/لا)", + "conditionTooManyEdges": "يجب ألا تتجاوز الحواف الصادرة لعقدة الشرط {{label}} حافتين (نعم/لا)؛ اضبط شروط الحواف للحافة الثالثة وما بعدها", + "conditionBranchLabel": "يجب وسم الحواف الصادرة لعقدة الشرط {{label}} بنعم/لا", + "conditionBranchDuplicate": "تحتوي عقدة الشرط {{label}} على فروع مكررة", + "nodeNeedsIncoming": "تحتاج العقدة {{label}} إلى حافة واردة", + "nodeNeedsOutgoing": "تحتاج العقدة {{label}} إلى حافة صادرة", + "nodeUnreachable": "لا يمكن الوصول إلى العقدة {{label}} من البداية", + "graphCycle": "يحتوي سير العمل على حلقة؛ أبقِه رسمًا بيانيًا موجّهًا غير دوري (DAG)", + "serverFailed": "فشل التحقق من المخطط على الخادم", + "outputNeedsKey": "تتطلّب عقدة المخرجات {{label}} اسم متغير للمخرجات" + } + }, + "rbac": { + "title": "الوصول إلى المنصّة", + "pageSubtitle": "أدِر الأعضاء والأدوار والبيانات المتاحة في مكان واحد.", + "createUser": "إضافة عضو", + "memberManagement": "إدارة الأعضاء", + "memberManagementHint": "عيّن الأدوار ونطاق البيانات للأعضاء", + "roleManagement": "إدارة الأدوار", + "roleManagementHint": "حدّد مجموعات الصلاحيات والظهور الافتراضي", + "users": "المستخدمون", + "usersHint": "اختر حسابًا لإدارة الأدوار ونطاق الموارد.", + "searchUsers": "ابحث باسم المستخدم أو الاسم المعروض", + "metricUsers": "الحسابات", + "metricEnabled": "المُفعّلة", + "metricRoles": "الأدوار", + "metricAssignments": "منح الموارد", + "metricUsersPill": "حساب", + "metricEnabledPill": "حساب مُفعّل", + "metricRolesPill": "دور", + "metricAssignmentsPill": "منحة", + "selectUser": "اختيار مستخدم", + "selectUserHint": "اختر مستخدم منصّة لتعديل الأدوار والحالة ومنح الموارد.", + "createRole": "دور جديد", + "tabRoles": "الأدوار والصلاحيات", + "tabAssignments": "منح الموارد", + "tabAudit": "سجلّ التغييرات", + "roleMembership": "عضوية الأدوار", + "roleMembershipHint": "تتحكّم الأدوار في الإجراءات المسموح بها؛ ويتحكّم نطاق الموارد في البيانات الظاهرة.", + "unsavedChanges": "تغييرات غير محفوظة", + "saveRoleChanges": "حفظ تغييرات الأدوار", + "resourceType": "نوع المورد", + "resourceId": "معرّف المورد", + "resourceIdPlaceholder": "أدخل معرّفات الموارد؛ وافصل بين المعرّفات المتعدّدة بفواصل", + "searchResource": "البحث في الموارد الحقيقية", + "searchResourcePlaceholder": "ابحث بالاسم أو المعرّف", + "assignSelected": "منح الموارد المحدّدة", + "advancedAssignment": "متقدّم: إدخال معرّفات الموارد", + "advancedAssignmentHint": "للمعرّفات المعروفة المنسوخة من السجلّات أو واجهات API. وما زال وجود المورد يُتحقَّق منه عند الإرسال.", + "availableResources": "الموارد المتاحة", + "availableResourcesHint": "اختر في هذه الصفحة؛ وتبقى الاختيارات محفوظة عبر الصفحات", + "currentAssignments": "المنح الصريحة الحالية", + "allResourceTypes": "جميع الأنواع", + "searchAssignments": "ابحث في أسماء الموارد الممنوحة أو معرّفاتها", + "items": "عنصر", + "assignmentHelp": "لا تحتاج إلى منح صريحة سوى أدوار «الموارد المُسنَدة». وتشمل منح المشروع أيضًا المحادثات والثغرات ضمن ذلك المشروع.", + "assignResource": "منح", + "roleCatalog": "فهرس الأدوار", + "roleCatalogHint": "حدّد الصلاحيات ونطاق الموارد أولًا، ثم أسنِد الدور إلى الأعضاء.", + "searchRoles": "ابحث في الأدوار أو الصلاحيات", + "filterRoles": "تصفية الأدوار", + "userModalTitle": "مستخدم المنصّة", + "username": "اسم المستخدم", + "displayName": "الاسم المعروض", + "password": "كلمة المرور", + "passwordPlaceholder": "اتركه فارغًا عند التعديل للإبقاء عليه دون تغيير", + "enabled": "تفعيل الحساب", + "roles": "الأدوار", + "roleModalTitle": "دور المنصّة", + "roleName": "اسم الدور", + "description": "الوصف", + "scope": "نطاق الموارد", + "permissions": "الصلاحيات", + "searchPermissions": "ابحث في الصلاحيات، مثل project:read", + "selectVisible": "تحديد الظاهر", + "clearVisible": "مسح الظاهر", + "deleteUserConfirm": "حذف مستخدم المنصّة هذا؟ سيؤدّي ذلك إلى إبطال جلسات المستخدم النشطة فورًا.", + "deleteBuiltinUser": "لا يمكن حذف مستخدم المسؤول المدمج", + "deleteRoleConfirm": "حذف دور المنصّة هذا؟", + "scopeHint": "يحدّد نطاق الموارد وصلاحيات العمليات معًا الوصول الفعّال.", + "builtinAccount": "حساب مدمج", + "protectBuiltinAdminRole": "يجب أن يحتفظ المسؤول المدمج بدور المسؤول", + "protectBuiltinAdminEnabled": "لا يمكن تعطيل المسؤول المدمج", + "statusEnabled": "مُفعّل", + "statusDisabled": "معطّل", + "noRoleAssigned": "لم يُسنَد أي دور", + "noExplicitGrant": "لا توجد منح صريحة", + "notConfigured": "غير مضبوط", + "noRoleDescription": "لا يوجد وصف للدور", + "noDescription": "لا يوجد وصف", + "systemBuiltin": "دور نظام", + "customRole": "مخصّص", + "operationPermissions": "صلاحيات العمليات", + "assignedMembers": "الأعضاء المُسنَدون", + "viewPermissions": "عرض الصلاحيات", + "effectivePermissions": "الصلاحيات الفعّالة", + "effectiveScope": "نطاق الموارد الفعّال", + "revoke": "إبطال", + "retry": "إعادة المحاولة", + "alreadyAssigned": "مُسنَد", + "loadingResources": "جارٍ تحميل الموارد الحقيقية…", + "viewSystemRole": "عرض دور النظام", + "editRole": "تعديل الدور", + "resourceCount": "{{count}} مورد", + "permissionCount": "{{count}} صلاحية", + "userCount": "{{count}} مستخدم", + "confirmDiscardSwitchUser": "لم تُحفظ تغييرات الأدوار. هل تريد تجاهلها والتبديل بين الأعضاء؟", + "confirmDiscardEditUser": "لم تُحفظ تغييرات الأدوار. هل تريد تجاهلها وتعديل ملفّ العضو؟", + "confirmDiscardSwitchTab": "لم تُحفظ تغييرات الأدوار. هل تريد تجاهلها والتبديل بين علامات التبويب؟", + "confirmDiscardResourceType": "يؤدّي تبديل نوع المورد إلى مسح الاختيار الحالي. هل تريد المتابعة؟", + "pendingGrants": "المنح المعلّقة", + "clearSelection": "مسح الاختيار", + "confirmGrant": "تأكيد المنح", + "removePending": "إزالة", + "selectedCount": "{{count}} محدّد", + "noRoleNoticeTitle": "لم يُسنَد أي دور", + "noRoleNoticeHint": "أسنِد دورًا قبل ضبط منح الموارد. فبدون دور، لا يمكن الوصول إلّا إلى البيانات التي أنشأها المستخدم بنفسه، وتكون معظم الميزات غير متاحة.", + "assignRolesNow": "إسناد الأدوار", + "viewPermissionsDetail": "عرض التفاصيل", + "hidePermissions": "إخفاء التفاصيل", + "effectivePermissionsDetail": "تفاصيل الصلاحيات الفعّالة", + "editUserRolesHint": "أدِر الأدوار في صفحة تفاصيل العضو ضمن «الأدوار والصلاحيات».", + "auditTitle": "سجلّ تغييرات الصلاحيات", + "auditHint": "أحداث التدقيق المتعلّقة بوصول هذا العضو إلى المنصّة.", + "unknownActor": "فاعل غير معروف", + "metricUserContext": "العضو المحدّد: {{name}} · {{roles}} دور · {{grants}} منحة", + "currentMemberLabel": "العضو المحدّد", + "auditPageSizeLabel": "الصفوف في كل صفحة", + "auditPageSize20": "20 / صفحة", + "auditPageSize50": "50 / صفحة", + "auditPageSize100": "100 / صفحة", + "auditFilters": { + "filterBarLabel": "عوامل تصفية سجلّ التغييرات", + "actionLabel": "التصفية حسب الإجراء", + "allActions": "جميع الإجراءات", + "actorPlaceholder": "اسم مستخدم الفاعل", + "resourceTypeLabel": "التصفية حسب نوع المورد", + "allResourceTypes": "جميع أنواع الموارد" + }, + "unknownTarget": "عضو غير معروف", + "auditColumns": { + "action": "الإجراء", + "actor": "الفاعل", + "target": "العضو المستهدَف", + "resourceType": "نوع المورد", + "resourceId": "معرّف المورد", + "time": "الوقت" + }, + "deleteUserConfirmDetailed": "حذف العضو {{name}} (@{{username}})؟\n\nسيؤدّي ذلك إلى إبطال الجلسات النشطة وإزالة {{roles}} من ارتباطات الأدوار و{{grants}} من منح الموارد.", + "auditActions": { + "create_user": "إنشاء مستخدم منصّة", + "update_user": "تحديث مستخدم منصّة", + "delete_user": "حذف مستخدم منصّة", + "create_role": "إنشاء دور منصّة", + "update_role": "تحديث دور منصّة", + "delete_role": "حذف دور منصّة", + "assign_resource": "منح الوصول إلى مورد", + "delete_resource_assignment": "إبطال منح مورد" + }, + "copyId": "نسخ المعرّف", + "copiedId": "تمّ النسخ", + "resourceIdLabel": "المعرّف", + "resourceDetailProject": "المشروع {{id}}", + "pagination": { + "first": "الأولى", + "previous": "السابقة", + "next": "التالية", + "last": "الأخيرة", + "rowsPerPage": "الصفوف في كل صفحة", + "recordRange": "عرض {{start}}-{{end}} من {{total}} سجلّ", + "pageIndicator": "الصفحة {{page}} / {{pages}}", + "resourcePage": "الصفحة {{page}}", + "pageSummary": "الصفحة {{page}} / {{pages}} · {{total}} عنصر", + "emptySummary": "0 عنصر" + }, + "resourceTypes": { + "user": "مستخدم المنصّة", + "project": "مشروع", + "conversation": "محادثة", + "vulnerability": "ثغرة", + "webshell": "اتصال WebShell", + "batch_task": "مهمّة دفعية", + "c2_listener": "مستمع C2" + }, + "scopes": { + "all": { + "label": "جميع الموارد", + "hint": "يمكنه الوصول إلى جميع بيانات المنصّة", + "option": "جميع الموارد — الوصول إلى جميع بيانات المنصّة" + }, + "assigned": { + "label": "الموارد المُسنَدة", + "hint": "فقط البيانات الممنوحة صراحةً من قِبل مسؤول", + "option": "الموارد المُسنَدة — فقط البيانات الممنوحة من المسؤول" + }, + "own": { + "label": "ما أنشأته بنفسي", + "hint": "فقط البيانات التي أنشأها العضو", + "option": "ما أنشأته بنفسي — فقط البيانات التي أنشأها المستخدم بنفسه" + } + }, + "systemRoles": { + "admin": { + "name": "مسؤول", + "description": "وصول كامل لإدارة المنصّة" + }, + "operator": { + "name": "مُشغّل", + "description": "تنفيذ مسارات العمل الأمنية اليومية دون إدارة الحسابات أو الإعدادات الأساسية" + }, + "auditor": { + "name": "مدقّق", + "description": "وصول للقراءة فقط إلى عمليات التدقيق والمراقبة والأصول" + }, + "viewer": { + "name": "مستخدم للقراءة فقط", + "description": "وصول للقراءة فقط إلى الموارد الممنوحة صراحةً" + } + }, + "empty": { + "noMatchingUsers": "لا يوجد أعضاء مطابقون", + "noUsers": "لا يوجد أعضاء في المنصّة بعد", + "tryAnotherKeyword": "جرّب كلمة مفتاحية أخرى", + "addFirstUser": "استخدم «إضافة عضو» لإنشاء العضو الأول", + "noMatchingRoles": "لا توجد أدوار مطابقة", + "adjustRoleSearch": "عدّل البحث أو أنشئ دورًا مخصّصًا", + "noRoles": "لا توجد أدوار متاحة", + "selectUserFirst": "اختر عضوًا أولًا", + "noAssignments": "لا توجد منح موارد", + "addAssignmentHint": "اختر الموارد على اليسار، ثم استخدم «تأكيد المنح» في الأعلى", + "noMatchingAssignments": "لا توجد منح مطابقة", + "adjustAssignmentFilter": "عدّل نوع المورد أو البحث", + "noMatchingResources": "لا توجد موارد حقيقية مطابقة", + "noMatchingPermissions": "لا توجد صلاحيات مطابقة", + "noEffectivePermissions": "لا توجد صلاحيات فعّالة", + "assignRoleFirst": "أسنِد دورًا لعرض تفاصيل الصلاحيات", + "noAuditLogs": "لا يوجد سجلّ تغييرات", + "noAuditLogsHint": "ستظهر هنا تغييرات الأدوار والمنح الخاصة بهذا العضو" + }, + "messages": { + "roleChangesSaved": "حُفظت تغييرات الأدوار وأصبحت فعّالة فورًا", + "grantPartial": "تمّت إضافة {{created}} منحة؛ و{{skipped}} كانت موجودة بالفعل أو مكرّرة", + "grantSuccess": "تمّ منح {{count}} من موارد {{resource}}" + }, + "errors": { + "loadFailed": "فشل التحميل", + "loadDataFailed": "تعذّر تحميل بيانات التحكّم في الوصول", + "loadResourcesFailed": "تعذّر تحميل الموارد", + "saveFailed": "فشل الحفظ", + "saveUserFailed": "تعذّر حفظ العضو", + "saveRoleFailed": "تعذّر حفظ الدور", + "deleteFailed": "فشل الحذف", + "deleteUserFailed": "تعذّر حذف العضو", + "deleteRoleFailed": "تعذّر حذف الدور", + "enterResourceId": "أدخل معرّف مورد واحدًا على الأقلّ", + "tooManyResources": "يمكن منح 100 مورد كحدّ أقصى في المرّة الواحدة", + "batchGrantFailed": "فشل المنح الدفعي", + "grantFailed": "فشل المنح", + "revokeFailed": "فشل الإبطال", + "loadAuditFailed": "تعذّر تحميل سجلّ التدقيق" + }, + "permissionModules": { + "agent": "تنفيذ الوكيل", + "agents": "تعريفات الوكلاء", + "attackchain": "سلاسل الهجوم", + "audit": "التدقيق", + "auth": "أمان الحساب", + "c2": "C2", + "chat": "المحادثات", + "config": "إعدادات النظام", + "dashboard": "لوحة التحكّم", + "files": "الملفّات", + "fofa": "FOFA", + "hitl": "التدخّل البشري", + "knowledge": "قاعدة المعرفة", + "mcp": "MCP", + "monitor": "مراقبة التنفيذ", + "notification": "الإشعارات", + "openapi": "OpenAPI", + "project": "المشاريع", + "rbac": "الوصول إلى المنصّة", + "robot": "الروبوتات", + "roles": "أدوار الاختبار", + "skills": "المهارات", + "tasks": "المهامّ", + "terminal": "الطرفية", + "vulnerability": "الثغرات", + "webshell": "WebShell", + "workflow": "مسارات العمل" + }, + "permissionDescriptions": { + "auth": { + "self": "إدارة الجلسة وكلمة المرور الخاصة" + }, + "dashboard": { + "read": "عرض ملخّصات لوحة التحكّم" + }, + "chat": { + "read": "عرض المحادثات", + "write": "إنشاء المحادثات وتحديثها", + "delete": "حذف المحادثات والأدوار الحوارية" + }, + "agent": { + "execute": "تشغيل وكلاء الذكاء الاصطناعي ومسارات العمل" + }, + "hitl": { + "read": "عرض قوائم انتظار التدخّل البشري وسجلّاته", + "write": "الموافقة على طلبات التدخّل البشري ورفضها وضبطها" + }, + "tasks": { + "read": "عرض قوائم انتظار المهامّ", + "write": "إنشاء قوائم انتظار المهامّ وتشغيلها", + "delete": "حذف قوائم انتظار المهامّ" + }, + "project": { + "read": "عرض المشاريع وحقائق المشاريع", + "write": "إنشاء المشاريع وحقائق المشاريع وتحديثها", + "delete": "حذف المشاريع وحقائق المشاريع" + }, + "vulnerability": { + "read": "عرض الثغرات", + "write": "إنشاء الثغرات وتحديثها", + "delete": "حذف الثغرات" + }, + "webshell": { + "read": "عرض اتصالات WebShell", + "write": "إدارة اتصالات WebShell واستخدامها", + "delete": "حذف اتصالات WebShell" + }, + "c2": { + "read": "عرض مستمعي C2 والجلسات والمهامّ والأحداث والملفّات الشخصية", + "write": "تشغيل مستمعي C2 والجلسات والمهامّ والحمولات والملفّات والملفّات الشخصية", + "delete": "حذف كائنات C2" + }, + "mcp": { + "read": "عرض حالة MCP وإعدادات MCP الخارجية", + "write": "إدارة خوادم MCP الخارجية واستدعاء نقطة نهاية MCP" + }, + "knowledge": { + "read": "عرض قاعدة المعرفة وسجلّات الاسترجاع", + "write": "إنشاء قاعدة المعرفة وتحديثها وفهرستها وفحصها", + "delete": "حذف عناصر المعرفة وسجلّات الاسترجاع" + }, + "skills": { + "read": "عرض المهارات والإحصاءات", + "write": "إنشاء المهارات وتحديثها", + "delete": "حذف المهارات والإحصاءات" + }, + "agents": { + "read": "عرض وكلاء Markdown", + "write": "إنشاء وكلاء Markdown وتحديثهم", + "delete": "حذف وكلاء Markdown" + }, + "roles": { + "read": "عرض أدوار اختبار الذكاء الاصطناعي", + "write": "إنشاء أدوار اختبار الذكاء الاصطناعي وتحديثها", + "delete": "حذف أدوار اختبار الذكاء الاصطناعي" + }, + "workflow": { + "read": "عرض تعريفات مسارات العمل وعمليات تشغيلها", + "write": "إنشاء مسارات العمل وتحديثها والتحقّق منها وتشغيلها واستئنافها", + "delete": "حذف مسارات العمل" + }, + "config": { + "read": "عرض إعدادات النظام", + "write": "تحديث إعدادات النظام وتطبيقها" + }, + "terminal": { + "execute": "تنفيذ أوامر الطرفية" + }, + "audit": { + "read": "عرض سجلّات التدقيق وتصديرها", + "delete": "حذف سجلّات التدقيق" + }, + "rbac": { + "read": "عرض المستخدمين وأدوار المنصّة والصلاحيات والمنح", + "write": "إدارة المستخدمين وأدوار المنصّة والصلاحيات والمنح" + }, + "notification": { + "read": "عرض الإشعارات", + "write": "تحديد الإشعارات كمقروءة" + }, + "robot": { + "read": "عرض حالة ربط الروبوت", + "write": "إدارة ارتباطات الروبوت واختبار الاستدعاءات" + }, + "files": { + "read": "عرض الملفّات المرفوعة في المحادثة", + "write": "رفع ملفّات المحادثة وتعديلها وإعادة تسميتها", + "delete": "حذف ملفّات المحادثة" + }, + "attackchain": { + "read": "عرض سلاسل الهجوم", + "write": "إعادة توليد سلاسل الهجوم" + }, + "fofa": { + "execute": "تشغيل عمليات بحث FOFA وتحليل الاستعلامات" + }, + "openapi": { + "read": "قراءة نتائج تجميع OpenAPI" + }, + "monitor": { + "read": "عرض مراقبة التنفيذ", + "write": "إلغاء عمليات التنفيذ المراقَبة", + "delete": "حذف سجلّات مراقبة التنفيذ" + } + } + }, + "vulnerabilityRetest": { + "startFailed": "لم يُرسَل إعادة الاختبار. مُوجّهه محفوظ في الفرع؛ عُد إليه للمتابعة.", + "title": "إعادة اختبار الثغرة", + "button": "إعادة اختبار", + "hint": "يُنشئ كلّ تأكيد فرعاً مستقلاً من المحادثة الأصلية، محافظاً على مشروعها وسجلّها للجولات اللاحقة.", + "promptLabel": "مُوجّه إعادة الاختبار", + "placeholderHint": "تُستبدل العناصر النائبة عند الإرسال. يمكنك أيضاً كتابتها في الرسائل اللاحقة.", + "confirm": "تأكيد وبدء إعادة الاختبار", + "defaultPrompt": "يُرجى التحقّق مما إذا كانت الثغرة {{vulnerability_id}} قد أُصلحت. حدّث حالتها عند اكتمال التحقّق.", + "guidanceHeading": "طريقة التحقّق (من ملاحظات إعادة اختبار النتيجة):", + "promptRequired": "أدخل مُوجّه إعادة اختبار.", + "navigationChanged": "لقد بدّلت المحادثات. عُد إلى فرع إعادة الاختبار لإرسال المُوجّه.", + "branchCreated": "تم إنشاء فرع إعادة الاختبار. تابع التحقّق في ذلك الفرع." + } +} diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index bf586265..01b85ae0 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -2,7 +2,8 @@ "lang": { "zhCN": "中文", "enUS": "English", - "ruRU": "Русский" + "ruRU": "Русский", + "arSA": "العربية" }, "mobile": { "openNav": "Open navigation menu", diff --git a/web/static/i18n/ru-RU.json b/web/static/i18n/ru-RU.json index 18a6aeae..5d2e6fc1 100644 --- a/web/static/i18n/ru-RU.json +++ b/web/static/i18n/ru-RU.json @@ -2,7 +2,8 @@ "lang": { "zhCN": "中文", "enUS": "English", - "ruRU": "Русский" + "ruRU": "Русский", + "arSA": "العربية" }, "mobile": { "openNav": "Открыть меню навигации", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index b5d97ba7..039fcb6d 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -2,7 +2,8 @@ "lang": { "zhCN": "中文", "enUS": "English", - "ruRU": "Русский" + "ruRU": "Русский", + "arSA": "العربية" }, "mobile": { "openNav": "打开导航菜单", diff --git a/web/static/js/i18n.js b/web/static/js/i18n.js index b403db48..0917bccc 100644 --- a/web/static/js/i18n.js +++ b/web/static/js/i18n.js @@ -29,12 +29,27 @@ if (navLang.startsWith('ru')) { return 'ru-RU'; } + if (navLang.startsWith('ar')) { + return 'ar-SA'; + } if (navLang.startsWith('en')) { return 'en-US'; } return DEFAULT_LANG; } + // 根据语言设置文档方向(阿拉伯语为从右到左 RTL) + function applyDirection(lang) { + try { + if (document && document.documentElement) { + var l = String(lang || DEFAULT_LANG).toLowerCase(); + document.documentElement.dir = l.indexOf('ar') === 0 ? 'rtl' : 'ltr'; + } + } catch (e) { + // ignore + } + } + async function loadLanguageResources(lang) { if (loadedLangs[lang]) { return; @@ -114,6 +129,9 @@ // ignore } + // 更新文档方向(RTL/LTR) + applyDirection(i18next.language || DEFAULT_LANG); + try { if (typeof window.syncHitlAuditBackendUI === 'function') { window.syncHitlAuditBackendUI(); @@ -129,6 +147,8 @@ label.textContent = i18next.t('lang.zhCN'); } else if (lang.indexOf('ru') === 0) { label.textContent = i18next.t('lang.ruRU'); + } else if (lang.indexOf('ar') === 0) { + label.textContent = i18next.t('lang.arSA'); } else { label.textContent = i18next.t('lang.enUS'); } @@ -159,7 +179,7 @@ const current = i18next.language || DEFAULT_LANG; if (lang === current) return; await loadLanguageResources(lang); - if (lang === 'ru-RU') { + if (lang === 'ru-RU' || lang === 'ar-SA') { await loadLanguageResources('en-US'); } await i18next.changeLanguage(lang); @@ -193,6 +213,7 @@ lng: initialLang, fallbackLng: { 'ru-RU': ['en-US', 'zh-CN'], + 'ar-SA': ['en-US', 'zh-CN'], 'default': [DEFAULT_LANG] }, debug: false, @@ -200,7 +221,7 @@ }); await loadLanguageResources(initialLang); - if (initialLang === 'ru-RU') { + if (initialLang === 'ru-RU' || initialLang === 'ar-SA') { await loadLanguageResources('en-US'); } applyTranslations(document); @@ -221,6 +242,7 @@ const lang = String((window.__locale || (typeof i18next !== 'undefined' && i18next.language) || '')).toLowerCase(); if (lang.indexOf('zh') === 0) return 'zh-CN'; if (lang.indexOf('ru') === 0) return 'ru-RU'; + if (lang.indexOf('ar') === 0) return 'ar-SA'; return 'en-US'; }; window.changeLanguage = changeLanguage; diff --git a/web/static/js/webshell-probe.test.cjs b/web/static/js/webshell-probe.test.cjs new file mode 100644 index 00000000..3d2d71f9 --- /dev/null +++ b/web/static/js/webshell-probe.test.cjs @@ -0,0 +1,60 @@ +const fs = require('node:fs'); +const vm = require('node:vm'); +const test = require('node:test'); +const assert = require('node:assert/strict'); +const source = fs.readFileSync('web/static/js/webshell.js', 'utf8'); +function harness(reply) { + const requests = []; + const button = {}; + const ctx = { + webshellConnections: Array.from({length: 6}, (_, i) => ({id: `conn-${i}`, url: 'http://example.invalid', password: '********'})), + webshellBatchProbeRunning: false, webshellProbeStatusById: {}, + wsT: key => key, renderWebshellList() {}, + document: {getElementById: id => id === 'webshell-batch-probe-btn' ? button : {value: 'unrelated-edit-id'}}, + webshellConnEncoding: () => 'utf-8', webshellConnOS: () => 'linux', + apiFetch: (url, options) => { + const body = JSON.parse(options.body); + requests.push(body); + return reply(body); + } + }; + vm.createContext(ctx); + for (const name of ['buildWebshellProbeToken', 'buildWebshellProbeCommand', 'isWebshellProbeOutputMatched', 'probeWebshellConnection', 'updateWebshellBatchProbeButton', 'runBatchProbeWebshellConnections']) { + const start = source.indexOf(`function ${name}(`); + const end = source.indexOf('\nfunction ', start + 1); + assert.ok(start >= 0 && end > start); + vm.runInContext(source.slice(start, end), ctx); + } + return {ctx, requests, button}; +} +function success(body) { + return Promise.resolve({json: async () => ({ok: true, output: body.command.replace(/^echo /, '')})}); +} +test('saved masked credentials use each connection ID and complete a batch, then allow retry', async () => { + const {ctx, requests, button} = harness(success); + await ctx.runBatchProbeWebshellConnections(); + assert.deepEqual(requests.map(r => r.connection_id).sort(), Array.from({length: 6}, (_, i) => `conn-${i}`)); + assert.ok(Object.values(ctx.webshellProbeStatusById).every(s => s.state === 'ok')); + assert.equal(ctx.webshellBatchProbeRunning, false); + assert.equal(button.disabled, false); + await ctx.runBatchProbeWebshellConnections(); + assert.equal(requests.length, 12); +}); +for (const failure of ['sync', 'network', 'json', 'server', 'mismatch']) { + test(`${failure} failure settles offline, completes other connections and restores button`, async () => { + const {ctx, button, requests} = harness(body => { + if (body.connection_id !== 'conn-0') return success(body); + if (failure === 'sync') throw new Error('sync failure'); + if (failure === 'network') return Promise.reject(new Error('network failure')); + if (failure === 'json') return Promise.resolve({json: () => Promise.reject(new Error('invalid JSON'))}); + return Promise.resolve({json: async () => ({ok: failure === 'mismatch', output: '', error: 'probe failed'})}); + }); + await ctx.runBatchProbeWebshellConnections(); + assert.equal(requests.length, 6); + assert.equal(ctx.webshellProbeStatusById['conn-0'].state, 'fail'); + assert.ok(ctx.webshellProbeStatusById['conn-0'].message); + assert.equal(Object.values(ctx.webshellProbeStatusById).filter(s => s.state === 'ok').length, 5); + assert.equal(ctx.webshellBatchProbeRunning, false); + assert.equal(button.disabled, false); + }); +} diff --git a/web/static/js/webshell.js b/web/static/js/webshell.js index b71e3e7c..8a8a5302 100644 --- a/web/static/js/webshell.js +++ b/web/static/js/webshell.js @@ -1232,21 +1232,23 @@ function probeWebshellConnection(conn) { return Promise.resolve({ ok: false, message: wsT('webshell.testFailed') || '连通性测试失败' }); } var probeToken = buildWebshellProbeToken(); - return apiFetch('/api/webshell/exec', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - url: conn.url, - password: conn.password || '', - type: conn.type || 'php', - method: ((conn.method || 'post').toLowerCase() === 'get') ? 'get' : 'post', - cmd_param: conn.cmdParam || '', - encoding: webshellConnEncoding(conn), - os: webshellConnOS(conn), - connection_id: conn.id || '', - command: buildWebshellProbeCommand(probeToken), - connection_id: password === '********' ? (document.getElementById('webshell-edit-id')?.value || '') : '' - }) + // Include request construction and synchronous transport errors in the failure path. + return Promise.resolve().then(function () { + return apiFetch('/api/webshell/exec', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + url: conn.url, + password: conn.password || '', + type: conn.type || 'php', + method: ((conn.method || 'post').toLowerCase() === 'get') ? 'get' : 'post', + cmd_param: conn.cmdParam || '', + encoding: webshellConnEncoding(conn), + os: webshellConnOS(conn), + connection_id: conn.id || '', + command: buildWebshellProbeCommand(probeToken) + }) + }); }) .then(function (r) { return r.json(); }) .then(function (data) { @@ -1322,7 +1324,7 @@ function runBatchProbeWebshellConnections() { var workers = []; for (var i = 0; i < concurrency; i++) workers.push(runOne()); - Promise.all(workers).finally(function () { + return Promise.all(workers).finally(function () { webshellBatchProbeRunning = false; updateWebshellBatchProbeButton(done, total, okCount); }); diff --git a/web/templates/api-docs.html b/web/templates/api-docs.html index 4239bc2a..666091d3 100644 --- a/web/templates/api-docs.html +++ b/web/templates/api-docs.html @@ -989,6 +989,7 @@
中文
English
Русский
+
العربية
diff --git a/web/templates/index.html b/web/templates/index.html index fa6dffb1..b23fe3f2 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -138,6 +138,7 @@
中文
English
Русский
+
العربية