From 35b6bc55b814fced68431a9ce10609a2b9dea201 Mon Sep 17 00:00:00 2001 From: Rdmclin2 Date: Fri, 12 Jun 2026 16:08:31 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20workspace=20error=20(#157?= =?UTF-8?q?01)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: support workspace (page author, copyTo/transferTo, notifications, i18n & fixes) Squashed 13 commits from fix/workspace-error for clean rebase onto main's submodule base. --- .../routers/lambda/_schema/documentHistory.ts | 1 + apps/server/src/routers/lambda/agentSignal.ts | 1 + apps/server/src/routers/lambda/verify.ts | 22 +- apps/server/src/services/agent/index.test.ts | 51 +++ apps/server/src/services/agent/index.ts | 20 +- .../agentRuntime/CompletionLifecycle.ts | 15 +- .../src/services/bot/BotMessageRouter.ts | 3 + apps/server/src/services/document/history.ts | 2 + apps/server/src/services/document/types.ts | 1 + .../serverRuntimes/lobeDeliveryChecker.ts | 6 +- .../serverRuntimes/verifyResult.ts | 16 +- .../src/services/verify/agentVerifier.ts | 12 +- apps/server/src/services/verify/executor.ts | 10 +- .../src/services/verify/feedbackService.ts | 4 +- apps/server/src/services/verify/lifecycle.ts | 8 +- .../src/services/verify/planGenerator.ts | 10 +- .../src/services/verify/repairService.ts | 32 +- .../src/services/verify/statusService.ts | 6 +- locales/ar/chat.json | 1 + locales/ar/notification.json | 2 + locales/ar/plugin.json | 18 + locales/ar/setting.json | 3 + locales/bg-BG/chat.json | 1 + locales/bg-BG/notification.json | 2 + locales/bg-BG/plugin.json | 18 + locales/bg-BG/setting.json | 3 + locales/de-DE/chat.json | 1 + locales/de-DE/notification.json | 2 + locales/de-DE/plugin.json | 18 + locales/de-DE/setting.json | 3 + locales/en-US/common.json | 3 - locales/en-US/notification.json | 2 + locales/en-US/setting.json | 11 +- locales/es-ES/chat.json | 1 + locales/es-ES/notification.json | 2 + locales/es-ES/plugin.json | 18 + locales/es-ES/setting.json | 3 + locales/fa-IR/chat.json | 1 + locales/fa-IR/notification.json | 2 + locales/fa-IR/plugin.json | 18 + locales/fa-IR/setting.json | 3 + locales/fr-FR/chat.json | 1 + locales/fr-FR/notification.json | 2 + locales/fr-FR/plugin.json | 18 + locales/fr-FR/setting.json | 3 + locales/it-IT/chat.json | 1 + locales/it-IT/notification.json | 2 + locales/it-IT/plugin.json | 18 + locales/it-IT/setting.json | 3 + locales/ja-JP/chat.json | 1 + locales/ja-JP/notification.json | 2 + locales/ja-JP/plugin.json | 18 + locales/ja-JP/setting.json | 3 + locales/ko-KR/chat.json | 1 + locales/ko-KR/notification.json | 2 + locales/ko-KR/plugin.json | 18 + locales/ko-KR/setting.json | 3 + locales/nl-NL/chat.json | 1 + locales/nl-NL/notification.json | 2 + locales/nl-NL/plugin.json | 18 + locales/nl-NL/setting.json | 3 + locales/pl-PL/chat.json | 1 + locales/pl-PL/notification.json | 2 + locales/pl-PL/plugin.json | 18 + locales/pl-PL/setting.json | 3 + locales/pt-BR/chat.json | 1 + locales/pt-BR/notification.json | 2 + locales/pt-BR/plugin.json | 18 + locales/pt-BR/setting.json | 3 + locales/ru-RU/chat.json | 1 + locales/ru-RU/notification.json | 2 + locales/ru-RU/plugin.json | 18 + locales/ru-RU/setting.json | 3 + locales/tr-TR/chat.json | 1 + locales/tr-TR/notification.json | 2 + locales/tr-TR/plugin.json | 18 + locales/tr-TR/setting.json | 3 + locales/vi-VN/chat.json | 1 + locales/vi-VN/notification.json | 2 + locales/vi-VN/plugin.json | 18 + locales/vi-VN/setting.json | 3 + locales/zh-CN/common.json | 3 - locales/zh-CN/notification.json | 2 + locales/zh-CN/setting.json | 13 +- locales/zh-CN/subscription.json | 2 +- locales/zh-TW/chat.json | 1 + locales/zh-TW/notification.json | 2 + locales/zh-TW/plugin.json | 18 + locales/zh-TW/setting.json | 3 + package.json | 2 - .../database/src/models/verifyCheckResult.ts | 35 +- .../database/src/models/verifyCriterion.ts | 27 +- packages/database/src/models/verifyRubric.ts | 58 +-- packages/locales/src/default/common.ts | 3 - packages/locales/src/default/notification.ts | 3 + packages/locales/src/default/setting.ts | 13 +- packages/types/src/user/settings/tool.ts | 10 + scripts/codemodWorkspaceNav.ts | 371 ------------------ .../TaskDetailHeaderActions.tsx | 20 +- .../features/useTaskItemContextMenu.tsx | 10 +- .../AgentTasks/shared/taskDetailPath.test.ts | 16 + .../AgentTasks/shared/taskDetailPath.ts | 6 +- .../PageEditor/Header/useMenu.test.tsx | 1 + src/features/PageEditor/Header/useMenu.tsx | 33 +- .../History/CompareModal/CompareContent.tsx | 8 + .../History/CompareModal/HistorySidebar.tsx | 119 ++++-- .../History/HistoryItemsProvider.test.tsx | 1 + .../History/HistoryItemsProvider.tsx | 3 +- .../PageEditor/History/HistoryListItem.tsx | 31 +- src/features/User/UserPanel/useMenu.tsx | 6 +- .../__tests__/workspaceAwarePath.test.ts | 4 + src/features/Workspace/workspaceAwarePath.ts | 9 +- src/libs/swr/index.ts | 2 +- .../Agent/List/AgentItem/useDropdownMenu.tsx | 7 + .../(main)/home/_layout/Footer/index.tsx | 8 +- .../home/features/Recents/useDropdownMenu.tsx | 15 +- src/services/document/index.ts | 1 + src/store/tool/slices/builtin/action.test.ts | 88 ++++- src/store/tool/slices/builtin/action.ts | 157 +++++--- 119 files changed, 1061 insertions(+), 646 deletions(-) delete mode 100644 scripts/codemodWorkspaceNav.ts diff --git a/apps/server/src/routers/lambda/_schema/documentHistory.ts b/apps/server/src/routers/lambda/_schema/documentHistory.ts index 6e01224f63..255241bddd 100644 --- a/apps/server/src/routers/lambda/_schema/documentHistory.ts +++ b/apps/server/src/routers/lambda/_schema/documentHistory.ts @@ -58,6 +58,7 @@ export interface DocumentHistoryListItem { isCurrent: boolean; savedAt: string; saveSource: DocumentHistorySaveSource; + userId: string; } export interface ListHistoryOutput { diff --git a/apps/server/src/routers/lambda/agentSignal.ts b/apps/server/src/routers/lambda/agentSignal.ts index 65143b9ae2..cb2d51760b 100644 --- a/apps/server/src/routers/lambda/agentSignal.ts +++ b/apps/server/src/routers/lambda/agentSignal.ts @@ -85,6 +85,7 @@ export const agentSignalRouter = router({ return enqueueAgentSignalSourceEvent(sourceEvent, { agentId: input.agentId, userId: ctx.userId, + workspaceId: ctx.workspaceId ?? undefined, }); }), listReceipts: agentSignalProcedure diff --git a/apps/server/src/routers/lambda/verify.ts b/apps/server/src/routers/lambda/verify.ts index b2a0786408..1ec9d9b916 100644 --- a/apps/server/src/routers/lambda/verify.ts +++ b/apps/server/src/routers/lambda/verify.ts @@ -1,11 +1,12 @@ import { z } from 'zod'; +import { wsCompatProcedure } from '@/business/server/trpc-middlewares/workspaceAuth'; import { AgentOperationModel } from '@/database/models/agentOperation'; import { LlmGenerationTracingModel } from '@/database/models/llmGenerationTracing'; import { VerifyCheckResultModel } from '@/database/models/verifyCheckResult'; import { VerifyCriterionModel } from '@/database/models/verifyCriterion'; import { VerifyRubricModel } from '@/database/models/verifyRubric'; -import { authedProcedure, router } from '@/libs/trpc/lambda'; +import { router } from '@/libs/trpc/lambda'; import { serverDatabase } from '@/libs/trpc/lambda/middleware'; import { VerifyExecutorService, @@ -35,18 +36,19 @@ const checkItemSchema = z.object({ verifierType: verifierTypeSchema, }); -const verifyProcedure = authedProcedure.use(serverDatabase).use(async (opts) => { +const verifyProcedure = wsCompatProcedure.use(serverDatabase).use(async (opts) => { const { ctx } = opts; + const workspaceId = ctx.workspaceId ?? undefined; return opts.next({ ctx: { - criterionModel: new VerifyCriterionModel(ctx.serverDB, ctx.userId), - executorService: new VerifyExecutorService(ctx.serverDB, ctx.userId), - tracingModel: new LlmGenerationTracingModel(ctx.serverDB, ctx.userId), - feedbackService: new VerifyFeedbackService(ctx.serverDB, ctx.userId), - operationModel: new AgentOperationModel(ctx.serverDB, ctx.userId), - planGenerator: new VerifyPlanGeneratorService(ctx.serverDB, ctx.userId), - resultModel: new VerifyCheckResultModel(ctx.serverDB, ctx.userId), - rubricModel: new VerifyRubricModel(ctx.serverDB, ctx.userId), + criterionModel: new VerifyCriterionModel(ctx.serverDB, ctx.userId, workspaceId), + executorService: new VerifyExecutorService(ctx.serverDB, ctx.userId, workspaceId), + tracingModel: new LlmGenerationTracingModel(ctx.serverDB, ctx.userId, workspaceId), + feedbackService: new VerifyFeedbackService(ctx.serverDB, ctx.userId, workspaceId), + operationModel: new AgentOperationModel(ctx.serverDB, ctx.userId, workspaceId), + planGenerator: new VerifyPlanGeneratorService(ctx.serverDB, ctx.userId, workspaceId), + resultModel: new VerifyCheckResultModel(ctx.serverDB, ctx.userId, workspaceId), + rubricModel: new VerifyRubricModel(ctx.serverDB, ctx.userId, workspaceId), }, }); }); diff --git a/apps/server/src/services/agent/index.test.ts b/apps/server/src/services/agent/index.test.ts index 9eb49cd5cf..bc10ecb7c3 100644 --- a/apps/server/src/services/agent/index.test.ts +++ b/apps/server/src/services/agent/index.test.ts @@ -231,6 +231,57 @@ describe('AgentService', () => { // Avatar should not be present for non-builtin agents expect((result as any)?.avatar).toBeUndefined(); }); + + it('should NOT inherit the member personal default model for a workspace inbox', async () => { + // Workspace inbox is persisted with an empty model/provider. + const mockAgent = { + id: 'agent-1', + slug: 'inbox', + }; + const serverDefaultConfig = { model: 'system-default-model', provider: 'system-provider' }; + + const mockAgentModel = { + getBuiltinAgent: vi.fn().mockResolvedValue(mockAgent), + }; + + (AgentModel as any).mockImplementation(() => mockAgentModel); + (parseAgentConfig as any).mockReturnValue(serverDefaultConfig); + // The member opening the workspace inbox has a personal default model. + mockUserModel.getUserSettingsDefaultAgentConfig.mockResolvedValueOnce({ + config: { model: 'opus-4.6', provider: 'anthropic' }, + }); + + const workspaceService = new AgentService(mockDb, mockUserId, mockWorkspaceId); + const result = await workspaceService.getBuiltinAgent('inbox'); + + // Should fall back to the system default, NOT the member's personal model. + expect(result?.model).toBe('system-default-model'); + expect(result?.provider).toBe('system-provider'); + }); + + it('should still apply the personal default model for a personal inbox', async () => { + const mockAgent = { + id: 'agent-1', + slug: 'inbox', + }; + + const mockAgentModel = { + getBuiltinAgent: vi.fn().mockResolvedValue(mockAgent), + }; + + (AgentModel as any).mockImplementation(() => mockAgentModel); + (parseAgentConfig as any).mockReturnValue({}); + mockUserModel.getUserSettingsDefaultAgentConfig.mockResolvedValueOnce({ + config: { model: 'user-preferred-model', provider: 'user-provider' }, + }); + + // No workspaceId → personal scope keeps the personal default behavior. + const newService = new AgentService(mockDb, mockUserId); + const result = await newService.getBuiltinAgent('inbox'); + + expect(result?.model).toBe('user-preferred-model'); + expect(result?.provider).toBe('user-provider'); + }); }); describe('getAgentConfig', () => { diff --git a/apps/server/src/services/agent/index.ts b/apps/server/src/services/agent/index.ts index febe845080..76d0ca5639 100644 --- a/apps/server/src/services/agent/index.ts +++ b/apps/server/src/services/agent/index.ts @@ -174,6 +174,13 @@ export class AgentService { * 2. serverDefaultAgentConfig - from environment variable * 3. userDefaultAgentConfig - from user settings (defaultAgent.config) * 4. agent - actual agent config from database + * + * Workspace exception: a workspace is a shared resource, so its agents must + * NOT inherit any individual member's *personal* default model. Otherwise a + * shared agent persisted with an empty model (e.g. the workspace inbox) + * resolves to whoever opens it — the creator's personal default leaks in and + * the workspace looks "initialized" with their model. For workspace-scoped + * reads we skip the user layer and fall back to the system default instead. */ private mergeDefaultConfig( agent: any, @@ -181,12 +188,17 @@ export class AgentService { ): LobeAgentConfig | null { if (!agent) return null; - const userDefaultAgentConfig = - (defaultAgentConfig as { config?: PartialDeep })?.config || {}; - - // Merge configs in order: DEFAULT -> server -> user -> agent + // Merge configs in order: DEFAULT -> server -> [user] -> agent const serverDefaultAgentConfig = getServerDefaultAgentConfig(); const baseConfig = merge(DEFAULT_AGENT_CONFIG, serverDefaultAgentConfig); + + // Skip the personal default layer for workspace-scoped agents (see above). + if (this.workspaceId) { + return merge(baseConfig, cleanObject(agent)); + } + + const userDefaultAgentConfig = + (defaultAgentConfig as { config?: PartialDeep })?.config || {}; const withUserConfig = merge(baseConfig, userDefaultAgentConfig); return merge(withUserConfig, cleanObject(agent)); diff --git a/apps/server/src/services/agentRuntime/CompletionLifecycle.ts b/apps/server/src/services/agentRuntime/CompletionLifecycle.ts index 86440b5074..8e7b854ef5 100644 --- a/apps/server/src/services/agentRuntime/CompletionLifecycle.ts +++ b/apps/server/src/services/agentRuntime/CompletionLifecycle.ts @@ -344,11 +344,16 @@ export class CompletionLifecycle { metadata?.assistantMessageId, metadata?.userId || this.userId, ); - void runVerifyOnCompletion(this.serverDB, metadata?.userId || this.userId, { - deliverable: event.lastAssistantContent ?? '', - goal, - operationId, - }); + void runVerifyOnCompletion( + this.serverDB, + metadata?.userId || this.userId, + { + deliverable: event.lastAssistantContent ?? '', + goal, + operationId, + }, + this.workspaceId, + ); } if (reason === 'error') { diff --git a/apps/server/src/services/bot/BotMessageRouter.ts b/apps/server/src/services/bot/BotMessageRouter.ts index 2999a67edc..b89bef74ed 100644 --- a/apps/server/src/services/bot/BotMessageRouter.ts +++ b/apps/server/src/services/bot/BotMessageRouter.ts @@ -990,6 +990,7 @@ export class BotMessageRouter { agentId, db: serverDB, userId, + workspaceId: workspaceId ?? undefined, }, { ignoreError: true }, ); @@ -1175,6 +1176,7 @@ export class BotMessageRouter { agentId, db: serverDB, userId, + workspaceId: workspaceId ?? undefined, }, { ignoreError: true }, ); @@ -1392,6 +1394,7 @@ export class BotMessageRouter { agentId, db: serverDB, userId, + workspaceId: workspaceId ?? undefined, }, { ignoreError: true }, ); diff --git a/apps/server/src/services/document/history.ts b/apps/server/src/services/document/history.ts index 4741ec2faa..b3514d7d71 100644 --- a/apps/server/src/services/document/history.ts +++ b/apps/server/src/services/document/history.ts @@ -185,6 +185,7 @@ export class DocumentHistoryService { isCurrent: true, saveSource: 'system', savedAt: headDocument.updatedAt, + userId: headDocument.userId, }); } @@ -193,6 +194,7 @@ export class DocumentHistoryService { isCurrent: false, saveSource: row.saveSource as DocumentHistorySaveSource, savedAt: row.savedAt, + userId: row.userId, })); // If head consumed a slot and we fetched a full page of history rows, diff --git a/apps/server/src/services/document/types.ts b/apps/server/src/services/document/types.ts index acd0a41096..c811e1f4e7 100644 --- a/apps/server/src/services/document/types.ts +++ b/apps/server/src/services/document/types.ts @@ -22,6 +22,7 @@ export interface DocumentHistoryListItem { isCurrent: boolean; savedAt: Date; saveSource: DocumentHistorySaveSource; + userId: string; } export interface DocumentHistoryItemResult { diff --git a/apps/server/src/services/toolExecution/serverRuntimes/lobeDeliveryChecker.ts b/apps/server/src/services/toolExecution/serverRuntimes/lobeDeliveryChecker.ts index 85bd3442d6..9861cee874 100644 --- a/apps/server/src/services/toolExecution/serverRuntimes/lobeDeliveryChecker.ts +++ b/apps/server/src/services/toolExecution/serverRuntimes/lobeDeliveryChecker.ts @@ -10,6 +10,7 @@ interface LobeDeliveryCheckerRuntimeContext { operationId?: string; serverDB: LobeChatDatabase; userId: string; + workspaceId?: string; } const buildError = (content: string, code: string): BuiltinServerRuntimeOutput => ({ @@ -28,11 +29,13 @@ class LobeDeliveryCheckerExecutionRuntime { private operationId?: string; private db: LobeChatDatabase; private userId: string; + private workspaceId?: string; constructor(context: LobeDeliveryCheckerRuntimeContext) { this.operationId = context.operationId; this.db = context.serverDB; this.userId = context.userId; + this.workspaceId = context.workspaceId; } generateVerifyPlan = async (params: { @@ -64,7 +67,7 @@ class LobeDeliveryCheckerExecutionRuntime { // criteria + a rubric, snapshot it onto this operation, and confirm it. The // tool call is human-reviewed (humanIntervention); this runs post-approval. const { VerifyPlanGeneratorService } = await import('@/server/services/verify'); - const planGenerator = new VerifyPlanGeneratorService(this.db, this.userId); + const planGenerator = new VerifyPlanGeneratorService(this.db, this.userId, this.workspaceId); const { items, rubricId } = await planGenerator.createPlanFromCriteria({ criteria, operationId: this.operationId, @@ -110,6 +113,7 @@ export const lobeDeliveryCheckerRuntime: ServerRuntimeRegistration = { operationId: context.operationId, serverDB: context.serverDB, userId: context.userId, + workspaceId: context.workspaceId, }); }, identifier: LobeDeliveryCheckerIdentifier, diff --git a/apps/server/src/services/toolExecution/serverRuntimes/verifyResult.ts b/apps/server/src/services/toolExecution/serverRuntimes/verifyResult.ts index 8d41985430..43571eac73 100644 --- a/apps/server/src/services/toolExecution/serverRuntimes/verifyResult.ts +++ b/apps/server/src/services/toolExecution/serverRuntimes/verifyResult.ts @@ -15,6 +15,7 @@ interface VerifyResultRuntimeContext { operationId?: string; serverDB: LobeChatDatabase; userId: string; + workspaceId?: string; } /** @@ -27,11 +28,13 @@ class VerifyResultExecutionRuntime { private operationId?: string; private db: LobeChatDatabase; private userId: string; + private workspaceId?: string; constructor(context: VerifyResultRuntimeContext) { this.operationId = context.operationId; this.db = context.serverDB; this.userId = context.userId; + this.workspaceId = context.workspaceId; } submitVerifyResult = async (params: SubmitVerifyResultParams) => { @@ -47,11 +50,13 @@ class VerifyResultExecutionRuntime { } // The verifier runs as a sub-agent; the row to update belongs to the parent run. - const op = await new AgentOperationModel(this.db, this.userId).findById(this.operationId); + const op = await new AgentOperationModel(this.db, this.userId, this.workspaceId).findById( + this.operationId, + ); const targetOperationId = op?.parentOperationId ?? this.operationId; const status = params.verdict === 'passed' ? 'passed' : 'failed'; - await new VerifyCheckResultModel(this.db, this.userId).updateByCheckItem( + await new VerifyCheckResultModel(this.db, this.userId, this.workspaceId).updateByCheckItem( targetOperationId, params.checkItemId, { @@ -66,10 +71,12 @@ class VerifyResultExecutionRuntime { verdict: params.verdict, }, ); - await new VerifyStatusService(this.db, this.userId).recompute(targetOperationId); + await new VerifyStatusService(this.db, this.userId, this.workspaceId).recompute( + targetOperationId, + ); // This may be the last check to resolve — kick auto-repair if the run failed // with auto_repair checks (no-op until everything has a terminal result). - await maybeAutoRepair(this.db, this.userId, targetOperationId); + await maybeAutoRepair(this.db, this.userId, targetOperationId, this.workspaceId); log( 'submitted verdict %s for check %s (op %s)', @@ -94,6 +101,7 @@ export const verifyResultRuntime: ServerRuntimeRegistration = { operationId: context.operationId, serverDB: context.serverDB, userId: context.userId, + workspaceId: context.workspaceId, }); }, identifier: VerifyToolIdentifier, diff --git a/apps/server/src/services/verify/agentVerifier.ts b/apps/server/src/services/verify/agentVerifier.ts index 57459da98d..8ad106799f 100644 --- a/apps/server/src/services/verify/agentVerifier.ts +++ b/apps/server/src/services/verify/agentVerifier.ts @@ -52,18 +52,20 @@ export const createVerifierAgentRunner = (params: { provider?: string | null; topicId?: string | null; userId: string; + workspaceId?: string; }): VerifierAgentRunner | undefined => { - const { db, deliverable, model, provider, topicId, userId } = params; + const { db, deliverable, model, provider, topicId, userId, workspaceId } = params; if (!topicId) return undefined; return async ({ checkItem, goal, operationId }) => { // The detailed instruction is the criterion's rule body, stored in a document. const instruction = checkItem.documentId - ? ((await new DocumentModel(db, userId).findById(checkItem.documentId))?.content ?? undefined) + ? ((await new DocumentModel(db, userId, workspaceId).findById(checkItem.documentId)) + ?.content ?? undefined) : undefined; // Materialize the builtin verify agent (idempotent) to get an id for the thread. - const verifyAgent = await new AgentModel(db, userId).getBuiltinAgent( + const verifyAgent = await new AgentModel(db, userId, workspaceId).getBuiltinAgent( BUILTIN_AGENT_SLUGS.verifyAgent, ); if (!verifyAgent) { @@ -71,7 +73,7 @@ export const createVerifierAgentRunner = (params: { return null; } - const thread = await new ThreadModel(db, userId).create({ + const thread = await new ThreadModel(db, userId, workspaceId).create({ agentId: verifyAgent.id, title: `Verify: ${checkItem.title}`, topicId, @@ -85,7 +87,7 @@ export const createVerifierAgentRunner = (params: { // Dynamic import breaks the static cycle: aiAgent → agentRuntime completion // → verify lifecycle → this runner → aiAgent. const { AiAgentService } = await import('@/server/services/aiAgent'); - const result = await new AiAgentService(db, userId).execAgent({ + const result = await new AiAgentService(db, userId, { workspaceId }).execAgent({ appContext: { threadId: thread.id, topicId }, autoStart: true, // Inherit the parent run's model/provider so the verifier uses a provider diff --git a/apps/server/src/services/verify/executor.ts b/apps/server/src/services/verify/executor.ts index 83f5317439..25a9031e2d 100644 --- a/apps/server/src/services/verify/executor.ts +++ b/apps/server/src/services/verify/executor.ts @@ -80,13 +80,13 @@ export class VerifyExecutorService { private readonly statusService: VerifyStatusService; private readonly documentModel: DocumentModel; - constructor(db: LobeChatDatabase, userId: string) { + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { this.db = db; this.userId = userId; - this.operationModel = new AgentOperationModel(db, userId); - this.resultModel = new VerifyCheckResultModel(db, userId); - this.statusService = new VerifyStatusService(db, userId); - this.documentModel = new DocumentModel(db, userId); + this.operationModel = new AgentOperationModel(db, userId, workspaceId); + this.resultModel = new VerifyCheckResultModel(db, userId, workspaceId); + this.statusService = new VerifyStatusService(db, userId, workspaceId); + this.documentModel = new DocumentModel(db, userId, workspaceId); } /** diff --git a/apps/server/src/services/verify/feedbackService.ts b/apps/server/src/services/verify/feedbackService.ts index a026f5462d..1355b19cef 100644 --- a/apps/server/src/services/verify/feedbackService.ts +++ b/apps/server/src/services/verify/feedbackService.ts @@ -23,8 +23,8 @@ export const computeFalseFlags = ( export class VerifyFeedbackService { private readonly resultModel: VerifyCheckResultModel; - constructor(db: LobeChatDatabase, userId: string) { - this.resultModel = new VerifyCheckResultModel(db, userId); + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { + this.resultModel = new VerifyCheckResultModel(db, userId, workspaceId); } /** Record a user's decision on a result and precompute its FP/FN flags. */ diff --git a/apps/server/src/services/verify/lifecycle.ts b/apps/server/src/services/verify/lifecycle.ts index 13d80bb7a6..8763948be0 100644 --- a/apps/server/src/services/verify/lifecycle.ts +++ b/apps/server/src/services/verify/lifecycle.ts @@ -33,9 +33,10 @@ export const runVerifyOnCompletion = async ( db: LobeChatDatabase, userId: string, params: RunVerifyOnCompletionParams, + workspaceId?: string, ): Promise => { try { - const operationModel = new AgentOperationModel(db, userId); + const operationModel = new AgentOperationModel(db, userId, workspaceId); const state = await operationModel.getVerifyState(params.operationId); // Opt-in gate: only runs with a confirmed plan that hasn't been verified yet. @@ -48,7 +49,7 @@ export const runVerifyOnCompletion = async ( return; } - const executor = new VerifyExecutorService(db, userId); + const executor = new VerifyExecutorService(db, userId, workspaceId); await executor.execute({ deliverable: params.deliverable, goal: params.goal, @@ -63,13 +64,14 @@ export const runVerifyOnCompletion = async ( provider: op.provider, topicId: op.topicId, userId, + workspaceId, }), }); // Auto-repair once verification has fully resolved. For runs with only inline // (LLM/program) checks, everything is resolved now; runs with async agent // checks no-op here and re-trigger from the verifier's writeback path. - await maybeAutoRepair(db, userId, params.operationId); + await maybeAutoRepair(db, userId, params.operationId, workspaceId); } catch (error) { log('runVerifyOnCompletion failed for op %s (non-fatal): %O', params.operationId, error); } diff --git a/apps/server/src/services/verify/planGenerator.ts b/apps/server/src/services/verify/planGenerator.ts index 66312407f3..c11e6aaf86 100644 --- a/apps/server/src/services/verify/planGenerator.ts +++ b/apps/server/src/services/verify/planGenerator.ts @@ -75,13 +75,13 @@ export class VerifyPlanGeneratorService { private readonly operationModel: AgentOperationModel; private readonly documentModel: DocumentModel; - constructor(db: LobeChatDatabase, userId: string) { + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { this.db = db; this.userId = userId; - this.criterionModel = new VerifyCriterionModel(db, userId); - this.rubricModel = new VerifyRubricModel(db, userId); - this.operationModel = new AgentOperationModel(db, userId); - this.documentModel = new DocumentModel(db, userId); + this.criterionModel = new VerifyCriterionModel(db, userId, workspaceId); + this.rubricModel = new VerifyRubricModel(db, userId, workspaceId); + this.operationModel = new AgentOperationModel(db, userId, workspaceId); + this.documentModel = new DocumentModel(db, userId, workspaceId); } /** diff --git a/apps/server/src/services/verify/repairService.ts b/apps/server/src/services/verify/repairService.ts index 35c4059760..64082f98ab 100644 --- a/apps/server/src/services/verify/repairService.ts +++ b/apps/server/src/services/verify/repairService.ts @@ -22,11 +22,12 @@ const resolveMaxRepairRounds = async ( db: LobeChatDatabase, userId: string, plan: VerifyCheckItem[], + workspaceId?: string, ): Promise => { const rubricId = plan.find((i) => i.sourceRubricId)?.sourceRubricId; if (!rubricId) return DEFAULT_MAX_REPAIR_ROUNDS; - const rubric = await new VerifyRubricModel(db, userId).findById(rubricId); + const rubric = await new VerifyRubricModel(db, userId, workspaceId).findById(rubricId); return rubric?.config?.maxRepairRounds ?? DEFAULT_MAX_REPAIR_ROUNDS; }; @@ -79,12 +80,13 @@ export const createRepairRunner = (params: { provider?: string | null; topicId?: string | null; userId: string; + workspaceId?: string; }): RepairSpawner | undefined => { - const { agentId, db, maxRepairRounds, model, provider, topicId, userId } = params; + const { agentId, db, maxRepairRounds, model, provider, topicId, userId, workspaceId } = params; if (!agentId || !topicId) return undefined; return async ({ instruction, operationId, verifyMessageId }) => { - const operationModel = new AgentOperationModel(db, userId); + const operationModel = new AgentOperationModel(db, userId, workspaceId); const round = await countRepairRounds(operationModel, operationId); if (round >= maxRepairRounds) { @@ -98,7 +100,7 @@ export const createRepairRunner = (params: { // for the operation title / logs. `verifyMessageId` parents the new turn under // the verify card it responds to. const { AiAgentService } = await import('@/server/services/aiAgent'); - const result = await new AiAgentService(db, userId).execAgent({ + const result = await new AiAgentService(db, userId, { workspaceId }).execAgent({ agentId, appContext: { topicId }, autoStart: true, @@ -138,13 +140,16 @@ export const maybeAutoRepair = async ( db: LobeChatDatabase, userId: string, operationId: string, + workspaceId?: string, ): Promise => { - const operationModel = new AgentOperationModel(db, userId); + const operationModel = new AgentOperationModel(db, userId, workspaceId); const state = await operationModel.getVerifyState(operationId); const plan = (state?.verifyPlan ?? []) as VerifyCheckItem[]; if (plan.length === 0) return; - const results = await new VerifyCheckResultModel(db, userId).listByOperation(operationId); + const results = await new VerifyCheckResultModel(db, userId, workspaceId).listByOperation( + operationId, + ); const byItem = new Map(results.map((r) => [r.checkItemId, r])); // Wait until every required check has a terminal result (don't repair early). @@ -160,13 +165,14 @@ export const maybeAutoRepair = async ( const spawner = createRepairRunner({ agentId: op?.agentId, db, - maxRepairRounds: await resolveMaxRepairRounds(db, userId, plan), + maxRepairRounds: await resolveMaxRepairRounds(db, userId, plan, workspaceId), model: op?.model, provider: op?.provider, topicId: op?.topicId, userId, + workspaceId, }); - await new VerifyRepairService(db, userId).triggerAutoRepair(operationId, spawner); + await new VerifyRepairService(db, userId, workspaceId).triggerAutoRepair(operationId, spawner); }; const isFailed = (r: VerifyCheckResultItem | undefined): boolean => @@ -191,11 +197,11 @@ export class VerifyRepairService { private readonly resultModel: VerifyCheckResultModel; private readonly statusService: VerifyStatusService; - constructor(db: LobeChatDatabase, userId: string) { - this.messageModel = new MessageModel(db, userId); - this.operationModel = new AgentOperationModel(db, userId); - this.resultModel = new VerifyCheckResultModel(db, userId); - this.statusService = new VerifyStatusService(db, userId); + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { + this.messageModel = new MessageModel(db, userId, workspaceId); + this.operationModel = new AgentOperationModel(db, userId, workspaceId); + this.resultModel = new VerifyCheckResultModel(db, userId, workspaceId); + this.statusService = new VerifyStatusService(db, userId, workspaceId); } /** Collect the auto-repairable failures for a run. */ diff --git a/apps/server/src/services/verify/statusService.ts b/apps/server/src/services/verify/statusService.ts index 78b0fe9b1c..506c642b45 100644 --- a/apps/server/src/services/verify/statusService.ts +++ b/apps/server/src/services/verify/statusService.ts @@ -17,9 +17,9 @@ export class VerifyStatusService { private readonly operationModel: AgentOperationModel; private readonly resultModel: VerifyCheckResultModel; - constructor(db: LobeChatDatabase, userId: string) { - this.operationModel = new AgentOperationModel(db, userId); - this.resultModel = new VerifyCheckResultModel(db, userId); + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { + this.operationModel = new AgentOperationModel(db, userId, workspaceId); + this.resultModel = new VerifyCheckResultModel(db, userId, workspaceId); } /** diff --git a/locales/ar/chat.json b/locales/ar/chat.json index 3c8d975bca..9fa13c21cb 100644 --- a/locales/ar/chat.json +++ b/locales/ar/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "الذاكرة المحفوظة", "workflow.toolDisplayName.calculate": "محسوب", "workflow.toolDisplayName.callAgent": "تم استدعاء وكيل", + "workflow.toolDisplayName.callMcpTool": "تم استدعاء أداة MCP", "workflow.toolDisplayName.callSubAgent": "تم إرسال وكيل فرعي", "workflow.toolDisplayName.clearTodos": "تم مسح المهام", "workflow.toolDisplayName.copyDocument": "تم نسخ مستند", diff --git a/locales/ar/notification.json b/locales/ar/notification.json index 08eab4874e..c6fe8eeec4 100644 --- a/locales/ar/notification.json +++ b/locales/ar/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "تم الوصول إلى الحد الأقصى للدفع حسب الاستخدام للتخزين", "video_generation_completed": "الفيديو الخاص بك \"{{prompt}}\" جاهز.", "video_generation_completed_title": "اكتملت عملية إنشاء الفيديو", + "workspace_member_invited": "{{inviterLabel}} دعاك للانضمام إلى مساحة العمل \"{{workspaceName}}\" ك{{role}}.", + "workspace_member_invited_title": "دعوة للانضمام إلى {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} انضم إلى مساحة العمل \"{{workspaceName}}\" كـ {{role}}.", "workspace_member_joined_member": "{{memberLabel}} انضم إلى مساحة العمل \"{{workspaceName}}\" كعضو.", "workspace_member_joined_member_title": "عضو جديد انضم إلى {{workspaceName}}", diff --git a/locales/ar/plugin.json b/locales/ar/plugin.json index e1ded9a32b..7a0ac159f8 100644 --- a/locales/ar/plugin.json +++ b/locales/ar/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "إجمالي {{count}} من المعاملات", "arguments.title": "المعلمات", + "builtins.codex.apiName.command_execution": "تشغيل الأمر", + "builtins.codex.apiName.file_change": "تعديل الملفات", + "builtins.codex.apiName.mcp_tool_call": "استدعاء أداة MCP", + "builtins.codex.apiName.todo_list": "تحديث المهام", + "builtins.codex.apiName.web_search": "البحث على الويب", + "builtins.codex.commandExecution.grep": "بحث", + "builtins.codex.commandExecution.noResults": "لا توجد نتائج", + "builtins.codex.commandExecution.readFile": "قراءة الملف", + "builtins.codex.fileChange.editedFiles_one": "تم تعديل {{count}} ملف", + "builtins.codex.fileChange.editedFiles_other": "تم تعديل {{count}} ملفات", + "builtins.codex.fileChange.editing": "تعديل الملفات", + "builtins.codex.fileChange.noChanges": "لا توجد تغييرات في الملفات", + "builtins.codex.fileChange.unknownFile": "ملف غير معروف", + "builtins.codex.mcpTool.error": "خطأ", + "builtins.codex.mcpTool.input": "إدخال", + "builtins.codex.mcpTool.result": "النتيجة", + "builtins.codex.mcpTool.unknownTool": "أداة MCP", + "builtins.codex.webSearch.query": "استعلام", "builtins.lobe-activator.apiName.activateTools": "تفعيل الأدوات", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} غير موجود", "builtins.lobe-agent-builder.apiName.getAvailableModels": "الحصول على النماذج المتاحة", diff --git a/locales/ar/setting.json b/locales/ar/setting.json index a74a71d8b8..c4f150e115 100644 --- a/locales/ar/setting.json +++ b/locales/ar/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "اكتمل إنشاء الصورة", "notification.item.storage_overage_cap_reached": "تم الوصول إلى الحد الأقصى للدفع حسب الاستخدام للتخزين", "notification.item.video_generation_completed": "اكتمل إنشاء الفيديو", + "notification.item.workspace_member_invited": "دعوة إلى مساحة العمل", "notification.item.workspace_member_joined": "انضم عضو جديد", "notification.item.workspace_member_removed": "تمت إزالته من مساحة العمل", "notification.item.workspace_payment_failed": "فشل تجديد الدفع", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} هو بالفعل عضو في هذه مساحة العمل.", "workspace.members.invite.failed": "فشل في إرسال الدعوة", "workspace.members.invite.limitReached": "يمكن أن تحتوي هذه مساحة العمل على ما يصل إلى {{limit}} أعضاء. قم بإزالة عضو قبل دعوة المزيد.", + "workspace.members.invite.modal.billIncrease": "ستزيد فاتورتك بمقدار ${{amount}}/شهريًا.", "workspace.members.invite.modal.cancel": "إلغاء", "workspace.members.invite.modal.confirm": "تأكيد", "workspace.members.invite.modal.description_one": "فريقك يتوسع! بالتأكيد، ستدعو عضو فريق جديد واحد إلى هذه مساحة العمل.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "عند النقر على الترقية، سيتم فرض رسوم ${{fee}}، بالإضافة إلى أي ضرائب ورسوم قابلة للتطبيق، فورًا ثم كل شهر، حتى تقوم بالإلغاء. يتم تسوية رسوم المقاعد والاستخدام حسب الطلب في نهاية الشهر؛ إذا تجاوز استخدامك حد الفوترة خلال دورة، قد يتم فرض رسوم على طريقة الدفع الخاصة بك قبل انتهاء الدورة.", "workspace.upgradeModal.continueCta": "متابعة", "workspace.upgradeModal.createTeam": "إنشاء مساحة العمل", + "workspace.upgradeModal.formDescription": "راجع التفاصيل أدناه وقم بتأكيد الترقية.", "workspace.upgradeModal.formSubtitle": "يتم فرض رسوم المنصة فقط اليوم — يتم تسوية رسوم المقاعد في نهاية الشهر.", "workspace.upgradeModal.formTitle": "ترقية {{name}} إلى Pro", "workspace.upgradeModal.heading": "ترقية مساحة العمل إلى Pro", diff --git a/locales/bg-BG/chat.json b/locales/bg-BG/chat.json index 013c6bf994..66e3d92208 100644 --- a/locales/bg-BG/chat.json +++ b/locales/bg-BG/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Запазена памет", "workflow.toolDisplayName.calculate": "Изчислено", "workflow.toolDisplayName.callAgent": "Извикан агент", + "workflow.toolDisplayName.callMcpTool": "Извикан MCP инструмент", "workflow.toolDisplayName.callSubAgent": "Изпратен под-агент", "workflow.toolDisplayName.clearTodos": "Изчистени задачи", "workflow.toolDisplayName.copyDocument": "Копиран документ", diff --git a/locales/bg-BG/notification.json b/locales/bg-BG/notification.json index b835b2c679..c65f316449 100644 --- a/locales/bg-BG/notification.json +++ b/locales/bg-BG/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Достигнат лимит за плащане според използваното хранилище", "video_generation_completed": "Вашето видео \"{{prompt}}\" е готово.", "video_generation_completed_title": "Генерирането на видеото завърши", + "workspace_member_invited": "{{inviterLabel}} ви покани да се присъедините към работното пространство \"{{workspaceName}}\" като {{role}}.", + "workspace_member_invited_title": "Покана за присъединяване към {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} се присъедини към работното пространство \"{{workspaceName}}\" като {{role}}.", "workspace_member_joined_member": "{{memberLabel}} се присъедини към работното пространство \"{{workspaceName}}\" като Член.", "workspace_member_joined_member_title": "Нов член се присъедини към {{workspaceName}}", diff --git a/locales/bg-BG/plugin.json b/locales/bg-BG/plugin.json index 6716084d98..8b10aed399 100644 --- a/locales/bg-BG/plugin.json +++ b/locales/bg-BG/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} параметъра общо", "arguments.title": "Аргументи", + "builtins.codex.apiName.command_execution": "Изпълни команда", + "builtins.codex.apiName.file_change": "Редактиране на файлове", + "builtins.codex.apiName.mcp_tool_call": "Извикване на MCP инструмент", + "builtins.codex.apiName.todo_list": "Актуализиране на задачи", + "builtins.codex.apiName.web_search": "Търсене в интернет", + "builtins.codex.commandExecution.grep": "Търсене", + "builtins.codex.commandExecution.noResults": "Няма резултати", + "builtins.codex.commandExecution.readFile": "Прочети файл", + "builtins.codex.fileChange.editedFiles_one": "Редактиран {{count}} файл", + "builtins.codex.fileChange.editedFiles_other": "Редактирани {{count}} файла", + "builtins.codex.fileChange.editing": "Редактиране на файлове", + "builtins.codex.fileChange.noChanges": "Няма промени във файловете", + "builtins.codex.fileChange.unknownFile": "Неизвестен файл", + "builtins.codex.mcpTool.error": "Грешка", + "builtins.codex.mcpTool.input": "Вход", + "builtins.codex.mcpTool.result": "Резултат", + "builtins.codex.mcpTool.unknownTool": "MCP инструмент", + "builtins.codex.webSearch.query": "Запитване", "builtins.lobe-activator.apiName.activateTools": "Активиране на инструменти", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} не са намерени", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Извличане на налични модели", diff --git a/locales/bg-BG/setting.json b/locales/bg-BG/setting.json index dec9a3f04d..a26ce92e36 100644 --- a/locales/bg-BG/setting.json +++ b/locales/bg-BG/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Генерирането на изображение завърши", "notification.item.storage_overage_cap_reached": "Достигнат лимит за допълнително съхранение", "notification.item.video_generation_completed": "Генерирането на видео завърши", + "notification.item.workspace_member_invited": "Покана за работно пространство", "notification.item.workspace_member_joined": "Нов член се присъедини", "notification.item.workspace_member_removed": "Премахнат от работното пространство", "notification.item.workspace_payment_failed": "Неуспешно плащане за подновяване", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} вече е член на това работно пространство.", "workspace.members.invite.failed": "Неуспешно изпращане на покана", "workspace.members.invite.limitReached": "Това работно пространство може да има до {{limit}} членове. Премахнете член преди да поканите повече.", + "workspace.members.invite.modal.billIncrease": "Вашата сметка ще се увеличи с ${{amount}}/месец.", "workspace.members.invite.modal.cancel": "Отказ", "workspace.members.invite.modal.confirm": "Потвърждение", "workspace.members.invite.modal.description_one": "Вашият екип се разширява! С потвърждение ще поканите 1 нов член на екипа в това работно пространство.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "При щракване върху Надграждане, ще бъдете таксувани ${{fee}}, плюс всички приложими данъци и такси, незабавно и след това всеки месец, докато не отмените. Таксите за места и използване при поискване се уреждат в края на месеца; ако вашето използване надвиши прага за фактуриране по време на цикъл, вашият метод на плащане може да бъде таксуван преди края на цикъла.", "workspace.upgradeModal.continueCta": "Продължете", "workspace.upgradeModal.createTeam": "Създайте работно пространство", + "workspace.upgradeModal.formDescription": "Прегледайте подробностите по-долу и потвърдете надграждането си.", "workspace.upgradeModal.formSubtitle": "Днес се таксува само платформената такса — таксите за места се уреждат в края на месеца.", "workspace.upgradeModal.formTitle": "Надградете {{name}} до Pro", "workspace.upgradeModal.heading": "Надградете работно пространство до Pro", diff --git a/locales/de-DE/chat.json b/locales/de-DE/chat.json index efe45ce8d5..5d9bd25ff0 100644 --- a/locales/de-DE/chat.json +++ b/locales/de-DE/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Gespeicherte Erinnerung", "workflow.toolDisplayName.calculate": "Berechnet", "workflow.toolDisplayName.callAgent": "Agent wurde aufgerufen", + "workflow.toolDisplayName.callMcpTool": "MCP-Tool aufgerufen", "workflow.toolDisplayName.callSubAgent": "Ein Sub-Agent wurde gestartet", "workflow.toolDisplayName.clearTodos": "Todos gelöscht", "workflow.toolDisplayName.copyDocument": "Ein Dokument wurde kopiert", diff --git a/locales/de-DE/notification.json b/locales/de-DE/notification.json index 33e1b3270d..04d5051f7b 100644 --- a/locales/de-DE/notification.json +++ b/locales/de-DE/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Speicher-Limit für nutzungsbasierte Abrechnung erreicht", "video_generation_completed": "Ihr Video „{{prompt}}“ ist fertig.", "video_generation_completed_title": "Videoerstellung abgeschlossen", + "workspace_member_invited": "{{inviterLabel}} hat Sie eingeladen, dem Arbeitsbereich \"{{workspaceName}}\" als {{role}} beizutreten.", + "workspace_member_invited_title": "Einladung zum Beitritt zu {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} ist dem Arbeitsbereich \"{{workspaceName}}\" als {{role}} beigetreten.", "workspace_member_joined_member": "{{memberLabel}} ist dem Arbeitsbereich \"{{workspaceName}}\" als Mitglied beigetreten.", "workspace_member_joined_member_title": "Neues Mitglied ist {{workspaceName}} beigetreten", diff --git a/locales/de-DE/plugin.json b/locales/de-DE/plugin.json index 2655cc8798..6810d43723 100644 --- a/locales/de-DE/plugin.json +++ b/locales/de-DE/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} Parameter insgesamt", "arguments.title": "Argumente", + "builtins.codex.apiName.command_execution": "Befehl ausführen", + "builtins.codex.apiName.file_change": "Dateien bearbeiten", + "builtins.codex.apiName.mcp_tool_call": "MCP-Tool aufrufen", + "builtins.codex.apiName.todo_list": "Aufgaben aktualisieren", + "builtins.codex.apiName.web_search": "Im Web suchen", + "builtins.codex.commandExecution.grep": "Suchen", + "builtins.codex.commandExecution.noResults": "Keine Ergebnisse", + "builtins.codex.commandExecution.readFile": "Datei lesen", + "builtins.codex.fileChange.editedFiles_one": "{{count}} Datei bearbeitet", + "builtins.codex.fileChange.editedFiles_other": "{{count}} Dateien bearbeitet", + "builtins.codex.fileChange.editing": "Dateien bearbeiten", + "builtins.codex.fileChange.noChanges": "Keine Dateiänderungen", + "builtins.codex.fileChange.unknownFile": "Unbekannte Datei", + "builtins.codex.mcpTool.error": "Fehler", + "builtins.codex.mcpTool.input": "Eingabe", + "builtins.codex.mcpTool.result": "Ergebnis", + "builtins.codex.mcpTool.unknownTool": "MCP-Tool", + "builtins.codex.webSearch.query": "Abfrage", "builtins.lobe-activator.apiName.activateTools": "Werkzeuge aktivieren", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} nicht gefunden", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Verfügbare Modelle abrufen", diff --git a/locales/de-DE/setting.json b/locales/de-DE/setting.json index bc0b8a35e1..c8707fd54a 100644 --- a/locales/de-DE/setting.json +++ b/locales/de-DE/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Bilderstellung abgeschlossen", "notification.item.storage_overage_cap_reached": "Speicher-Pay-as-you-go-Limit erreicht", "notification.item.video_generation_completed": "Videoerstellung abgeschlossen", + "notification.item.workspace_member_invited": "Arbeitsbereichseinladung", "notification.item.workspace_member_joined": "Neues Mitglied beigetreten", "notification.item.workspace_member_removed": "Aus Arbeitsbereich entfernt", "notification.item.workspace_payment_failed": "Erneuerungszahlung fehlgeschlagen", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} ist bereits Mitglied dieses Arbeitsbereichs.", "workspace.members.invite.failed": "Einladung konnte nicht gesendet werden", "workspace.members.invite.limitReached": "Dieser Arbeitsbereich kann bis zu {{limit}} Mitglieder haben. Entfernen Sie ein Mitglied, bevor Sie weitere einladen.", + "workspace.members.invite.modal.billIncrease": "Ihre Rechnung wird um ${{amount}}/Monat steigen.", "workspace.members.invite.modal.cancel": "Abbrechen", "workspace.members.invite.modal.confirm": "Bestätigen", "workspace.members.invite.modal.description_one": "Ihr Team wächst! Durch Bestätigen laden Sie 1 neues Teammitglied in diesen Arbeitsbereich ein.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Beim Klicken auf Upgrade wird Ihnen sofort ${{fee}} zuzüglich aller anfallenden Steuern und Gebühren berechnet und dann jeden Monat, bis Sie kündigen. Sitzplatzgebühren und nutzungsabhängige Kosten werden am Monatsende abgerechnet; wenn Ihre Nutzung während eines Zyklus eine Abrechnungsschwelle überschreitet, kann Ihre hinterlegte Zahlungsmethode vor Ende des Zyklus belastet werden.", "workspace.upgradeModal.continueCta": "Weiter", "workspace.upgradeModal.createTeam": "Workspace erstellen", + "workspace.upgradeModal.formDescription": "Überprüfen Sie die unten stehenden Details und bestätigen Sie Ihr Upgrade.", "workspace.upgradeModal.formSubtitle": "Nur die Plattformgebühr wird heute berechnet – Sitzplatzgebühren werden am Monatsende abgerechnet.", "workspace.upgradeModal.formTitle": "Upgrade {{name}} auf Pro", "workspace.upgradeModal.heading": "Workspace auf Pro upgraden", diff --git a/locales/en-US/common.json b/locales/en-US/common.json index bfcb04bc04..92d7073e73 100644 --- a/locales/en-US/common.json +++ b/locales/en-US/common.json @@ -480,9 +480,6 @@ "userPanel.setting": "Settings", "userPanel.upgradePlan": "Upgrade Plan", "userPanel.usages": "Usage", - "userPanel.workspaceCredits": "Workspace Credits", - "userPanel.workspaceSetting": "Workspace Settings", - "userPanel.workspaceUsages": "Workspace Usage", "version": "Version", "zoom": "Zoom" } diff --git a/locales/en-US/notification.json b/locales/en-US/notification.json index 59da6364d7..bd1a1f3be5 100644 --- a/locales/en-US/notification.json +++ b/locales/en-US/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Storage pay-as-you-go cap reached", "video_generation_completed": "Your video \"{{prompt}}\" is ready.", "video_generation_completed_title": "Video generation completed", + "workspace_member_invited": "{{inviterLabel}} invited you to join workspace \"{{workspaceName}}\" as a {{role}}.", + "workspace_member_invited_title": "Invitation to join {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} joined workspace \"{{workspaceName}}\" as a {{role}}.", "workspace_member_joined_member": "{{memberLabel}} joined workspace \"{{workspaceName}}\" as a Member.", "workspace_member_joined_member_title": "New member joined {{workspaceName}}", diff --git a/locales/en-US/setting.json b/locales/en-US/setting.json index ed1bcda253..44871923e8 100644 --- a/locales/en-US/setting.json +++ b/locales/en-US/setting.json @@ -554,6 +554,7 @@ "notification.item.image_generation_completed": "Image generation completed", "notification.item.storage_overage_cap_reached": "Storage pay-as-you-go cap reached", "notification.item.video_generation_completed": "Video generation completed", + "notification.item.workspace_member_invited": "Workspace invitation", "notification.item.workspace_member_joined": "New member joined", "notification.item.workspace_member_removed": "Removed from workspace", "notification.item.workspace_payment_failed": "Renewal payment failed", @@ -1495,7 +1496,7 @@ "workspace.billingPage.plans.modelsHint": "Estimated messages from the shared pool", "workspace.billingPage.plans.modelsTitle": "Featured models", "workspace.billingPage.plans.perMonth": "/ month", - "workspace.billingPage.plans.popularTag": "Popular", + "workspace.billingPage.plans.popularTag": "Recommended", "workspace.billingPage.plans.priceProCaption": "Platform fee · billed monthly", "workspace.billingPage.plans.priceProHeadline": "${{fee}} / mo", "workspace.billingPage.plans.pricingBannerCta": "View pricing", @@ -1801,6 +1802,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} is already a member of this workspace.", "workspace.members.invite.failed": "Failed to send invitation", "workspace.members.invite.limitReached": "This workspace can have up to {{limit}} members. Remove a member before inviting more.", + "workspace.members.invite.modal.billIncrease": " Your bill will increase by ${{amount}}/mo.", "workspace.members.invite.modal.cancel": "Cancel", "workspace.members.invite.modal.confirm": "Confirm", "workspace.members.invite.modal.description_one": "Your team is expanding! By confirming, you will invite 1 new team member to this workspace.", @@ -1905,9 +1907,10 @@ "workspace.switchWorkspace": "Switch workspace", "workspace.upgradeModal.alreadyUpgraded": "Already upgraded", "workspace.upgradeModal.changeWorkspace": "Back", - "workspace.upgradeModal.chargeDisclosure": "Upon clicking Upgrade, you will be charged ${{fee}}, plus any applicable taxes and fees, immediately and then every month, until you cancel. Seat fees and on-demand usage are settled at month-end; if your usage exceeds a billing threshold during a cycle, your payment method on file may be charged before the cycle ends.", + "workspace.upgradeModal.chargeDisclosure": "Clicking Upgrade charges ${{fee}} now, plus any applicable taxes. The subscription renews monthly until you cancel. Seats and on-demand usage are billed at month-end.", "workspace.upgradeModal.continueCta": "Continue", "workspace.upgradeModal.createTeam": "Create workspace", + "workspace.upgradeModal.formDescription": "Review the details below and confirm your upgrade.", "workspace.upgradeModal.formSubtitle": "Only the platform fee is charged today — seat fees are settled at month-end.", "workspace.upgradeModal.formTitle": "Upgrade {{name}} to Pro", "workspace.upgradeModal.heading": "Upgrade a workspace to Pro", @@ -1998,13 +2001,13 @@ "workspace.wizard.step2.createdToast": "Workspace {{name}} created.", "workspace.wizard.step2.details.description": "See what's included in your selected plan.", "workspace.wizard.step2.details.title": "Plan Details", - "workspace.wizard.step2.features.hobby.onDemand": "On-demand usage · AutoTopUp (${{price}}/M)", + "workspace.wizard.step2.features.hobby.onDemand": "On-demand usage · Auto top-up (${{price}} / 1M credits)", "workspace.wizard.step2.features.hobby.share": "Single-owner workspace", "workspace.wizard.step2.features.hobby.solo": "Solo workspace, no member seats", "workspace.wizard.step2.features.hobby.upgradable": "Upgrade anytime to invite members", "workspace.wizard.step2.features.pro.adminControls": "Centralized billing, roles, and audit logs", "workspace.wizard.step2.features.pro.collaboration": "Invite members · share agents and files", - "workspace.wizard.step2.features.pro.onDemand": "On-demand usage · AutoTopUp (${{price}}/M)", + "workspace.wizard.step2.features.pro.onDemand": "On-demand usage · Auto top-up (${{price}} / 1M credits)", "workspace.wizard.step2.features.pro.priorityModels": "Priority premium models", "workspace.wizard.step2.features.pro.support": "Priority email support", "workspace.wizard.step2.freeLimitReached": "You've reached the free workspace limit ({{limit}}). Upgrade to Pro to create more.", diff --git a/locales/es-ES/chat.json b/locales/es-ES/chat.json index fa1b81c171..7a13bd5277 100644 --- a/locales/es-ES/chat.json +++ b/locales/es-ES/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Memoria guardada", "workflow.toolDisplayName.calculate": "Calculado", "workflow.toolDisplayName.callAgent": "Agente llamado", + "workflow.toolDisplayName.callMcpTool": "Herramienta MCP llamada", "workflow.toolDisplayName.callSubAgent": "Subagente despachado", "workflow.toolDisplayName.clearTodos": "Tareas borradas", "workflow.toolDisplayName.copyDocument": "Copió un documento", diff --git a/locales/es-ES/notification.json b/locales/es-ES/notification.json index c50b13b31b..19398a42c7 100644 --- a/locales/es-ES/notification.json +++ b/locales/es-ES/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Límite de pago por uso de almacenamiento alcanzado", "video_generation_completed": "Tu video \"{{prompt}}\" está listo.", "video_generation_completed_title": "Generación de video completada", + "workspace_member_invited": "{{inviterLabel}} te ha invitado a unirte al espacio de trabajo \"{{workspaceName}}\" como {{role}}.", + "workspace_member_invited_title": "Invitación para unirte a {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} se unió al espacio de trabajo \"{{workspaceName}}\" como {{role}}.", "workspace_member_joined_member": "{{memberLabel}} se unió al espacio de trabajo \"{{workspaceName}}\" como Miembro.", "workspace_member_joined_member_title": "Nuevo miembro se unió a {{workspaceName}}", diff --git a/locales/es-ES/plugin.json b/locales/es-ES/plugin.json index 17547206db..0acebf738a 100644 --- a/locales/es-ES/plugin.json +++ b/locales/es-ES/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} parámetros en total", "arguments.title": "Argumentos", + "builtins.codex.apiName.command_execution": "Ejecutar comando", + "builtins.codex.apiName.file_change": "Editar archivos", + "builtins.codex.apiName.mcp_tool_call": "Llamar herramienta MCP", + "builtins.codex.apiName.todo_list": "Actualizar tareas", + "builtins.codex.apiName.web_search": "Buscar en la web", + "builtins.codex.commandExecution.grep": "Buscar", + "builtins.codex.commandExecution.noResults": "Sin resultados", + "builtins.codex.commandExecution.readFile": "Leer archivo", + "builtins.codex.fileChange.editedFiles_one": "Editado {{count}} archivo", + "builtins.codex.fileChange.editedFiles_other": "Editados {{count}} archivos", + "builtins.codex.fileChange.editing": "Editando archivos", + "builtins.codex.fileChange.noChanges": "Sin cambios en los archivos", + "builtins.codex.fileChange.unknownFile": "Archivo desconocido", + "builtins.codex.mcpTool.error": "Error", + "builtins.codex.mcpTool.input": "Entrada", + "builtins.codex.mcpTool.result": "Resultado", + "builtins.codex.mcpTool.unknownTool": "Herramienta MCP", + "builtins.codex.webSearch.query": "Consulta", "builtins.lobe-activator.apiName.activateTools": "Activar Herramientas", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} no encontrado", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Obtener modelos disponibles", diff --git a/locales/es-ES/setting.json b/locales/es-ES/setting.json index a57a49c48d..6377498d07 100644 --- a/locales/es-ES/setting.json +++ b/locales/es-ES/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Generación de imagen completada", "notification.item.storage_overage_cap_reached": "Se alcanzó el límite de almacenamiento por uso adicional", "notification.item.video_generation_completed": "Generación de vídeo completada", + "notification.item.workspace_member_invited": "Invitación al espacio de trabajo", "notification.item.workspace_member_joined": "Nuevo miembro se unió", "notification.item.workspace_member_removed": "Eliminado del espacio de trabajo", "notification.item.workspace_payment_failed": "Error en el pago de renovación", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} ya es miembro de este espacio de trabajo.", "workspace.members.invite.failed": "Error al enviar la invitación", "workspace.members.invite.limitReached": "Este espacio de trabajo puede tener hasta {{limit}} miembros. Elimina un miembro antes de invitar a más.", + "workspace.members.invite.modal.billIncrease": "Tu factura aumentará en ${{amount}}/mes.", "workspace.members.invite.modal.cancel": "Cancelar", "workspace.members.invite.modal.confirm": "Confirmar", "workspace.members.invite.modal.description_one": "¡Tu equipo está creciendo! Al confirmar, invitarás a 1 nuevo miembro del equipo a este espacio de trabajo.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Al hacer clic en Actualizar, se te cobrará ${{fee}}, más cualquier impuesto y tarifa aplicable, inmediatamente y luego cada mes, hasta que canceles. Las tarifas de asiento y el uso bajo demanda se liquidan al final del mes; si tu uso excede un umbral de facturación durante un ciclo, el método de pago registrado puede ser cargado antes de que termine el ciclo.", "workspace.upgradeModal.continueCta": "Continuar", "workspace.upgradeModal.createTeam": "Crear espacio de trabajo", + "workspace.upgradeModal.formDescription": "Revisa los detalles a continuación y confirma tu actualización.", "workspace.upgradeModal.formSubtitle": "Solo se cobra la tarifa de plataforma hoy — las tarifas de asiento se liquidan al final del mes.", "workspace.upgradeModal.formTitle": "Actualizar {{name}} a Pro", "workspace.upgradeModal.heading": "Actualizar un espacio de trabajo a Pro", diff --git a/locales/fa-IR/chat.json b/locales/fa-IR/chat.json index 8ae80f6dd8..cd90136eed 100644 --- a/locales/fa-IR/chat.json +++ b/locales/fa-IR/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "حافظه ذخیره‌شده", "workflow.toolDisplayName.calculate": "محاسبه‌شده", "workflow.toolDisplayName.callAgent": "یک عامل فراخوانی شد", + "workflow.toolDisplayName.callMcpTool": "ابزار MCP فراخوانی شد", "workflow.toolDisplayName.callSubAgent": "زیرعامل ارسال شد", "workflow.toolDisplayName.clearTodos": "وظایف پاک شدند", "workflow.toolDisplayName.copyDocument": "یک سند کپی شد", diff --git a/locales/fa-IR/notification.json b/locales/fa-IR/notification.json index ac83079ae5..b43dd18156 100644 --- a/locales/fa-IR/notification.json +++ b/locales/fa-IR/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "سقف پرداخت به ازای مصرف ذخیره‌سازی رسید", "video_generation_completed": "ویدیوی شما با موضوع «{{prompt}}» آماده است.", "video_generation_completed_title": "تولید ویدیو تکمیل شد", + "workspace_member_invited": "{{inviterLabel}} شما را دعوت کرده است تا به فضای کاری \"{{workspaceName}}\" به عنوان {{role}} بپیوندید.", + "workspace_member_invited_title": "دعوت به پیوستن به {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} به فضای کاری \"{{workspaceName}}\" به عنوان {{role}} پیوست.", "workspace_member_joined_member": "{{memberLabel}} به فضای کاری \"{{workspaceName}}\" به عنوان عضو پیوست.", "workspace_member_joined_member_title": "عضو جدید به {{workspaceName}} پیوست", diff --git a/locales/fa-IR/plugin.json b/locales/fa-IR/plugin.json index ea0145844a..824ea8c401 100644 --- a/locales/fa-IR/plugin.json +++ b/locales/fa-IR/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} پارامتر در مجموع", "arguments.title": "آرگومان‌ها", + "builtins.codex.apiName.command_execution": "اجرای فرمان", + "builtins.codex.apiName.file_change": "ویرایش فایل‌ها", + "builtins.codex.apiName.mcp_tool_call": "فراخوانی ابزار MCP", + "builtins.codex.apiName.todo_list": "به‌روزرسانی وظایف", + "builtins.codex.apiName.web_search": "جستجوی وب", + "builtins.codex.commandExecution.grep": "جستجو", + "builtins.codex.commandExecution.noResults": "بدون نتیجه", + "builtins.codex.commandExecution.readFile": "خواندن فایل", + "builtins.codex.fileChange.editedFiles_one": "{{count}} فایل ویرایش شد", + "builtins.codex.fileChange.editedFiles_other": "{{count}} فایل ویرایش شدند", + "builtins.codex.fileChange.editing": "در حال ویرایش فایل‌ها", + "builtins.codex.fileChange.noChanges": "هیچ تغییری در فایل‌ها", + "builtins.codex.fileChange.unknownFile": "فایل ناشناخته", + "builtins.codex.mcpTool.error": "خطا", + "builtins.codex.mcpTool.input": "ورودی", + "builtins.codex.mcpTool.result": "نتیجه", + "builtins.codex.mcpTool.unknownTool": "ابزار MCP", + "builtins.codex.webSearch.query": "پرس‌وجو", "builtins.lobe-activator.apiName.activateTools": "فعال کردن ابزارها", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} یافت نشد", "builtins.lobe-agent-builder.apiName.getAvailableModels": "دریافت مدل‌های موجود", diff --git a/locales/fa-IR/setting.json b/locales/fa-IR/setting.json index f88bee9baa..1d40b3e513 100644 --- a/locales/fa-IR/setting.json +++ b/locales/fa-IR/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "تولید تصویر با موفقیت انجام شد", "notification.item.storage_overage_cap_reached": "حد پرداخت اضافی ذخیره‌سازی رسید", "notification.item.video_generation_completed": "تولید ویدئو با موفقیت انجام شد", + "notification.item.workspace_member_invited": "دعوت به فضای کاری", "notification.item.workspace_member_joined": "عضو جدید اضافه شد", "notification.item.workspace_member_removed": "از فضای کاری حذف شد", "notification.item.workspace_payment_failed": "پرداخت تمدید ناموفق بود", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} قبلاً عضو این فضای کاری است.", "workspace.members.invite.failed": "ارسال دعوت‌نامه ناموفق بود", "workspace.members.invite.limitReached": "این فضای کاری می‌تواند حداکثر {{limit}} عضو داشته باشد. قبل از دعوت اعضای جدید، یک عضو را حذف کنید.", + "workspace.members.invite.modal.billIncrease": "صورتحساب شما به میزان ${{amount}}/ماه افزایش خواهد یافت.", "workspace.members.invite.modal.cancel": "لغو", "workspace.members.invite.modal.confirm": "تأیید", "workspace.members.invite.modal.description_one": "تیم شما در حال گسترش است! با تأیید، ۱ عضو جدید به این فضای کاری دعوت خواهد شد.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "با کلیک بر روی ارتقا، مبلغ ${{fee}} به همراه مالیات‌ها و هزینه‌های قابل اعمال، بلافاصله و سپس هر ماه تا زمان لغو، از شما دریافت خواهد شد. هزینه‌های صندلی و استفاده بر اساس تقاضا در پایان ماه تسویه می‌شوند؛ اگر استفاده شما در طول یک چرخه از حد آستانه صورتحساب فراتر رود، ممکن است روش پرداخت ثبت‌شده شما قبل از پایان چرخه شارژ شود.", "workspace.upgradeModal.continueCta": "ادامه", "workspace.upgradeModal.createTeam": "ایجاد فضای کاری", + "workspace.upgradeModal.formDescription": "جزئیات زیر را بررسی کنید و ارتقاء خود را تأیید کنید.", "workspace.upgradeModal.formSubtitle": "امروز فقط هزینه پلتفرم دریافت می‌شود — هزینه‌های صندلی در پایان ماه تسویه می‌شوند.", "workspace.upgradeModal.formTitle": "ارتقا {{name}} به نسخه حرفه‌ای", "workspace.upgradeModal.heading": "ارتقا فضای کاری به نسخه حرفه‌ای", diff --git a/locales/fr-FR/chat.json b/locales/fr-FR/chat.json index 43c11465a1..5a8af27db8 100644 --- a/locales/fr-FR/chat.json +++ b/locales/fr-FR/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Mémoire enregistrée", "workflow.toolDisplayName.calculate": "Calculé", "workflow.toolDisplayName.callAgent": "Agent appelé", + "workflow.toolDisplayName.callMcpTool": "Outil MCP appelé", "workflow.toolDisplayName.callSubAgent": "Sous-agent dispatché", "workflow.toolDisplayName.clearTodos": "Tâches effacées", "workflow.toolDisplayName.copyDocument": "Document copié", diff --git a/locales/fr-FR/notification.json b/locales/fr-FR/notification.json index 85eca4983f..cc0c74a1aa 100644 --- a/locales/fr-FR/notification.json +++ b/locales/fr-FR/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Plafond de paiement à l'utilisation pour le stockage atteint", "video_generation_completed": "Votre vidéo « {{prompt}} » est prête.", "video_generation_completed_title": "Génération de vidéo terminée", + "workspace_member_invited": "{{inviterLabel}} vous a invité à rejoindre l'espace de travail \"{{workspaceName}}\" en tant que {{role}}.", + "workspace_member_invited_title": "Invitation à rejoindre {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} a rejoint l'espace de travail \"{{workspaceName}}\" en tant que {{role}}.", "workspace_member_joined_member": "{{memberLabel}} a rejoint l'espace de travail \"{{workspaceName}}\" en tant que Membre.", "workspace_member_joined_member_title": "Nouveau membre a rejoint {{workspaceName}}", diff --git a/locales/fr-FR/plugin.json b/locales/fr-FR/plugin.json index d955966619..f4eb3f98b1 100644 --- a/locales/fr-FR/plugin.json +++ b/locales/fr-FR/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} paramètres au total", "arguments.title": "Arguments", + "builtins.codex.apiName.command_execution": "Exécuter une commande", + "builtins.codex.apiName.file_change": "Modifier des fichiers", + "builtins.codex.apiName.mcp_tool_call": "Appeler l'outil MCP", + "builtins.codex.apiName.todo_list": "Mettre à jour les tâches", + "builtins.codex.apiName.web_search": "Rechercher sur le web", + "builtins.codex.commandExecution.grep": "Rechercher", + "builtins.codex.commandExecution.noResults": "Aucun résultat", + "builtins.codex.commandExecution.readFile": "Lire le fichier", + "builtins.codex.fileChange.editedFiles_one": "{{count}} fichier modifié", + "builtins.codex.fileChange.editedFiles_other": "{{count}} fichiers modifiés", + "builtins.codex.fileChange.editing": "Modification des fichiers", + "builtins.codex.fileChange.noChanges": "Aucune modification de fichier", + "builtins.codex.fileChange.unknownFile": "Fichier inconnu", + "builtins.codex.mcpTool.error": "Erreur", + "builtins.codex.mcpTool.input": "Entrée", + "builtins.codex.mcpTool.result": "Résultat", + "builtins.codex.mcpTool.unknownTool": "Outil MCP", + "builtins.codex.webSearch.query": "Requête", "builtins.lobe-activator.apiName.activateTools": "Activer les Outils", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} introuvable(s)", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Obtenir les modèles disponibles", diff --git a/locales/fr-FR/setting.json b/locales/fr-FR/setting.json index 1a55037056..2dc739d816 100644 --- a/locales/fr-FR/setting.json +++ b/locales/fr-FR/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Génération d’image terminée", "notification.item.storage_overage_cap_reached": "Plafond de dépassement de stockage atteint", "notification.item.video_generation_completed": "Génération de vidéo terminée", + "notification.item.workspace_member_invited": "Invitation à l'espace de travail", "notification.item.workspace_member_joined": "Nouveau membre ajouté", "notification.item.workspace_member_removed": "Supprimé de l'espace de travail", "notification.item.workspace_payment_failed": "Échec du paiement du renouvellement", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} est déjà membre de cet espace de travail.", "workspace.members.invite.failed": "Échec de l'envoi de l'invitation", "workspace.members.invite.limitReached": "Cet espace de travail peut avoir jusqu'à {{limit}} membres. Supprimez un membre avant d'en inviter d'autres.", + "workspace.members.invite.modal.billIncrease": "Votre facture augmentera de ${{amount}}/mois.", "workspace.members.invite.modal.cancel": "Annuler", "workspace.members.invite.modal.confirm": "Confirmer", "workspace.members.invite.modal.description_one": "Votre équipe s'agrandit ! En confirmant, vous inviterez 1 nouveau membre de l'équipe à cet espace de travail.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "En cliquant sur Mettre à niveau, vous serez facturé ${{fee}}, plus les taxes et frais applicables, immédiatement et ensuite chaque mois, jusqu'à annulation. Les frais de siège et l'utilisation à la demande sont réglés en fin de mois ; si votre utilisation dépasse un seuil de facturation pendant un cycle, votre moyen de paiement enregistré peut être facturé avant la fin du cycle.", "workspace.upgradeModal.continueCta": "Continuer", "workspace.upgradeModal.createTeam": "Créer un espace de travail", + "workspace.upgradeModal.formDescription": "Examinez les détails ci-dessous et confirmez votre mise à niveau.", "workspace.upgradeModal.formSubtitle": "Seuls les frais de plateforme sont facturés aujourd'hui — les frais de siège sont réglés en fin de mois.", "workspace.upgradeModal.formTitle": "Mettre à niveau {{name}} vers Pro", "workspace.upgradeModal.heading": "Mettre à niveau un espace de travail vers Pro", diff --git a/locales/it-IT/chat.json b/locales/it-IT/chat.json index 5c0be1b468..146c26edcc 100644 --- a/locales/it-IT/chat.json +++ b/locales/it-IT/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Memoria salvata", "workflow.toolDisplayName.calculate": "Calcolato", "workflow.toolDisplayName.callAgent": "Ha chiamato un agente", + "workflow.toolDisplayName.callMcpTool": "Strumento MCP chiamato", "workflow.toolDisplayName.callSubAgent": "Sotto-agente inviato", "workflow.toolDisplayName.clearTodos": "Attività cancellate", "workflow.toolDisplayName.copyDocument": "Ha copiato un documento", diff --git a/locales/it-IT/notification.json b/locales/it-IT/notification.json index d95e0524e1..693bea9208 100644 --- a/locales/it-IT/notification.json +++ b/locales/it-IT/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Limite di pagamento a consumo per l'archiviazione raggiunto", "video_generation_completed": "Il tuo video \"{{prompt}}\" è pronto.", "video_generation_completed_title": "Generazione video completata", + "workspace_member_invited": "{{inviterLabel}} ti ha invitato a unirti al workspace \"{{workspaceName}}\" come {{role}}.", + "workspace_member_invited_title": "Invito a unirti a {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} si è unito al workspace \"{{workspaceName}}\" come {{role}}.", "workspace_member_joined_member": "{{memberLabel}} si è unito al workspace \"{{workspaceName}}\" come Membro.", "workspace_member_joined_member_title": "Nuovo membro unito a {{workspaceName}}", diff --git a/locales/it-IT/plugin.json b/locales/it-IT/plugin.json index 832f8c6894..a76bd246fa 100644 --- a/locales/it-IT/plugin.json +++ b/locales/it-IT/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} parametri in totale", "arguments.title": "Argomenti", + "builtins.codex.apiName.command_execution": "Esegui comando", + "builtins.codex.apiName.file_change": "Modifica file", + "builtins.codex.apiName.mcp_tool_call": "Chiama strumento MCP", + "builtins.codex.apiName.todo_list": "Aggiorna attività", + "builtins.codex.apiName.web_search": "Cerca sul web", + "builtins.codex.commandExecution.grep": "Cerca", + "builtins.codex.commandExecution.noResults": "Nessun risultato", + "builtins.codex.commandExecution.readFile": "Leggi file", + "builtins.codex.fileChange.editedFiles_one": "Modificato {{count}} file", + "builtins.codex.fileChange.editedFiles_other": "Modificati {{count}} file", + "builtins.codex.fileChange.editing": "Modifica file", + "builtins.codex.fileChange.noChanges": "Nessuna modifica ai file", + "builtins.codex.fileChange.unknownFile": "File sconosciuto", + "builtins.codex.mcpTool.error": "Errore", + "builtins.codex.mcpTool.input": "Input", + "builtins.codex.mcpTool.result": "Risultato", + "builtins.codex.mcpTool.unknownTool": "Strumento MCP", + "builtins.codex.webSearch.query": "Richiesta", "builtins.lobe-activator.apiName.activateTools": "Attiva Strumenti", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} non trovati", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Ottieni modelli disponibili", diff --git a/locales/it-IT/setting.json b/locales/it-IT/setting.json index c20af2839b..3aed696479 100644 --- a/locales/it-IT/setting.json +++ b/locales/it-IT/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Generazione immagine completata", "notification.item.storage_overage_cap_reached": "Raggiunto il limite di spesa per l'archiviazione", "notification.item.video_generation_completed": "Generazione video completata", + "notification.item.workspace_member_invited": "Invito al workspace", "notification.item.workspace_member_joined": "Nuovo membro aggiunto", "notification.item.workspace_member_removed": "Rimosso dallo spazio di lavoro", "notification.item.workspace_payment_failed": "Pagamento per il rinnovo fallito", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} è già un membro di questo workspace.", "workspace.members.invite.failed": "Invio dell'invito non riuscito", "workspace.members.invite.limitReached": "Questo workspace può avere fino a {{limit}} membri. Rimuovi un membro prima di invitarne altri.", + "workspace.members.invite.modal.billIncrease": "La tua fattura aumenterà di ${{amount}}/mese.", "workspace.members.invite.modal.cancel": "Annulla", "workspace.members.invite.modal.confirm": "Conferma", "workspace.members.invite.modal.description_one": "Il tuo team si sta espandendo! Confermando, inviterai 1 nuovo membro al workspace.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Cliccando su Upgrade, ti verrà addebitato ${{fee}}, più eventuali tasse e commissioni applicabili, immediatamente e poi ogni mese, fino a quando non annulli. Le tariffe per i posti e l'utilizzo su richiesta vengono regolate a fine mese; se il tuo utilizzo supera una soglia di fatturazione durante un ciclo, il metodo di pagamento registrato potrebbe essere addebitato prima della fine del ciclo.", "workspace.upgradeModal.continueCta": "Continua", "workspace.upgradeModal.createTeam": "Crea workspace", + "workspace.upgradeModal.formDescription": "Esamina i dettagli qui sotto e conferma il tuo aggiornamento.", "workspace.upgradeModal.formSubtitle": "Oggi viene addebitata solo la tariffa della piattaforma — le tariffe per i posti vengono regolate a fine mese.", "workspace.upgradeModal.formTitle": "Aggiorna {{name}} a Pro", "workspace.upgradeModal.heading": "Aggiorna un workspace a Pro", diff --git a/locales/ja-JP/chat.json b/locales/ja-JP/chat.json index 82c08e11a0..05e7240ca6 100644 --- a/locales/ja-JP/chat.json +++ b/locales/ja-JP/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "保存済みメモリ", "workflow.toolDisplayName.calculate": "計算済み", "workflow.toolDisplayName.callAgent": "エージェントを呼び出しました", + "workflow.toolDisplayName.callMcpTool": "MCPツールを呼び出しました", "workflow.toolDisplayName.callSubAgent": "サブエージェントを派遣しました", "workflow.toolDisplayName.clearTodos": "ToDoをクリアしました", "workflow.toolDisplayName.copyDocument": "ドキュメントをコピーしました", diff --git a/locales/ja-JP/notification.json b/locales/ja-JP/notification.json index afa8660850..a4fa0092bd 100644 --- a/locales/ja-JP/notification.json +++ b/locales/ja-JP/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "ストレージ従量課金制上限に達しました", "video_generation_completed": "動画「{{prompt}}」の生成が完了しました。", "video_generation_completed_title": "動画生成が完了しました", + "workspace_member_invited": "{{inviterLabel}} があなたをワークスペース「{{workspaceName}}」に {{role}} として招待しました。", + "workspace_member_invited_title": "{{workspaceName}} への参加招待", "workspace_member_joined": "{{memberLabel}}が{{role}}としてワークスペース「{{workspaceName}}」に参加しました。", "workspace_member_joined_member": "{{memberLabel}}がメンバーとしてワークスペース「{{workspaceName}}」に参加しました。", "workspace_member_joined_member_title": "新しいメンバーが{{workspaceName}}に参加しました", diff --git a/locales/ja-JP/plugin.json b/locales/ja-JP/plugin.json index 91cb24ad07..f82e6bd026 100644 --- a/locales/ja-JP/plugin.json +++ b/locales/ja-JP/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "合計で{{count}}個のパラメーターがあります", "arguments.title": "パラメーター一覧", + "builtins.codex.apiName.command_execution": "コマンドを実行", + "builtins.codex.apiName.file_change": "ファイルを編集", + "builtins.codex.apiName.mcp_tool_call": "MCPツールを呼び出し", + "builtins.codex.apiName.todo_list": "タスクを更新", + "builtins.codex.apiName.web_search": "ウェブを検索", + "builtins.codex.commandExecution.grep": "検索", + "builtins.codex.commandExecution.noResults": "結果なし", + "builtins.codex.commandExecution.readFile": "ファイルを読む", + "builtins.codex.fileChange.editedFiles_one": "{{count}} 件のファイルを編集しました", + "builtins.codex.fileChange.editedFiles_other": "{{count}} 件のファイルを編集しました", + "builtins.codex.fileChange.editing": "ファイルを編集中", + "builtins.codex.fileChange.noChanges": "ファイルの変更なし", + "builtins.codex.fileChange.unknownFile": "不明なファイル", + "builtins.codex.mcpTool.error": "エラー", + "builtins.codex.mcpTool.input": "入力", + "builtins.codex.mcpTool.result": "結果", + "builtins.codex.mcpTool.unknownTool": "MCPツール", + "builtins.codex.webSearch.query": "クエリ", "builtins.lobe-activator.apiName.activateTools": "ツールをアクティブ化", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} 件が見つかりません", "builtins.lobe-agent-builder.apiName.getAvailableModels": "利用可能なモデルを取得", diff --git a/locales/ja-JP/setting.json b/locales/ja-JP/setting.json index 055bf3e54e..19070762ca 100644 --- a/locales/ja-JP/setting.json +++ b/locales/ja-JP/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "画像生成が完了しました", "notification.item.storage_overage_cap_reached": "ストレージの従量課金上限に到達しました", "notification.item.video_generation_completed": "動画生成が完了しました", + "notification.item.workspace_member_invited": "ワークスペースへの招待", "notification.item.workspace_member_joined": "新しいメンバーが参加しました", "notification.item.workspace_member_removed": "ワークスペースから削除されました", "notification.item.workspace_payment_failed": "更新支払いに失敗しました", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}}はすでにこのワークスペースのメンバーです。", "workspace.members.invite.failed": "招待の送信に失敗しました", "workspace.members.invite.limitReached": "このワークスペースには最大{{limit}}人のメンバーを追加できます。さらに招待する前にメンバーを削除してください。", + "workspace.members.invite.modal.billIncrease": "請求額が${{amount}}/月増加します。", "workspace.members.invite.modal.cancel": "キャンセル", "workspace.members.invite.modal.confirm": "確認", "workspace.members.invite.modal.description_one": "チームが拡大しています!確認すると、このワークスペースに1人の新しいチームメンバーを招待します。", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "「アップグレード」をクリックすると、${{fee}}(適用される税金および手数料を含む)が即時に請求され、その後毎月請求されます。シート料金およびオンデマンド使用料は月末に精算されます。請求サイクル中に使用量が請求閾値を超えた場合、サイクル終了前に登録済みの支払い方法に請求される場合があります。", "workspace.upgradeModal.continueCta": "続行", "workspace.upgradeModal.createTeam": "ワークスペースを作成", + "workspace.upgradeModal.formDescription": "以下の詳細を確認し、アップグレードを確定してください。", "workspace.upgradeModal.formSubtitle": "本日はプラットフォーム料金のみが請求されます。シート料金は月末に精算されます。", "workspace.upgradeModal.formTitle": "{{name}}をProにアップグレード", "workspace.upgradeModal.heading": "ワークスペースをProにアップグレード", diff --git a/locales/ko-KR/chat.json b/locales/ko-KR/chat.json index 400434203b..7bba45f2d6 100644 --- a/locales/ko-KR/chat.json +++ b/locales/ko-KR/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "저장된 메모리", "workflow.toolDisplayName.calculate": "계산됨", "workflow.toolDisplayName.callAgent": "에이전트를 호출했습니다", + "workflow.toolDisplayName.callMcpTool": "MCP 도구 호출됨", "workflow.toolDisplayName.callSubAgent": "하위 에이전트가 디스패치되었습니다", "workflow.toolDisplayName.clearTodos": "할 일 목록이 삭제되었습니다", "workflow.toolDisplayName.copyDocument": "문서를 복사했습니다", diff --git a/locales/ko-KR/notification.json b/locales/ko-KR/notification.json index e16711e8b9..437b813e6a 100644 --- a/locales/ko-KR/notification.json +++ b/locales/ko-KR/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "스토리지 종량제 한도 도달", "video_generation_completed": "동영상 \"{{prompt}}\"이(가) 준비되었습니다.", "video_generation_completed_title": "동영상 생성 완료", + "workspace_member_invited": "{{inviterLabel}}님이 {{workspaceName}} 워크스페이스에 {{role}} 역할로 초대했습니다.", + "workspace_member_invited_title": "{{workspaceName}}에 참여 초대", "workspace_member_joined": "{{memberLabel}}님이 \"{{workspaceName}}\" 워크스페이스에 {{role}}로 참여했습니다.", "workspace_member_joined_member": "{{memberLabel}}님이 \"{{workspaceName}}\" 워크스페이스에 멤버로 참여했습니다.", "workspace_member_joined_member_title": "{{workspaceName}}에 새로운 멤버가 참여했습니다", diff --git a/locales/ko-KR/plugin.json b/locales/ko-KR/plugin.json index 664a6e18af..20016bd9e0 100644 --- a/locales/ko-KR/plugin.json +++ b/locales/ko-KR/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "총 {{count}}개의 매개변수가 있습니다", "arguments.title": "매개변수 목록", + "builtins.codex.apiName.command_execution": "명령 실행", + "builtins.codex.apiName.file_change": "파일 편집", + "builtins.codex.apiName.mcp_tool_call": "MCP 도구 호출", + "builtins.codex.apiName.todo_list": "작업 업데이트", + "builtins.codex.apiName.web_search": "웹 검색", + "builtins.codex.commandExecution.grep": "검색", + "builtins.codex.commandExecution.noResults": "결과 없음", + "builtins.codex.commandExecution.readFile": "파일 읽기", + "builtins.codex.fileChange.editedFiles_one": "{{count}}개의 파일을 편집했습니다", + "builtins.codex.fileChange.editedFiles_other": "{{count}}개의 파일을 편집했습니다", + "builtins.codex.fileChange.editing": "파일 편집 중", + "builtins.codex.fileChange.noChanges": "파일 변경 없음", + "builtins.codex.fileChange.unknownFile": "알 수 없는 파일", + "builtins.codex.mcpTool.error": "오류", + "builtins.codex.mcpTool.input": "입력", + "builtins.codex.mcpTool.result": "결과", + "builtins.codex.mcpTool.unknownTool": "MCP 도구", + "builtins.codex.webSearch.query": "검색어", "builtins.lobe-activator.apiName.activateTools": "도구 활성화", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}}개를 찾을 수 없음", "builtins.lobe-agent-builder.apiName.getAvailableModels": "사용 가능한 모델 가져오기", diff --git a/locales/ko-KR/setting.json b/locales/ko-KR/setting.json index e165dec723..776958e6e5 100644 --- a/locales/ko-KR/setting.json +++ b/locales/ko-KR/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "이미지 생성 완료", "notification.item.storage_overage_cap_reached": "스토리지 초과 사용 한도 도달", "notification.item.video_generation_completed": "동영상 생성 완료", + "notification.item.workspace_member_invited": "워크스페이스 초대", "notification.item.workspace_member_joined": "새 멤버가 가입했습니다", "notification.item.workspace_member_removed": "워크스페이스에서 제거됨", "notification.item.workspace_payment_failed": "갱신 결제 실패", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}}은 이미 이 워크스페이스의 멤버입니다.", "workspace.members.invite.failed": "초대 이메일 전송에 실패했습니다", "workspace.members.invite.limitReached": "이 워크스페이스는 최대 {{limit}}명의 멤버를 가질 수 있습니다. 더 많은 멤버를 초대하기 전에 멤버를 제거하세요.", + "workspace.members.invite.modal.billIncrease": "귀하의 청구 금액이 월 ${{amount}} 증가합니다.", "workspace.members.invite.modal.cancel": "취소", "workspace.members.invite.modal.confirm": "확인", "workspace.members.invite.modal.description_one": "팀이 확장되고 있습니다! 확인하면 이 워크스페이스에 새 팀 멤버 1명을 초대합니다.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "업그레이드 버튼을 클릭하면 ${{fee}}와 적용 가능한 세금 및 수수료가 즉시 청구되며 매월 청구됩니다. 좌석 요금 및 주문형 사용량은 월말에 정산됩니다; 주기 동안 청구 임계값을 초과하면 주기 끝 전에 파일에 있는 결제 방법이 청구될 수 있습니다.", "workspace.upgradeModal.continueCta": "계속", "workspace.upgradeModal.createTeam": "워크스페이스 생성", + "workspace.upgradeModal.formDescription": "아래 세부 정보를 검토하고 업그레이드를 확인하세요.", "workspace.upgradeModal.formSubtitle": "오늘은 플랫폼 요금만 청구됩니다 — 좌석 요금은 월말에 정산됩니다.", "workspace.upgradeModal.formTitle": "{{name}}을 프로로 업그레이드", "workspace.upgradeModal.heading": "워크스페이스를 프로로 업그레이드", diff --git a/locales/nl-NL/chat.json b/locales/nl-NL/chat.json index cc1030973b..bdde89f2d5 100644 --- a/locales/nl-NL/chat.json +++ b/locales/nl-NL/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Opgeslagen geheugen", "workflow.toolDisplayName.calculate": "Berekend", "workflow.toolDisplayName.callAgent": "Agent aangeroepen", + "workflow.toolDisplayName.callMcpTool": "Geroepen MCP-tool", "workflow.toolDisplayName.callSubAgent": "Een sub-agent verzonden", "workflow.toolDisplayName.clearTodos": "Taken gewist", "workflow.toolDisplayName.copyDocument": "Een document gekopieerd", diff --git a/locales/nl-NL/notification.json b/locales/nl-NL/notification.json index 440971c235..30a9042e13 100644 --- a/locales/nl-NL/notification.json +++ b/locales/nl-NL/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Limiet voor opslag op basis van verbruik bereikt", "video_generation_completed": "Uw video \"{{prompt}}\" is klaar.", "video_generation_completed_title": "Videogeneratie voltooid", + "workspace_member_invited": "{{inviterLabel}} heeft je uitgenodigd om lid te worden van de werkruimte \"{{workspaceName}}\" als {{role}}.", + "workspace_member_invited_title": "Uitnodiging om lid te worden van {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} is lid geworden van werkruimte \"{{workspaceName}}\" als {{role}}.", "workspace_member_joined_member": "{{memberLabel}} is lid geworden van werkruimte \"{{workspaceName}}\" als Lid.", "workspace_member_joined_member_title": "Nieuw lid toegevoegd aan {{workspaceName}}", diff --git a/locales/nl-NL/plugin.json b/locales/nl-NL/plugin.json index e15cc7a1cf..44d9d3d060 100644 --- a/locales/nl-NL/plugin.json +++ b/locales/nl-NL/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} parameters in totaal", "arguments.title": "Argumenten", + "builtins.codex.apiName.command_execution": "Voer opdracht uit", + "builtins.codex.apiName.file_change": "Bewerk bestanden", + "builtins.codex.apiName.mcp_tool_call": "Roep MCP-tool aan", + "builtins.codex.apiName.todo_list": "Werk taken bij", + "builtins.codex.apiName.web_search": "Zoek op het web", + "builtins.codex.commandExecution.grep": "Zoeken", + "builtins.codex.commandExecution.noResults": "Geen resultaten", + "builtins.codex.commandExecution.readFile": "Lees bestand", + "builtins.codex.fileChange.editedFiles_one": "{{count}} bestand bewerkt", + "builtins.codex.fileChange.editedFiles_other": "{{count}} bestanden bewerkt", + "builtins.codex.fileChange.editing": "Bestanden bewerken", + "builtins.codex.fileChange.noChanges": "Geen wijzigingen in bestanden", + "builtins.codex.fileChange.unknownFile": "Onbekend bestand", + "builtins.codex.mcpTool.error": "Fout", + "builtins.codex.mcpTool.input": "Invoer", + "builtins.codex.mcpTool.result": "Resultaat", + "builtins.codex.mcpTool.unknownTool": "MCP-tool", + "builtins.codex.webSearch.query": "Zoekopdracht", "builtins.lobe-activator.apiName.activateTools": "Hulpmiddelen Activeren", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} niet gevonden", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Beschikbare modellen ophalen", diff --git a/locales/nl-NL/setting.json b/locales/nl-NL/setting.json index 18104adeb8..2dd06d3c11 100644 --- a/locales/nl-NL/setting.json +++ b/locales/nl-NL/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Afbeeldingsgeneratie voltooid", "notification.item.storage_overage_cap_reached": "Opslaglimiet bereikt", "notification.item.video_generation_completed": "Videogeneratie voltooid", + "notification.item.workspace_member_invited": "Werkruimte-uitnodiging", "notification.item.workspace_member_joined": "Nieuw lid toegevoegd", "notification.item.workspace_member_removed": "Verwijderd uit werkruimte", "notification.item.workspace_payment_failed": "Betaling voor verlenging mislukt", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} is al lid van deze werkruimte.", "workspace.members.invite.failed": "Uitnodiging verzenden mislukt", "workspace.members.invite.limitReached": "Deze werkruimte kan maximaal {{limit}} leden hebben. Verwijder een lid voordat je meer uitnodigt.", + "workspace.members.invite.modal.billIncrease": "Uw rekening zal stijgen met ${{amount}}/maand.", "workspace.members.invite.modal.cancel": "Annuleren", "workspace.members.invite.modal.confirm": "Bevestigen", "workspace.members.invite.modal.description_one": "Je team groeit! Door te bevestigen nodig je 1 nieuw teamlid uit voor deze werkruimte.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Bij het klikken op Upgrade wordt er onmiddellijk ${{fee}} in rekening gebracht, plus eventuele toepasselijke belastingen en kosten, en vervolgens elke maand totdat u annuleert. Stoelkosten en gebruik op aanvraag worden aan het einde van de maand verrekend; als uw gebruik een factureringsdrempel overschrijdt tijdens een cyclus, kan uw betaalmethode vóór het einde van de cyclus worden belast.", "workspace.upgradeModal.continueCta": "Doorgaan", "workspace.upgradeModal.createTeam": "Werkruimte maken", + "workspace.upgradeModal.formDescription": "Bekijk de onderstaande details en bevestig uw upgrade.", "workspace.upgradeModal.formSubtitle": "Alleen de platformkosten worden vandaag in rekening gebracht — stoelkosten worden aan het einde van de maand verrekend.", "workspace.upgradeModal.formTitle": "Upgrade {{name}} naar Pro", "workspace.upgradeModal.heading": "Upgrade een werkruimte naar Pro", diff --git a/locales/pl-PL/chat.json b/locales/pl-PL/chat.json index 731a227d64..38e5bc5ffd 100644 --- a/locales/pl-PL/chat.json +++ b/locales/pl-PL/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Zapisana pamięć", "workflow.toolDisplayName.calculate": "Obliczone", "workflow.toolDisplayName.callAgent": "Wywołano agenta", + "workflow.toolDisplayName.callMcpTool": "Wywołano narzędzie MCP", "workflow.toolDisplayName.callSubAgent": "Wysłano pod-agenta", "workflow.toolDisplayName.clearTodos": "Wyczyszczono zadania", "workflow.toolDisplayName.copyDocument": "Skopiowano dokument", diff --git a/locales/pl-PL/notification.json b/locales/pl-PL/notification.json index 229163660b..48bd9139c0 100644 --- a/locales/pl-PL/notification.json +++ b/locales/pl-PL/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Osiągnięto limit płatności za przechowywanie danych", "video_generation_completed": "Twój film \"{{prompt}}\" jest gotowy.", "video_generation_completed_title": "Generowanie filmu zakończone", + "workspace_member_invited": "{{inviterLabel}} zaprosił(a) Cię do dołączenia do przestrzeni roboczej \"{{workspaceName}}\" jako {{role}}.", + "workspace_member_invited_title": "Zaproszenie do dołączenia do {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} dołączył(a) do przestrzeni roboczej \"{{workspaceName}}\" jako {{role}}.", "workspace_member_joined_member": "{{memberLabel}} dołączył(a) do przestrzeni roboczej \"{{workspaceName}}\" jako Członek.", "workspace_member_joined_member_title": "Nowy członek dołączył do {{workspaceName}}", diff --git a/locales/pl-PL/plugin.json b/locales/pl-PL/plugin.json index 7105c0ac1d..ab15c40e37 100644 --- a/locales/pl-PL/plugin.json +++ b/locales/pl-PL/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "Łącznie {{count}} parametrów", "arguments.title": "Argumenty", + "builtins.codex.apiName.command_execution": "Uruchom polecenie", + "builtins.codex.apiName.file_change": "Edytuj pliki", + "builtins.codex.apiName.mcp_tool_call": "Wywołaj narzędzie MCP", + "builtins.codex.apiName.todo_list": "Aktualizuj zadania", + "builtins.codex.apiName.web_search": "Wyszukaj w sieci", + "builtins.codex.commandExecution.grep": "Szukaj", + "builtins.codex.commandExecution.noResults": "Brak wyników", + "builtins.codex.commandExecution.readFile": "Odczytaj plik", + "builtins.codex.fileChange.editedFiles_one": "Edytowano {{count}} plik", + "builtins.codex.fileChange.editedFiles_other": "Edytowano {{count}} pliki", + "builtins.codex.fileChange.editing": "Edytowanie plików", + "builtins.codex.fileChange.noChanges": "Brak zmian w plikach", + "builtins.codex.fileChange.unknownFile": "Nieznany plik", + "builtins.codex.mcpTool.error": "Błąd", + "builtins.codex.mcpTool.input": "Wejście", + "builtins.codex.mcpTool.result": "Wynik", + "builtins.codex.mcpTool.unknownTool": "Narzędzie MCP", + "builtins.codex.webSearch.query": "Zapytanie", "builtins.lobe-activator.apiName.activateTools": "Aktywuj Narzędzia", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} nie znaleziono", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Pobierz dostępne modele", diff --git a/locales/pl-PL/setting.json b/locales/pl-PL/setting.json index 0acbc51451..5eacdcf627 100644 --- a/locales/pl-PL/setting.json +++ b/locales/pl-PL/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Generowanie obrazu zakończone", "notification.item.storage_overage_cap_reached": "Osiągnięto limit płatności za nadmiarowe przechowywanie", "notification.item.video_generation_completed": "Generowanie wideo zakończone", + "notification.item.workspace_member_invited": "Zaproszenie do przestrzeni roboczej", "notification.item.workspace_member_joined": "Nowy członek dołączył", "notification.item.workspace_member_removed": "Usunięto z przestrzeni roboczej", "notification.item.workspace_payment_failed": "Nieudana płatność za odnowienie", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} jest już członkiem tego workspace.", "workspace.members.invite.failed": "Nie udało się wysłać zaproszenia", "workspace.members.invite.limitReached": "To miejsce pracy może mieć maksymalnie {{limit}} członków. Usuń członka, zanim zaprosisz więcej.", + "workspace.members.invite.modal.billIncrease": "Twój rachunek wzrośnie o ${{amount}}/mies.", "workspace.members.invite.modal.cancel": "Anuluj", "workspace.members.invite.modal.confirm": "Potwierdź", "workspace.members.invite.modal.description_one": "Twój zespół się powiększa! Potwierdzając, zaprosisz 1 nowego członka zespołu do tego miejsca pracy.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Klikając Upgrade, zostaniesz obciążony kwotą ${{fee}}, plus wszelkie obowiązujące podatki i opłaty, natychmiast, a następnie co miesiąc, aż do anulowania. Opłaty za miejsca i użycie na żądanie są rozliczane na koniec miesiąca; jeśli Twoje użycie przekroczy próg rozliczeniowy w trakcie cyklu, Twoja metoda płatności może zostać obciążona przed końcem cyklu.", "workspace.upgradeModal.continueCta": "Kontynuuj", "workspace.upgradeModal.createTeam": "Utwórz przestrzeń roboczą", + "workspace.upgradeModal.formDescription": "Przejrzyj poniższe szczegóły i potwierdź swoją aktualizację.", "workspace.upgradeModal.formSubtitle": "Dziś pobierana jest tylko opłata platformowa — opłaty za miejsca są rozliczane na koniec miesiąca.", "workspace.upgradeModal.formTitle": "Ulepsz {{name}} do wersji Pro", "workspace.upgradeModal.heading": "Ulepsz przestrzeń roboczą do wersji Pro", diff --git a/locales/pt-BR/chat.json b/locales/pt-BR/chat.json index 77bb54c3b9..efe7806721 100644 --- a/locales/pt-BR/chat.json +++ b/locales/pt-BR/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Memória salva", "workflow.toolDisplayName.calculate": "Calculado", "workflow.toolDisplayName.callAgent": "Chamou um agente", + "workflow.toolDisplayName.callMcpTool": "Ferramenta MCP chamada", "workflow.toolDisplayName.callSubAgent": "Subagente despachado", "workflow.toolDisplayName.clearTodos": "Tarefas apagadas", "workflow.toolDisplayName.copyDocument": "Copiou um documento", diff --git a/locales/pt-BR/notification.json b/locales/pt-BR/notification.json index 6a427f142a..5c93a9b15d 100644 --- a/locales/pt-BR/notification.json +++ b/locales/pt-BR/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Limite de pagamento por uso do armazenamento atingido", "video_generation_completed": "Seu vídeo \"{{prompt}}\" está pronto.", "video_generation_completed_title": "Geração de vídeo concluída", + "workspace_member_invited": "{{inviterLabel}} convidou você para ingressar no espaço de trabalho \"{{workspaceName}}\" como {{role}}.", + "workspace_member_invited_title": "Convite para ingressar no {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} entrou no espaço de trabalho \"{{workspaceName}}\" como {{role}}.", "workspace_member_joined_member": "{{memberLabel}} entrou no espaço de trabalho \"{{workspaceName}}\" como Membro.", "workspace_member_joined_member_title": "Novo membro entrou em {{workspaceName}}", diff --git a/locales/pt-BR/plugin.json b/locales/pt-BR/plugin.json index cafe16a270..2cce619ecd 100644 --- a/locales/pt-BR/plugin.json +++ b/locales/pt-BR/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "{{count}} parâmetros no total", "arguments.title": "Argumentos", + "builtins.codex.apiName.command_execution": "Executar comando", + "builtins.codex.apiName.file_change": "Editar arquivos", + "builtins.codex.apiName.mcp_tool_call": "Chamar ferramenta MCP", + "builtins.codex.apiName.todo_list": "Atualizar tarefas", + "builtins.codex.apiName.web_search": "Pesquisar na web", + "builtins.codex.commandExecution.grep": "Pesquisar", + "builtins.codex.commandExecution.noResults": "Sem resultados", + "builtins.codex.commandExecution.readFile": "Ler arquivo", + "builtins.codex.fileChange.editedFiles_one": "Editado {{count}} arquivo", + "builtins.codex.fileChange.editedFiles_other": "Editados {{count}} arquivos", + "builtins.codex.fileChange.editing": "Editando arquivos", + "builtins.codex.fileChange.noChanges": "Sem alterações nos arquivos", + "builtins.codex.fileChange.unknownFile": "Arquivo desconhecido", + "builtins.codex.mcpTool.error": "Erro", + "builtins.codex.mcpTool.input": "Entrada", + "builtins.codex.mcpTool.result": "Resultado", + "builtins.codex.mcpTool.unknownTool": "Ferramenta MCP", + "builtins.codex.webSearch.query": "Consulta", "builtins.lobe-activator.apiName.activateTools": "Ativar Ferramentas", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} não encontrado", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Obter modelos disponíveis", diff --git a/locales/pt-BR/setting.json b/locales/pt-BR/setting.json index 449c82ffdf..c9e71c6662 100644 --- a/locales/pt-BR/setting.json +++ b/locales/pt-BR/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Geração de imagem concluída", "notification.item.storage_overage_cap_reached": "Limite de armazenamento excedido", "notification.item.video_generation_completed": "Geração de vídeo concluída", + "notification.item.workspace_member_invited": "Convite para espaço de trabalho", "notification.item.workspace_member_joined": "Novo membro entrou", "notification.item.workspace_member_removed": "Removido do espaço de trabalho", "notification.item.workspace_payment_failed": "Pagamento de renovação falhou", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} já é um membro deste workspace.", "workspace.members.invite.failed": "Falha ao enviar o convite", "workspace.members.invite.limitReached": "Este workspace pode ter até {{limit}} membros. Remova um membro antes de convidar mais.", + "workspace.members.invite.modal.billIncrease": "Sua conta aumentará em ${{amount}}/mês.", "workspace.members.invite.modal.cancel": "Cancelar", "workspace.members.invite.modal.confirm": "Confirmar", "workspace.members.invite.modal.description_one": "Sua equipe está crescendo! Ao confirmar, você convidará 1 novo membro para este workspace.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Ao clicar em Atualizar, você será cobrado ${{fee}}, mais quaisquer impostos e taxas aplicáveis, imediatamente e, em seguida, todos os meses, até que você cancele. As taxas por assento e o uso sob demanda são liquidados no final do mês; se o seu uso exceder um limite de faturamento durante um ciclo, seu método de pagamento registrado poderá ser cobrado antes do final do ciclo.", "workspace.upgradeModal.continueCta": "Continuar", "workspace.upgradeModal.createTeam": "Criar workspace", + "workspace.upgradeModal.formDescription": "Revise os detalhes abaixo e confirme sua atualização.", "workspace.upgradeModal.formSubtitle": "Apenas a taxa da plataforma será cobrada hoje — as taxas por assento são liquidadas no final do mês.", "workspace.upgradeModal.formTitle": "Atualizar {{name}} para Pro", "workspace.upgradeModal.heading": "Atualizar um workspace para Pro", diff --git a/locales/ru-RU/chat.json b/locales/ru-RU/chat.json index 22fc33b54d..62d0ebe5ac 100644 --- a/locales/ru-RU/chat.json +++ b/locales/ru-RU/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Сохранённая память", "workflow.toolDisplayName.calculate": "Рассчитано", "workflow.toolDisplayName.callAgent": "Вызван агент", + "workflow.toolDisplayName.callMcpTool": "Вызван инструмент MCP", "workflow.toolDisplayName.callSubAgent": "Вызван подагент", "workflow.toolDisplayName.clearTodos": "Задачи очищены", "workflow.toolDisplayName.copyDocument": "Документ скопирован", diff --git a/locales/ru-RU/notification.json b/locales/ru-RU/notification.json index bdbb35acf1..c1cc631247 100644 --- a/locales/ru-RU/notification.json +++ b/locales/ru-RU/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Достигнут лимит оплаты за превышение хранилища", "video_generation_completed": "Ваше видео «{{prompt}}» готово.", "video_generation_completed_title": "Создание видео завершено", + "workspace_member_invited": "{{inviterLabel}} пригласил вас присоединиться к рабочей области \"{{workspaceName}}\" в качестве {{role}}.", + "workspace_member_invited_title": "Приглашение присоединиться к {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} присоединился к рабочей области \"{{workspaceName}}\" в роли {{role}}.", "workspace_member_joined_member": "{{memberLabel}} присоединился к рабочей области \"{{workspaceName}}\" в роли Участника.", "workspace_member_joined_member_title": "Новый участник присоединился к {{workspaceName}}", diff --git a/locales/ru-RU/plugin.json b/locales/ru-RU/plugin.json index 7a00e938c6..e3813e6cb9 100644 --- a/locales/ru-RU/plugin.json +++ b/locales/ru-RU/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "Всего параметров: {{count}}", "arguments.title": "Аргументы", + "builtins.codex.apiName.command_execution": "Выполнить команду", + "builtins.codex.apiName.file_change": "Редактировать файлы", + "builtins.codex.apiName.mcp_tool_call": "Вызвать инструмент MCP", + "builtins.codex.apiName.todo_list": "Обновить задачи", + "builtins.codex.apiName.web_search": "Искать в интернете", + "builtins.codex.commandExecution.grep": "Поиск", + "builtins.codex.commandExecution.noResults": "Нет результатов", + "builtins.codex.commandExecution.readFile": "Читать файл", + "builtins.codex.fileChange.editedFiles_one": "Отредактирован {{count}} файл", + "builtins.codex.fileChange.editedFiles_other": "Отредактировано {{count}} файлов", + "builtins.codex.fileChange.editing": "Редактирование файлов", + "builtins.codex.fileChange.noChanges": "Нет изменений файлов", + "builtins.codex.fileChange.unknownFile": "Неизвестный файл", + "builtins.codex.mcpTool.error": "Ошибка", + "builtins.codex.mcpTool.input": "Ввод", + "builtins.codex.mcpTool.result": "Результат", + "builtins.codex.mcpTool.unknownTool": "Инструмент MCP", + "builtins.codex.webSearch.query": "Запрос", "builtins.lobe-activator.apiName.activateTools": "Активировать инструменты", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} не найдено", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Получить доступные модели", diff --git a/locales/ru-RU/setting.json b/locales/ru-RU/setting.json index 6a15a306f6..1ca5d3d30f 100644 --- a/locales/ru-RU/setting.json +++ b/locales/ru-RU/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Генерация изображения завершена", "notification.item.storage_overage_cap_reached": "Достигнут лимит оплаты за превышение хранилища", "notification.item.video_generation_completed": "Генерация видео завершена", + "notification.item.workspace_member_invited": "Приглашение в рабочую область", "notification.item.workspace_member_joined": "Новый участник присоединился", "notification.item.workspace_member_removed": "Удален из рабочей области", "notification.item.workspace_payment_failed": "Не удалось выполнить оплату за продление", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} уже является участником этой рабочей области.", "workspace.members.invite.failed": "Не удалось отправить приглашение", "workspace.members.invite.limitReached": "Эта рабочая область может иметь до {{limit}} участников. Удалите участника перед приглашением новых.", + "workspace.members.invite.modal.billIncrease": "Ваш счет увеличится на ${{amount}}/мес.", "workspace.members.invite.modal.cancel": "Отмена", "workspace.members.invite.modal.confirm": "Подтвердить", "workspace.members.invite.modal.description_one": "Ваша команда расширяется! Подтвердив, вы пригласите 1 нового участника команды в эту рабочую область.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Нажав «Обновить», вы будете немедленно и затем ежемесячно, пока не отмените, оплачивать ${{fee}}, плюс любые применимые налоги и сборы. Плата за места и использование по запросу рассчитывается в конце месяца; если ваше использование превысит порог оплаты в течение цикла, ваш способ оплаты может быть списан до окончания цикла.", "workspace.upgradeModal.continueCta": "Продолжить", "workspace.upgradeModal.createTeam": "Создать рабочую область", + "workspace.upgradeModal.formDescription": "Просмотрите детали ниже и подтвердите обновление.", "workspace.upgradeModal.formSubtitle": "Сегодня взимается только плата за платформу — плата за места рассчитывается в конце месяца.", "workspace.upgradeModal.formTitle": "Обновить {{name}} до Pro", "workspace.upgradeModal.heading": "Обновить рабочую область до Pro", diff --git a/locales/tr-TR/chat.json b/locales/tr-TR/chat.json index 684c3b7de4..2465c3a3ea 100644 --- a/locales/tr-TR/chat.json +++ b/locales/tr-TR/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Kaydedilmiş bellek", "workflow.toolDisplayName.calculate": "Hesaplandı", "workflow.toolDisplayName.callAgent": "Bir temsilci çağrıldı", + "workflow.toolDisplayName.callMcpTool": "MCP aracını çağırdı", "workflow.toolDisplayName.callSubAgent": "Bir alt ajan gönderildi", "workflow.toolDisplayName.clearTodos": "Yapılacaklar temizlendi", "workflow.toolDisplayName.copyDocument": "Bir belge kopyalandı", diff --git a/locales/tr-TR/notification.json b/locales/tr-TR/notification.json index b8208dda7c..a16809af66 100644 --- a/locales/tr-TR/notification.json +++ b/locales/tr-TR/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Depolama kullanımına göre ödeme sınırına ulaşıldı", "video_generation_completed": "\"{{prompt}}\" adlı videonuz hazır.", "video_generation_completed_title": "Video oluşturma tamamlandı", + "workspace_member_invited": "{{inviterLabel}}, \"{{workspaceName}}\" çalışma alanına {{role}} olarak katılmanız için sizi davet etti.", + "workspace_member_invited_title": "{{workspaceName}} çalışma alanına katılma daveti", "workspace_member_joined": "{{memberLabel}}, \"{{workspaceName}}\" çalışma alanına {{role}} olarak katıldı.", "workspace_member_joined_member": "{{memberLabel}}, \"{{workspaceName}}\" çalışma alanına Üye olarak katıldı.", "workspace_member_joined_member_title": "{{workspaceName}} çalışma alanına yeni bir üye katıldı", diff --git a/locales/tr-TR/plugin.json b/locales/tr-TR/plugin.json index 58ae6179c5..4724385b27 100644 --- a/locales/tr-TR/plugin.json +++ b/locales/tr-TR/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "Toplam {{count}} parametre", "arguments.title": "Argümanlar", + "builtins.codex.apiName.command_execution": "Komut çalıştır", + "builtins.codex.apiName.file_change": "Dosyaları düzenle", + "builtins.codex.apiName.mcp_tool_call": "MCP aracını çağır", + "builtins.codex.apiName.todo_list": "Görevleri güncelle", + "builtins.codex.apiName.web_search": "Web'de ara", + "builtins.codex.commandExecution.grep": "Ara", + "builtins.codex.commandExecution.noResults": "Sonuç yok", + "builtins.codex.commandExecution.readFile": "Dosyayı oku", + "builtins.codex.fileChange.editedFiles_one": "{{count}} dosya düzenlendi", + "builtins.codex.fileChange.editedFiles_other": "{{count}} dosya düzenlendi", + "builtins.codex.fileChange.editing": "Dosyalar düzenleniyor", + "builtins.codex.fileChange.noChanges": "Dosya değişikliği yok", + "builtins.codex.fileChange.unknownFile": "Bilinmeyen dosya", + "builtins.codex.mcpTool.error": "Hata", + "builtins.codex.mcpTool.input": "Girdi", + "builtins.codex.mcpTool.result": "Sonuç", + "builtins.codex.mcpTool.unknownTool": "MCP aracı", + "builtins.codex.webSearch.query": "Sorgu", "builtins.lobe-activator.apiName.activateTools": "Araçları Etkinleştir", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} bulunamadı", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Mevcut modelleri al", diff --git a/locales/tr-TR/setting.json b/locales/tr-TR/setting.json index 26294fea86..6729ad6371 100644 --- a/locales/tr-TR/setting.json +++ b/locales/tr-TR/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Görüntü oluşturma tamamlandı", "notification.item.storage_overage_cap_reached": "Depolama aşım sınırı aşıldı", "notification.item.video_generation_completed": "Video oluşturma tamamlandı", + "notification.item.workspace_member_invited": "Çalışma alanı daveti", "notification.item.workspace_member_joined": "Yeni üye katıldı", "notification.item.workspace_member_removed": "Çalışma alanından kaldırıldı", "notification.item.workspace_payment_failed": "Yenileme ödemesi başarısız oldu", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} zaten bu çalışma alanının bir üyesi.", "workspace.members.invite.failed": "Davet gönderilemedi", "workspace.members.invite.limitReached": "Bu çalışma alanında en fazla {{limit}} üye olabilir. Daha fazla davet etmeden önce bir üyeyi kaldırın.", + "workspace.members.invite.modal.billIncrease": "Faturanız aylık ${{amount}} artacaktır.", "workspace.members.invite.modal.cancel": "İptal", "workspace.members.invite.modal.confirm": "Onayla", "workspace.members.invite.modal.description_one": "Ekibiniz büyüyor! Onaylayarak, bu çalışma alanına 1 yeni ekip üyesi davet edeceksiniz.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Yükselt'e tıkladığınızda, hemen ve ardından her ay iptal edene kadar ${{fee}} + geçerli vergiler ve ücretler tahsil edilecektir. Koltuk ücretleri ve isteğe bağlı kullanım ay sonunda hesaplanır; bir döngü sırasında faturalandırma eşiğini aşarsanız, döngü bitmeden önce kayıtlı ödeme yönteminizden ücret alınabilir.", "workspace.upgradeModal.continueCta": "Devam et", "workspace.upgradeModal.createTeam": "Çalışma alanı oluştur", + "workspace.upgradeModal.formDescription": "Aşağıdaki detayları inceleyin ve yükseltmenizi onaylayın.", "workspace.upgradeModal.formSubtitle": "Bugün yalnızca platform ücreti tahsil edilir — koltuk ücretleri ay sonunda hesaplanır.", "workspace.upgradeModal.formTitle": "{{name}}'i Pro'ya yükselt", "workspace.upgradeModal.heading": "Bir çalışma alanını Pro'ya yükselt", diff --git a/locales/vi-VN/chat.json b/locales/vi-VN/chat.json index 8f53e8ced8..ec4938f187 100644 --- a/locales/vi-VN/chat.json +++ b/locales/vi-VN/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "Bộ nhớ đã lưu", "workflow.toolDisplayName.calculate": "Đã tính toán", "workflow.toolDisplayName.callAgent": "Đã gọi một tác nhân", + "workflow.toolDisplayName.callMcpTool": "Đã gọi công cụ MCP", "workflow.toolDisplayName.callSubAgent": "Đã phân phối một tác nhân phụ", "workflow.toolDisplayName.clearTodos": "Đã xóa danh sách việc cần làm", "workflow.toolDisplayName.copyDocument": "Đã sao chép một tài liệu", diff --git a/locales/vi-VN/notification.json b/locales/vi-VN/notification.json index e207361fbd..874aebfb66 100644 --- a/locales/vi-VN/notification.json +++ b/locales/vi-VN/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "Đã đạt giới hạn thanh toán theo mức sử dụng cho lưu trữ", "video_generation_completed": "Video \"{{prompt}}\" của bạn đã sẵn sàng.", "video_generation_completed_title": "Hoàn tất tạo video", + "workspace_member_invited": "{{inviterLabel}} đã mời bạn tham gia không gian làm việc \"{{workspaceName}}\" với vai trò {{role}}.", + "workspace_member_invited_title": "Lời mời tham gia {{workspaceName}}", "workspace_member_joined": "{{memberLabel}} đã tham gia không gian làm việc \"{{workspaceName}}\" với vai trò {{role}}.", "workspace_member_joined_member": "{{memberLabel}} đã tham gia không gian làm việc \"{{workspaceName}}\" với vai trò Thành viên.", "workspace_member_joined_member_title": "Thành viên mới đã tham gia {{workspaceName}}", diff --git a/locales/vi-VN/plugin.json b/locales/vi-VN/plugin.json index bad36be4e6..711c285670 100644 --- a/locales/vi-VN/plugin.json +++ b/locales/vi-VN/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "Tổng cộng có {{count}} tham số", "arguments.title": "Tham số", + "builtins.codex.apiName.command_execution": "Chạy lệnh", + "builtins.codex.apiName.file_change": "Chỉnh sửa tệp", + "builtins.codex.apiName.mcp_tool_call": "Gọi công cụ MCP", + "builtins.codex.apiName.todo_list": "Cập nhật nhiệm vụ", + "builtins.codex.apiName.web_search": "Tìm kiếm trên web", + "builtins.codex.commandExecution.grep": "Tìm kiếm", + "builtins.codex.commandExecution.noResults": "Không có kết quả", + "builtins.codex.commandExecution.readFile": "Đọc tệp", + "builtins.codex.fileChange.editedFiles_one": "Đã chỉnh sửa {{count}} tệp", + "builtins.codex.fileChange.editedFiles_other": "Đã chỉnh sửa {{count}} tệp", + "builtins.codex.fileChange.editing": "Đang chỉnh sửa tệp", + "builtins.codex.fileChange.noChanges": "Không có thay đổi tệp", + "builtins.codex.fileChange.unknownFile": "Tệp không xác định", + "builtins.codex.mcpTool.error": "Lỗi", + "builtins.codex.mcpTool.input": "Đầu vào", + "builtins.codex.mcpTool.result": "Kết quả", + "builtins.codex.mcpTool.unknownTool": "Công cụ MCP", + "builtins.codex.webSearch.query": "Truy vấn", "builtins.lobe-activator.apiName.activateTools": "Kích hoạt Công cụ", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} không tìm thấy", "builtins.lobe-agent-builder.apiName.getAvailableModels": "Lấy mô hình khả dụng", diff --git a/locales/vi-VN/setting.json b/locales/vi-VN/setting.json index f56dacc8e1..85daf183b5 100644 --- a/locales/vi-VN/setting.json +++ b/locales/vi-VN/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "Tạo ảnh hoàn tất", "notification.item.storage_overage_cap_reached": "Đã đạt giới hạn chi tiêu lưu trữ", "notification.item.video_generation_completed": "Tạo video hoàn tất", + "notification.item.workspace_member_invited": "Lời mời tham gia không gian làm việc", "notification.item.workspace_member_joined": "Thành viên mới đã tham gia", "notification.item.workspace_member_removed": "Đã bị xóa khỏi không gian làm việc", "notification.item.workspace_payment_failed": "Gia hạn thanh toán thất bại", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} đã là thành viên của không gian làm việc này.", "workspace.members.invite.failed": "Không thể gửi lời mời", "workspace.members.invite.limitReached": "Không gian làm việc này chỉ cho phép tối đa {{limit}} thành viên. Hãy xóa một thành viên trước khi mời thêm.", + "workspace.members.invite.modal.billIncrease": "Hóa đơn của bạn sẽ tăng thêm ${{amount}}/tháng.", "workspace.members.invite.modal.cancel": "Hủy", "workspace.members.invite.modal.confirm": "Xác nhận", "workspace.members.invite.modal.description_one": "Đội ngũ của bạn đang mở rộng! Bằng cách xác nhận, bạn sẽ mời 1 thành viên mới vào không gian làm việc này.", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "Khi nhấn Nâng cấp, bạn sẽ bị tính phí ${{fee}}, cộng với bất kỳ thuế và phí áp dụng nào, ngay lập tức và sau đó hàng tháng, cho đến khi bạn hủy. Phí ghế ngồi và sử dụng theo yêu cầu sẽ được thanh toán vào cuối tháng; nếu mức sử dụng của bạn vượt ngưỡng thanh toán trong một chu kỳ, phương thức thanh toán của bạn có thể bị tính phí trước khi chu kỳ kết thúc.", "workspace.upgradeModal.continueCta": "Tiếp tục", "workspace.upgradeModal.createTeam": "Tạo không gian làm việc", + "workspace.upgradeModal.formDescription": "Xem lại các chi tiết bên dưới và xác nhận nâng cấp của bạn.", "workspace.upgradeModal.formSubtitle": "Chỉ phí nền tảng được tính hôm nay — phí ghế ngồi sẽ được thanh toán vào cuối tháng.", "workspace.upgradeModal.formTitle": "Nâng cấp {{name}} lên Pro", "workspace.upgradeModal.heading": "Nâng cấp không gian làm việc lên Pro", diff --git a/locales/zh-CN/common.json b/locales/zh-CN/common.json index 3595707f71..80b644452e 100644 --- a/locales/zh-CN/common.json +++ b/locales/zh-CN/common.json @@ -480,9 +480,6 @@ "userPanel.setting": "应用设置", "userPanel.upgradePlan": "升级套餐", "userPanel.usages": "用量", - "userPanel.workspaceCredits": "空间积分", - "userPanel.workspaceSetting": "空间设置", - "userPanel.workspaceUsages": "空间用量", "version": "版本", "zoom": "缩放" } diff --git a/locales/zh-CN/notification.json b/locales/zh-CN/notification.json index af40ac0589..fc632249d7 100644 --- a/locales/zh-CN/notification.json +++ b/locales/zh-CN/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "存储按量付费上限已触达", "video_generation_completed": "视频「{{prompt}}」已生成。", "video_generation_completed_title": "视频生成完成", + "workspace_member_invited": "{{inviterLabel}} 邀请您以 {{role}} 的身份加入工作区 \"{{workspaceName}}\"。", + "workspace_member_invited_title": "加入 {{workspaceName}} 的邀请", "workspace_member_joined": "{{memberLabel}} 已加入工作区 \"{{workspaceName}}\",角色为 {{role}}。", "workspace_member_joined_member": "{{memberLabel}} 已加入工作区 \"{{workspaceName}}\",角色为成员。", "workspace_member_joined_member_title": "新成员加入了 {{workspaceName}}", diff --git a/locales/zh-CN/setting.json b/locales/zh-CN/setting.json index a6efbcd6a3..6fae2ab896 100644 --- a/locales/zh-CN/setting.json +++ b/locales/zh-CN/setting.json @@ -554,6 +554,7 @@ "notification.item.image_generation_completed": "图片生成完成", "notification.item.storage_overage_cap_reached": "存储按量付费上限已触达", "notification.item.video_generation_completed": "视频生成完成", + "notification.item.workspace_member_invited": "工作区邀请", "notification.item.workspace_member_joined": "新成员加入", "notification.item.workspace_member_removed": "已从工作区移除", "notification.item.workspace_payment_failed": "续订付款失败", @@ -1495,7 +1496,7 @@ "workspace.billingPage.plans.modelsHint": "共享池中的估计消息", "workspace.billingPage.plans.modelsTitle": "推荐模型", "workspace.billingPage.plans.perMonth": "/ 月", - "workspace.billingPage.plans.popularTag": "热门", + "workspace.billingPage.plans.popularTag": "推荐", "workspace.billingPage.plans.priceProCaption": "平台费用 · 每月计费", "workspace.billingPage.plans.priceProHeadline": "${{fee}} / 月", "workspace.billingPage.plans.pricingBannerCta": "查看定价", @@ -1801,6 +1802,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} 已是此工作区的成员。", "workspace.members.invite.failed": "发送邀请失败", "workspace.members.invite.limitReached": "此工作区最多可有 {{limit}} 名成员。请移除一名成员后再邀请更多。", + "workspace.members.invite.modal.billIncrease": "您的账单将每月增加 ${{amount}}。", "workspace.members.invite.modal.cancel": "取消", "workspace.members.invite.modal.confirm": "确认", "workspace.members.invite.modal.description_one": "您的团队正在扩展!确认后,您将邀请 1 名新团队成员加入此工作区。", @@ -1905,9 +1907,10 @@ "workspace.switchWorkspace": "切换工作区", "workspace.upgradeModal.alreadyUpgraded": "已升级", "workspace.upgradeModal.changeWorkspace": "返回", - "workspace.upgradeModal.chargeDisclosure": "点击升级后,您将立即支付 ${{fee}},加上任何适用的税费,并每月支付,直到您取消。座位费用和按需使用将在月底结算;如果您的使用量在周期内超过计费阈值,您的存档付款方式可能会在周期结束前收费。", + "workspace.upgradeModal.chargeDisclosure": "点击升级将立即支付 ${{fee}},加上任何适用税费。订阅每月续订,直到您取消。座位和按需使用将在月底结算。", "workspace.upgradeModal.continueCta": "继续", "workspace.upgradeModal.createTeam": "创建工作区", + "workspace.upgradeModal.formDescription": "请确认以下费用信息后完成升级。", "workspace.upgradeModal.formSubtitle": "今天仅收取平台费用——座位费用将在月底结算。", "workspace.upgradeModal.formTitle": "将 {{name}} 升级为专业版", "workspace.upgradeModal.heading": "将工作区升级为专业版", @@ -1998,19 +2001,19 @@ "workspace.wizard.step2.createdToast": "工作区 {{name}} 已创建。", "workspace.wizard.step2.details.description": "查看您选择的计划中包含的内容。", "workspace.wizard.step2.details.title": "计划详情", - "workspace.wizard.step2.features.hobby.onDemand": "按需使用 · 自动充值 (${{price}}/M)", + "workspace.wizard.step2.features.hobby.onDemand": "按需使用 · 自动充值(${{price}} / 百万积分)", "workspace.wizard.step2.features.hobby.share": "单所有者工作区", "workspace.wizard.step2.features.hobby.solo": "单人工作区,无成员座位", "workspace.wizard.step2.features.hobby.upgradable": "随时升级以邀请成员", "workspace.wizard.step2.features.pro.adminControls": "集中计费、角色和审计日志", "workspace.wizard.step2.features.pro.collaboration": "邀请成员 · 共享代理和文件", - "workspace.wizard.step2.features.pro.onDemand": "按需使用 · 自动充值 (${{price}}/M)", + "workspace.wizard.step2.features.pro.onDemand": "按需使用 · 自动充值(${{price}} / 百万积分)", "workspace.wizard.step2.features.pro.priorityModels": "优先高级模型", "workspace.wizard.step2.features.pro.support": "优先电子邮件支持", "workspace.wizard.step2.freeLimitReached": "您已达到免费工作区限制 ({{limit}})。升级到专业版以创建更多。", "workspace.wizard.step2.header.description": "每个工作区单独计费。", "workspace.wizard.step2.header.title": "选择您的计划", - "workspace.wizard.step2.hobbyAgreement": "爱好版免费创建,不包含月度积分。充值或自动充值仅在您确认后计费。", + "workspace.wizard.step2.hobbyAgreement": "Hobby 免费创建,不含每月积分;仅在您确认充值或开启自动充值后才会计费。", "workspace.wizard.step2.left.creditsHobbyHint": "无月度积分 · 按需支付", "workspace.wizard.step2.left.creditsLabel": "每月积分", "workspace.wizard.step2.left.creditsProHint": "共享工作区池 · 座位不会增加积分", diff --git a/locales/zh-CN/subscription.json b/locales/zh-CN/subscription.json index 2eff912d80..87987bb205 100644 --- a/locales/zh-CN/subscription.json +++ b/locales/zh-CN/subscription.json @@ -338,7 +338,7 @@ "plans.workspace.maxMembers": "最多 {{count}} 名成员", "plans.workspace.noSharedCredits": "无共享积分", "plans.workspace.sharedCredits": "~{{count}} 积分 / 月", - "plans.workspace.solo": "个人版 (1 名成员)", + "plans.workspace.solo": "单人工作区 (1 名成员)", "promoBanner.fableYearly": "年付订阅用户限时享 {{percent}}% 用量优惠", "qa.desc": "如果您的问题未被解答,请查看 <1>产品文档 获取更多常见问题,或联系我们。", "qa.detail": "查看详情", diff --git a/locales/zh-TW/chat.json b/locales/zh-TW/chat.json index 03895a73e9..5c03620005 100644 --- a/locales/zh-TW/chat.json +++ b/locales/zh-TW/chat.json @@ -915,6 +915,7 @@ "workflow.toolDisplayName.addPreferenceMemory": "已儲存的記憶", "workflow.toolDisplayName.calculate": "已計算", "workflow.toolDisplayName.callAgent": "已呼叫代理程式", + "workflow.toolDisplayName.callMcpTool": "呼叫 MCP 工具", "workflow.toolDisplayName.callSubAgent": "已調度子代理", "workflow.toolDisplayName.clearTodos": "已清除待辦事項", "workflow.toolDisplayName.copyDocument": "已複製文件", diff --git a/locales/zh-TW/notification.json b/locales/zh-TW/notification.json index 0d451b8685..0a5184acf3 100644 --- a/locales/zh-TW/notification.json +++ b/locales/zh-TW/notification.json @@ -17,6 +17,8 @@ "storage_overage_cap_reached_title": "儲存空間按使用量付費上限已達到", "video_generation_completed": "您的影片「{{prompt}}」已完成。", "video_generation_completed_title": "影片生成完成", + "workspace_member_invited": "{{inviterLabel}} 邀請您以 {{role}} 身份加入工作空間「{{workspaceName}}」。", + "workspace_member_invited_title": "加入 {{workspaceName}} 的邀請", "workspace_member_joined": "{{memberLabel}} 已加入工作區「{{workspaceName}}」,角色為 {{role}}。", "workspace_member_joined_member": "{{memberLabel}} 已加入工作區「{{workspaceName}}」,角色為成員。", "workspace_member_joined_member_title": "新成員加入 {{workspaceName}}", diff --git a/locales/zh-TW/plugin.json b/locales/zh-TW/plugin.json index f5f50bbc2b..91357c681b 100644 --- a/locales/zh-TW/plugin.json +++ b/locales/zh-TW/plugin.json @@ -1,6 +1,24 @@ { "arguments.moreParams": "總共有 {{count}} 個參數", "arguments.title": "參數清單", + "builtins.codex.apiName.command_execution": "執行指令", + "builtins.codex.apiName.file_change": "編輯檔案", + "builtins.codex.apiName.mcp_tool_call": "呼叫MCP工具", + "builtins.codex.apiName.todo_list": "更新任務", + "builtins.codex.apiName.web_search": "搜尋網頁", + "builtins.codex.commandExecution.grep": "搜尋", + "builtins.codex.commandExecution.noResults": "無結果", + "builtins.codex.commandExecution.readFile": "讀取檔案", + "builtins.codex.fileChange.editedFiles_one": "已編輯 {{count}} 個檔案", + "builtins.codex.fileChange.editedFiles_other": "已編輯 {{count}} 個檔案", + "builtins.codex.fileChange.editing": "正在編輯檔案", + "builtins.codex.fileChange.noChanges": "無檔案變更", + "builtins.codex.fileChange.unknownFile": "未知檔案", + "builtins.codex.mcpTool.error": "錯誤", + "builtins.codex.mcpTool.input": "輸入", + "builtins.codex.mcpTool.result": "結果", + "builtins.codex.mcpTool.unknownTool": "MCP工具", + "builtins.codex.webSearch.query": "查詢", "builtins.lobe-activator.apiName.activateTools": "啟用工具", "builtins.lobe-activator.inspector.activateTools.notFoundCount": "{{count}} 未找到", "builtins.lobe-agent-builder.apiName.getAvailableModels": "取得可用模型", diff --git a/locales/zh-TW/setting.json b/locales/zh-TW/setting.json index ecc3c94db3..1844579d0e 100644 --- a/locales/zh-TW/setting.json +++ b/locales/zh-TW/setting.json @@ -522,6 +522,7 @@ "notification.item.image_generation_completed": "圖片生成完成", "notification.item.storage_overage_cap_reached": "已達到存儲按需付費上限", "notification.item.video_generation_completed": "影片生成完成", + "notification.item.workspace_member_invited": "工作區邀請", "notification.item.workspace_member_joined": "新成員加入", "notification.item.workspace_member_removed": "已從工作區移除", "notification.item.workspace_payment_failed": "續訂付款失敗", @@ -1766,6 +1767,7 @@ "workspace.members.invite.errors.alreadyMember": "{{email}} 已是此工作區的成員。", "workspace.members.invite.failed": "發送邀請失敗", "workspace.members.invite.limitReached": "此工作區最多可有 {{limit}} 名成員。在邀請更多之前移除一名成員。", + "workspace.members.invite.modal.billIncrease": "您的帳單將增加 ${{amount}}/月。", "workspace.members.invite.modal.cancel": "取消", "workspace.members.invite.modal.confirm": "確認", "workspace.members.invite.modal.description_one": "您的團隊正在擴展!確認後,您將邀請 1 名新團隊成員加入此工作區。", @@ -1873,6 +1875,7 @@ "workspace.upgradeModal.chargeDisclosure": "點擊升級後,您將立即支付 ${{fee}},加上任何適用的稅費,然後每月支付,直到您取消。座位費用和按需使用在月末結算;如果您的使用量在週期內超過計費門檻,檔案中的付款方式可能會在週期結束前被收費。", "workspace.upgradeModal.continueCta": "繼續", "workspace.upgradeModal.createTeam": "創建工作區", + "workspace.upgradeModal.formDescription": "請檢視以下詳細資訊並確認您的升級。", "workspace.upgradeModal.formSubtitle": "今天僅收取平台費用——座位費用在月末結算。", "workspace.upgradeModal.formTitle": "將 {{name}} 升級至專業", "workspace.upgradeModal.heading": "將工作區升級至專業", diff --git a/package.json b/package.json index 5c58d328ce..8114d5f8a9 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,6 @@ "build:vercel": "cross-env-shell NODE_OPTIONS=--max-old-space-size=8192 \"bun run build:raw && bun run db:migrate\"", "build-migrate-db": "bun run db:migrate", "clean:node_modules": "bash -lc 'set -e; echo \"Removing all node_modules...\"; rm -rf node_modules; pnpm -r exec rm -rf node_modules; rm -rf apps/desktop/node_modules; echo \"All node_modules removed.\"'", - "codemod:workspace-nav": "tsx ./scripts/codemodWorkspaceNav.ts", - "codemod:workspace-nav:check": "tsx ./scripts/codemodWorkspaceNav.ts --check", "db:generate": "drizzle-kit generate && npm run workflow:dbml", "db:migrate": "cross-env MIGRATION_DB=1 tsx ./scripts/migrateServerDB/index.ts", "db:studio": "drizzle-kit studio", diff --git a/packages/database/src/models/verifyCheckResult.ts b/packages/database/src/models/verifyCheckResult.ts index 1e9fe229bc..371f519381 100644 --- a/packages/database/src/models/verifyCheckResult.ts +++ b/packages/database/src/models/verifyCheckResult.ts @@ -3,37 +3,47 @@ import { and, asc, eq, inArray, isNull } from 'drizzle-orm'; import type { NewVerifyCheckResult, VerifyCheckResultItem } from '../schemas/verify'; import { verifyCheckResults } from '../schemas/verify'; import type { LobeChatDatabase } from '../type'; +import { buildWorkspacePayload, buildWorkspaceWhere } from '../utils/workspace'; export class VerifyCheckResultModel { private readonly db: LobeChatDatabase; private readonly userId: string; + private readonly workspaceId?: string; - constructor(db: LobeChatDatabase, userId: string) { + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { this.db = db; this.userId = userId; + this.workspaceId = workspaceId; } - create = async (params: Omit) => { + private ownership = () => + buildWorkspaceWhere({ userId: this.userId, workspaceId: this.workspaceId }, verifyCheckResults); + + create = async (params: Omit) => { const [result] = await this.db .insert(verifyCheckResults) - .values({ ...params, userId: this.userId }) + .values(buildWorkspacePayload({ userId: this.userId, workspaceId: this.workspaceId }, params)) .returning(); return result; }; /** Batch-insert the initial `pending` rows when verify execution starts. */ - createMany = async (rows: Omit[]) => { + createMany = async (rows: Omit[]) => { if (rows.length === 0) return []; return this.db .insert(verifyCheckResults) - .values(rows.map((r) => ({ ...r, userId: this.userId }))) + .values( + rows.map((r) => + buildWorkspacePayload({ userId: this.userId, workspaceId: this.workspaceId }, r), + ), + ) .returning(); }; findById = async (id: string) => { return this.db.query.verifyCheckResults.findFirst({ - where: and(eq(verifyCheckResults.id, id), eq(verifyCheckResults.userId, this.userId)), + where: and(eq(verifyCheckResults.id, id), this.ownership()), }); }; @@ -42,12 +52,7 @@ export class VerifyCheckResultModel { return this.db .select() .from(verifyCheckResults) - .where( - and( - eq(verifyCheckResults.operationId, operationId), - eq(verifyCheckResults.userId, this.userId), - ), - ) + .where(and(eq(verifyCheckResults.operationId, operationId), this.ownership())) .orderBy(asc(verifyCheckResults.checkItemIndex)); }; @@ -55,7 +60,7 @@ export class VerifyCheckResultModel { return this.db .update(verifyCheckResults) .set(value) - .where(and(eq(verifyCheckResults.id, id), eq(verifyCheckResults.userId, this.userId))); + .where(and(eq(verifyCheckResults.id, id), this.ownership())); }; /** @@ -75,7 +80,7 @@ export class VerifyCheckResultModel { and( eq(verifyCheckResults.operationId, operationId), eq(verifyCheckResults.checkItemId, checkItemId), - eq(verifyCheckResults.userId, this.userId), + this.ownership(), ), ); }; @@ -95,7 +100,7 @@ export class VerifyCheckResultModel { .where( and( eq(verifyCheckResults.operationId, operationId), - eq(verifyCheckResults.userId, this.userId), + this.ownership(), inArray(verifyCheckResults.checkItemId, checkItemIds), isNull(verifyCheckResults.verifierTracingId), ), diff --git a/packages/database/src/models/verifyCriterion.ts b/packages/database/src/models/verifyCriterion.ts index f8bcc52983..7eaa7b5458 100644 --- a/packages/database/src/models/verifyCriterion.ts +++ b/packages/database/src/models/verifyCriterion.ts @@ -3,61 +3,66 @@ import { and, desc, eq, inArray } from 'drizzle-orm'; import type { NewVerifyCriterion, VerifyCriterionItem } from '../schemas/verify'; import { verifyCriteria } from '../schemas/verify'; import type { LobeChatDatabase } from '../type'; +import { buildWorkspacePayload, buildWorkspaceWhere } from '../utils/workspace'; export class VerifyCriterionModel { private readonly db: LobeChatDatabase; private readonly userId: string; + private readonly workspaceId?: string; - constructor(db: LobeChatDatabase, userId: string) { + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { this.db = db; this.userId = userId; + this.workspaceId = workspaceId; } - create = async (params: Omit) => { + private ownership = () => + buildWorkspaceWhere({ userId: this.userId, workspaceId: this.workspaceId }, verifyCriteria); + + create = async (params: Omit) => { const [result] = await this.db .insert(verifyCriteria) - .values({ ...params, userId: this.userId }) + .values(buildWorkspacePayload({ userId: this.userId, workspaceId: this.workspaceId }, params)) .returning(); return result; }; delete = async (id: string) => { - return this.db - .delete(verifyCriteria) - .where(and(eq(verifyCriteria.id, id), eq(verifyCriteria.userId, this.userId))); + return this.db.delete(verifyCriteria).where(and(eq(verifyCriteria.id, id), this.ownership())); }; query = async () => { return this.db.query.verifyCriteria.findMany({ orderBy: [desc(verifyCriteria.updatedAt)], - where: eq(verifyCriteria.userId, this.userId), + where: this.ownership(), }); }; findById = async (id: string) => { return this.db.query.verifyCriteria.findFirst({ - where: and(eq(verifyCriteria.id, id), eq(verifyCriteria.userId, this.userId)), + where: and(eq(verifyCriteria.id, id), this.ownership()), }); }; /** * Resolve a set of criterion ids into their current definitions. Used by the * plan generator to instantiate ad-hoc `verifyCriteriaIds` mounted on an agent. - * Always scoped by `userId` so a leaked id can't pull another user's criterion. + * Scoped to the active workspace (or personal scope) so a leaked id can't pull + * another tenant's criterion. */ findByIds = async (ids: string[]): Promise => { if (ids.length === 0) return []; return this.db .select() .from(verifyCriteria) - .where(and(inArray(verifyCriteria.id, ids), eq(verifyCriteria.userId, this.userId))); + .where(and(inArray(verifyCriteria.id, ids), this.ownership())); }; update = async (id: string, value: Partial>) => { return this.db .update(verifyCriteria) .set({ ...value, updatedAt: new Date() }) - .where(and(eq(verifyCriteria.id, id), eq(verifyCriteria.userId, this.userId))); + .where(and(eq(verifyCriteria.id, id), this.ownership())); }; } diff --git a/packages/database/src/models/verifyRubric.ts b/packages/database/src/models/verifyRubric.ts index 52e9a0a9a2..5deacf1de9 100644 --- a/packages/database/src/models/verifyRubric.ts +++ b/packages/database/src/models/verifyRubric.ts @@ -3,6 +3,7 @@ import { and, asc, desc, eq } from 'drizzle-orm'; import type { NewVerifyRubric, VerifyCriterionItem, VerifyRubricItem } from '../schemas/verify'; import { verifyCriteria, verifyRubricCriteria, verifyRubrics } from '../schemas/verify'; import type { LobeChatDatabase } from '../type'; +import { buildWorkspacePayload, buildWorkspaceWhere } from '../utils/workspace'; export interface RubricCriterionInput { criterionId: string; @@ -12,16 +13,27 @@ export interface RubricCriterionInput { export class VerifyRubricModel { private readonly db: LobeChatDatabase; private readonly userId: string; + private readonly workspaceId?: string; - constructor(db: LobeChatDatabase, userId: string) { + constructor(db: LobeChatDatabase, userId: string, workspaceId?: string) { this.db = db; this.userId = userId; + this.workspaceId = workspaceId; } - create = async (params: Omit) => { + private rubricOwnership = () => + buildWorkspaceWhere({ userId: this.userId, workspaceId: this.workspaceId }, verifyRubrics); + + private criteriaOwnership = () => + buildWorkspaceWhere( + { userId: this.userId, workspaceId: this.workspaceId }, + verifyRubricCriteria, + ); + + create = async (params: Omit) => { const [result] = await this.db .insert(verifyRubrics) - .values({ ...params, userId: this.userId }) + .values(buildWorkspacePayload({ userId: this.userId, workspaceId: this.workspaceId }, params)) .returning(); return result; @@ -31,19 +43,19 @@ export class VerifyRubricModel { // verify_rubric_criteria rows cascade via FK onDelete: 'cascade'. return this.db .delete(verifyRubrics) - .where(and(eq(verifyRubrics.id, id), eq(verifyRubrics.userId, this.userId))); + .where(and(eq(verifyRubrics.id, id), this.rubricOwnership())); }; query = async () => { return this.db.query.verifyRubrics.findMany({ orderBy: [desc(verifyRubrics.updatedAt)], - where: eq(verifyRubrics.userId, this.userId), + where: this.rubricOwnership(), }); }; findById = async (id: string) => { return this.db.query.verifyRubrics.findFirst({ - where: and(eq(verifyRubrics.id, id), eq(verifyRubrics.userId, this.userId)), + where: and(eq(verifyRubrics.id, id), this.rubricOwnership()), }); }; @@ -51,25 +63,20 @@ export class VerifyRubricModel { return this.db .update(verifyRubrics) .set({ ...value, updatedAt: new Date() }) - .where(and(eq(verifyRubrics.id, id), eq(verifyRubrics.userId, this.userId))); + .where(and(eq(verifyRubrics.id, id), this.rubricOwnership())); }; /** * Resolve a rubric into its current criterion definitions, ordered by the * junction `sortOrder`. Used by the plan generator to instantiate the rubric - * mounted on an agent. Scoped by `userId`. + * mounted on an agent. Scoped to the active workspace (or personal scope). */ getCriteria = async (rubricId: string): Promise => { const rows = await this.db .select({ criterion: verifyCriteria }) .from(verifyRubricCriteria) .innerJoin(verifyCriteria, eq(verifyRubricCriteria.criterionId, verifyCriteria.id)) - .where( - and( - eq(verifyRubricCriteria.rubricId, rubricId), - eq(verifyRubricCriteria.userId, this.userId), - ), - ) + .where(and(eq(verifyRubricCriteria.rubricId, rubricId), this.criteriaOwnership())) .orderBy(asc(verifyRubricCriteria.sortOrder)); return rows.map((r) => r.criterion); @@ -82,22 +89,21 @@ export class VerifyRubricModel { setCriteria = async (rubricId: string, criteria: RubricCriterionInput[]) => { await this.db .delete(verifyRubricCriteria) - .where( - and( - eq(verifyRubricCriteria.rubricId, rubricId), - eq(verifyRubricCriteria.userId, this.userId), - ), - ); + .where(and(eq(verifyRubricCriteria.rubricId, rubricId), this.criteriaOwnership())); if (criteria.length === 0) return; await this.db.insert(verifyRubricCriteria).values( - criteria.map((c, index) => ({ - criterionId: c.criterionId, - rubricId, - sortOrder: c.sortOrder ?? index, - userId: this.userId, - })), + criteria.map((c, index) => + buildWorkspacePayload( + { userId: this.userId, workspaceId: this.workspaceId }, + { + criterionId: c.criterionId, + rubricId, + sortOrder: c.sortOrder ?? index, + }, + ), + ), ); }; } diff --git a/packages/locales/src/default/common.ts b/packages/locales/src/default/common.ts index 03811695ef..8d463de426 100644 --- a/packages/locales/src/default/common.ts +++ b/packages/locales/src/default/common.ts @@ -562,9 +562,6 @@ export default { 'userPanel.setting': 'Settings', 'userPanel.upgradePlan': 'Upgrade Plan', 'userPanel.usages': 'Usage', - 'userPanel.workspaceCredits': 'Workspace Credits', - 'userPanel.workspaceSetting': 'Workspace Settings', - 'userPanel.workspaceUsages': 'Workspace Usage', 'version': 'Version', 'zoom': 'Zoom', }; diff --git a/packages/locales/src/default/notification.ts b/packages/locales/src/default/notification.ts index 25682836d3..72544115ce 100644 --- a/packages/locales/src/default/notification.ts +++ b/packages/locales/src/default/notification.ts @@ -20,6 +20,9 @@ export default { 'storage_overage_cap_reached_title': 'Storage pay-as-you-go cap reached', 'video_generation_completed': 'Your video "{{prompt}}" is ready.', 'video_generation_completed_title': 'Video generation completed', + 'workspace_member_invited': + '{{inviterLabel}} invited you to join workspace "{{workspaceName}}" as a {{role}}.', + 'workspace_member_invited_title': 'Invitation to join {{workspaceName}}', 'workspace_member_joined': '{{memberLabel}} joined workspace "{{workspaceName}}" as a {{role}}.', 'workspace_member_joined_member': '{{memberLabel}} joined workspace "{{workspaceName}}" as a Member.', diff --git a/packages/locales/src/default/setting.ts b/packages/locales/src/default/setting.ts index 3cc67b74a5..fd91faf2d8 100644 --- a/packages/locales/src/default/setting.ts +++ b/packages/locales/src/default/setting.ts @@ -608,6 +608,7 @@ export default { 'notification.item.image_generation_completed': 'Image generation completed', 'notification.item.storage_overage_cap_reached': 'Storage pay-as-you-go cap reached', 'notification.item.video_generation_completed': 'Video generation completed', + 'notification.item.workspace_member_invited': 'Workspace invitation', 'notification.item.workspace_member_joined': 'New member joined', 'notification.item.workspace_member_removed': 'Removed from workspace', 'notification.item.workspace_payment_failed': 'Renewal payment failed', @@ -1519,7 +1520,7 @@ When I am ___, I need ___ 'workspace.billingPage.plans.modelsHint': 'Estimated messages from the shared pool', 'workspace.billingPage.plans.modelsTitle': 'Featured models', 'workspace.billingPage.plans.perMonth': '/ month', - 'workspace.billingPage.plans.popularTag': 'Popular', + 'workspace.billingPage.plans.popularTag': 'Recommended', 'workspace.billingPage.plans.priceProCaption': 'Platform fee · billed monthly', 'workspace.billingPage.plans.priceProHeadline': '${{fee}} / mo', 'workspace.billingPage.plans.proCapacity': 'Up to {{max}} seats · ${{seatFee}}/seat / month', @@ -1898,6 +1899,7 @@ When I am ___, I need ___ 'Add new members by entering their email address and assigning a role', 'workspace.members.invite.addAnother': 'Add another', 'workspace.members.invite.button': 'Invite', + 'workspace.members.invite.modal.billIncrease': ' Your bill will increase by ${{amount}}/mo.', 'workspace.members.invite.modal.cancel': 'Cancel', 'workspace.members.invite.modal.confirm': 'Confirm', 'workspace.members.invite.modal.description_one': @@ -1934,7 +1936,7 @@ When I am ___, I need ___ 'workspace.upgradeModal.alreadyUpgraded': 'Already upgraded', 'workspace.upgradeModal.changeWorkspace': 'Back', 'workspace.upgradeModal.chargeDisclosure': - 'Upon clicking Upgrade, you will be charged ${{fee}}, plus any applicable taxes and fees, immediately and then every month, until you cancel. Seat fees and on-demand usage are settled at month-end; if your usage exceeds a billing threshold during a cycle, your payment method on file may be charged before the cycle ends.', + 'Clicking Upgrade charges ${{fee}} now, plus any applicable taxes. The subscription renews monthly until you cancel. Seats and on-demand usage are billed at month-end.', 'workspace.upgradeModal.inviteLaterHint': 'You can invite more members to your team in the next step.', 'workspace.upgradeModal.memberCount_one': '{{count}} member', @@ -1947,6 +1949,7 @@ When I am ___, I need ___ 'workspace.upgradeModal.createTeam': 'Create workspace', 'workspace.upgradeModal.formSubtitle': 'Only the platform fee is charged today — seat fees are settled at month-end.', + 'workspace.upgradeModal.formDescription': 'Review the details below and confirm your upgrade.', 'workspace.upgradeModal.formTitle': 'Upgrade {{name}} to Pro', 'workspace.upgradeModal.heading': 'Upgrade a workspace to Pro', 'workspace.upgradeModal.hobbyTag': 'Hobby', @@ -2046,11 +2049,13 @@ When I am ___, I need ___ "You've reached the maximum of {{limit}} workspaces. Leave one before creating another.", 'workspace.wizard.step2.features.hobby.share': 'Single-owner workspace', 'workspace.wizard.step2.features.hobby.solo': 'Solo workspace, no member seats', - 'workspace.wizard.step2.features.hobby.onDemand': 'On-demand usage · AutoTopUp (${{price}}/M)', + 'workspace.wizard.step2.features.hobby.onDemand': + 'On-demand usage · Auto top-up (${{price}} / 1M credits)', 'workspace.wizard.step2.features.hobby.upgradable': 'Upgrade anytime to invite members', 'workspace.wizard.step2.features.pro.adminControls': 'Centralized billing, roles, and audit logs', 'workspace.wizard.step2.features.pro.collaboration': 'Invite members · share agents and files', - 'workspace.wizard.step2.features.pro.onDemand': 'On-demand usage · AutoTopUp (${{price}}/M)', + 'workspace.wizard.step2.features.pro.onDemand': + 'On-demand usage · Auto top-up (${{price}} / 1M credits)', 'workspace.wizard.step2.features.pro.priorityModels': 'Priority premium models', 'workspace.wizard.step2.features.pro.support': 'Priority email support', 'workspace.wizard.step2.left.creditsHobbyHint': 'No monthly credits · pay as you go', diff --git a/packages/types/src/user/settings/tool.ts b/packages/types/src/user/settings/tool.ts index 467fccc96b..618d1b9890 100644 --- a/packages/types/src/user/settings/tool.ts +++ b/packages/types/src/user/settings/tool.ts @@ -6,6 +6,16 @@ export interface UserToolConfig { * List of builtin tool identifiers that have been uninstalled by the user. * By default, all builtin tools are enabled. Users can explicitly * uninstall tools they don't want to use. + * + * This is the personal-context list (no active workspace). Workspace-scoped + * lists are kept separately in `uninstalledBuiltinToolsByWorkspace` so a + * workspace never inherits the user's personal customization. */ uninstalledBuiltinTools?: string[]; + /** + * Per-workspace uninstalled builtin tool lists, keyed by workspace id. + * A workspace with no entry falls back to the default seed (i.e. a clean + * default state), not the user's personal `uninstalledBuiltinTools`. + */ + uninstalledBuiltinToolsByWorkspace?: Record; } diff --git a/scripts/codemodWorkspaceNav.ts b/scripts/codemodWorkspaceNav.ts deleted file mode 100644 index 2fa135bd1a..0000000000 --- a/scripts/codemodWorkspaceNav.ts +++ /dev/null @@ -1,371 +0,0 @@ -#!/usr/bin/env bun -/** - * Codemod: rewrite in-app callsites to be workspace-aware. - * - * useNavigate (from 'react-router-dom') → useWorkspaceAwareNavigate - * - * - * Idempotent. Re-run after rebasing the lobehub submodule onto upstream canary - * to re-apply Step B workspace-aware navigation patches. - * - * Strategy: - * - Scope: lobehub/src/{features,routes,hooks} excluding tests, the router - * configs themselves, and the Workspace feature folder. - * - For each file, collect every `navigate('/...')` literal and every - * `` callsites individually. - * - If the file has only personal-only navigate targets → skip entirely - * (the file is correct as-is). - * - Otherwise rewrite `useNavigate` → `useWorkspaceAwareNavigate` and add - * the appropriate import. - * - * Run: - * bun run scripts/codemodWorkspaceNav.ts # apply - * bun run scripts/codemodWorkspaceNav.ts --dry # report only - * bun run scripts/codemodWorkspaceNav.ts --check # exit 1 if would change - */ - -import { readdir, readFile, stat, writeFile } from 'node:fs/promises'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const ROOT = path.resolve(__dirname, '..'); - -const SCAN_ROOTS = ['src/features', 'src/routes', 'src/hooks']; - -const EXCLUDE_DIR_NAMES = new Set(['__tests__', '__mocks__', 'node_modules']); - -// Files whose pathname matches these substrings are skipped. -const EXCLUDE_PATH_SUBSTRINGS = ['/spa/router/', '/features/Workspace/']; - -const EXCLUDE_FILE_SUFFIXES = ['.test.ts', '.test.tsx', '.spec.ts', '.spec.tsx', '.d.ts']; - -// Top-level personal-only routes. `/settings` is handled separately by the -// shared-tabs allowlist below so workspace-mirrored sub-paths (general, plans, -// billing, …) get auto-prefixed while truly personal sub-paths (profile, llm, -// referral, system-tools, workspace-*) stay personal. -// -// Keep in sync with `PERSONAL_PATH_REGEX` in -// `src/features/Workspace/workspaceAwarePath.ts`. -const PERSONAL_PATH_REGEX = /^\/(?:onboarding|me|share|devtools|desktop-onboarding)(?:[/?#]|$)/; - -// Keep in sync with `WORKSPACE_SETTINGS_TABS` in -// `src/features/Workspace/workspaceAwarePath.ts`. -const SHARED_SETTINGS_TABS = - '(?:apikey|billing|creds|credits|general|members|memory|messenger|plans|provider|service-model|skill|stats|usage)'; - -const SHARED_PATH_REGEX = new RegExp( - `^\\/(?:agent|group|community|memory|page|resource|image|video|eval|tasks?|settings\\/${SHARED_SETTINGS_TABS})(?:[/?#]|$)`, -); - -type Verdict = 'personal' | 'shared' | 'unknown'; - -const classifyPath = (path: string): Verdict => { - if (PERSONAL_PATH_REGEX.test(path)) return 'personal'; - if (SHARED_PATH_REGEX.test(path)) return 'shared'; - return 'unknown'; -}; - -const WORKSPACE_NAVIGATE_IMPORT = - "import { useWorkspaceAwareNavigate } from '@/features/Workspace/useWorkspaceAwareNavigate';"; -const WORKSPACE_LINK_IMPORT = "import WorkspaceLink from '@/features/Workspace/WorkspaceLink';"; - -interface Report { - file: string; - reason: string; -} - -const reports: { transformed: Report[]; skipped: Report[]; warnings: Report[] } = { - transformed: [], - skipped: [], - warnings: [], -}; - -const args = new Set(process.argv.slice(2)); -const DRY = args.has('--dry') || args.has('--dry-run'); -const CHECK = args.has('--check'); - -async function walk(dir: string, files: string[] = []): Promise { - let entries; - try { - entries = await readdir(dir, { withFileTypes: true }); - } catch { - return files; - } - for (const entry of entries) { - if (EXCLUDE_DIR_NAMES.has(entry.name)) continue; - const full = path.join(dir, entry.name); - if (entry.isDirectory()) { - await walk(full, files); - continue; - } - if (!entry.isFile()) continue; - if (!entry.name.endsWith('.ts') && !entry.name.endsWith('.tsx')) continue; - if (EXCLUDE_FILE_SUFFIXES.some((s) => entry.name.endsWith(s))) continue; - const rel = path.relative(ROOT, full).replaceAll('\\', '/'); - if (EXCLUDE_PATH_SUBSTRINGS.some((s) => rel.includes(s))) continue; - files.push(full); - } - return files; -} - -// Extract the first-arg path literal from `navigate(...)` invocations. -// Handles: navigate('/foo'), navigate("/foo"), navigate(`/foo/${x}`) -const NAVIGATE_CALL_REGEX = /\bnavigate\s*\(\s*(['"`])((?:\\.|(?!\1).)*?)\1/g; - -const collectNavigateTargets = (source: string): string[] => { - const out: string[] = []; - for (const match of source.matchAll(NAVIGATE_CALL_REGEX)) { - const raw = match[2]; - // Strip template-literal `${...}` placeholders so the prefix is comparable. - const prefix = raw.replaceAll(/\$\{[^}]*\}/g, ''); - out.push(prefix); - } - return out; -}; - -const containsUseNavigateImport = (source: string): boolean => - /from\s+['"]react-router-dom['"]/.test(source) && /\buseNavigate\b/.test(source); - -/** - * Rewrite `import { ..., useNavigate, ... } from 'react-router-dom'`: - * - drop `useNavigate` from the named imports list - * - if no other names remain, drop the entire import line - * - append a new import for `useWorkspaceAwareNavigate` - */ -const rewriteImports = (source: string): string => { - const importRegex = /^(\s*)import\s+\{([^}]+)\}\s+from\s+(['"])react-router-dom\3\s*(?:;\s*)?$/m; - const match = source.match(importRegex); - if (!match) return source; - const indent = match[1]; - const names = match[2] - .split(',') - .map((s) => s.trim()) - .filter(Boolean); - const remaining = names.filter((n) => n.replace(/\s+as\s+\w+/, '').trim() !== 'useNavigate'); - - const newWorkspaceImport = `${indent}${WORKSPACE_NAVIGATE_IMPORT}`; - - let replacement: string; - if (remaining.length === 0) { - replacement = newWorkspaceImport; - } else { - replacement = `${indent}import { ${remaining.join(', ')} } from 'react-router-dom';\n${newWorkspaceImport}`; - } - - return source.replace(importRegex, replacement); -}; - -const rewriteUseNavigateCalls = (source: string): string => - source.replaceAll(/\buseNavigate\s*\(\s*\)/g, 'useWorkspaceAwareNavigate()'); - -const ensureWorkspaceLinkImport = (source: string): string => { - if (source.includes(WORKSPACE_LINK_IMPORT)) return source; - // Insert right after the first `from 'react-router-dom'` import, or at top. - const rrdImport = /^(\s*)import\s[^;]*from\s+['"]react-router-dom['"]\s*(?:;\s*)?$/m; - const match = source.match(rrdImport); - if (match && match.index !== undefined) { - const insertAt = match.index + match[0].length; - return `${source.slice(0, insertAt)}\n${match[1]}${WORKSPACE_LINK_IMPORT}${source.slice( - insertAt, - )}`; - } - // Fallback: prepend. - return `${WORKSPACE_LINK_IMPORT}\n${source}`; -}; - -interface LinkRewriteResult { - changed: boolean; - rewrote: number; - source: string; -} - -const rewriteLinkTags = (source: string): LinkRewriteResult => { - // Walk all `` / `` tokens, pair opens with closes by depth, - // collect a single list of edits, then apply them in descending-index order - // so all positions stay valid throughout the rewrite. - const tagRegex = /<\/?Link\b[^>]*>/g; - interface OpenToken { - idx: number; - raw: string; - rewrite: boolean; - selfClosing: boolean; - } - interface CloseToken { - idx: number; - raw: string; - } - const opens: OpenToken[] = []; - const closes: CloseToken[] = []; - const allTokens: Array<{ kind: 'open' | 'close'; idx: number; raw: string }> = []; - - for (const m of source.matchAll(tagRegex)) { - const raw = m[0]; - const idx = m.index!; - if (raw.startsWith(''); - const tok: OpenToken = { idx, raw, rewrite, selfClosing }; - opens.push(tok); - allTokens.push({ kind: 'open', idx, raw }); - } - - // Pair opens with closes by walking the token stream in source order. - const stack: OpenToken[] = []; - const edits: Array<{ idx: number; length: number; replacement: string }> = []; - let rewroteCount = 0; - - for (const t of allTokens) { - if (t.kind === 'open') { - const ot = opens.find((o) => o.idx === t.idx)!; - if (ot.selfClosing) { - if (ot.rewrite) { - edits.push({ - idx: ot.idx, - length: ot.raw.length, - replacement: ot.raw.replace(/^$/, ' />'), - }); - rewroteCount++; - } - continue; - } - stack.push(ot); - } else { - const opener = stack.pop(); - if (opener?.rewrite) { - edits.push({ - idx: opener.idx, - length: opener.raw.length, - replacement: opener.raw.replace(/^', - }); - rewroteCount += 2; - } - } - } - - edits.sort((a, b) => b.idx - a.idx); - let out = source; - for (const e of edits) { - out = `${out.slice(0, e.idx)}${e.replacement}${out.slice(e.idx + e.length)}`; - } - - return { changed: edits.length > 0, source: out, rewrote: rewroteCount }; -}; - -async function processFile(absPath: string): Promise { - const rel = path.relative(ROOT, absPath); - const original = await readFile(absPath, 'utf8'); - let next = original; - - const targets = collectNavigateTargets(original); - const verdicts = targets.map(classifyPath); - const hasPersonal = verdicts.includes('personal'); - const hasShared = verdicts.includes('shared'); - - let didUseNavigateRewrite = false; - const hasUseNavigate = containsUseNavigateImport(original); - - if (hasUseNavigate) { - if (hasPersonal && hasShared) { - reports.warnings.push({ - file: rel, - reason: 'mixed personal/shared navigate targets — useNavigate left unchanged', - }); - } else if (hasPersonal && !hasShared) { - // pure personal — leave as-is - } else { - // pure shared OR no navigate calls (e.g. only Link). The latter case is - // safe: useNavigate is imported but unused for shared paths; flipping it - // is a no-op behaviorally. We only flip if useNavigate is actually CALLED - // — otherwise leave the import alone to avoid removing genuinely-unused - // imports the codemod didn't introduce. - if (/\buseNavigate\s*\(\s*\)/.test(original)) { - const afterImport = rewriteImports(next); - if (afterImport !== next) { - next = rewriteUseNavigateCalls(afterImport); - didUseNavigateRewrite = true; - } - } - } - } - - // Link rewrite — independent of useNavigate decision. - const linkResult = rewriteLinkTags(next); - if (linkResult.changed) { - next = ensureWorkspaceLinkImport(linkResult.source); - } - - if (next === original) { - if (hasUseNavigate && (hasShared || hasPersonal)) { - reports.skipped.push({ - file: rel, - reason: hasPersonal && !hasShared ? 'personal-only navigate targets' : 'no change needed', - }); - } - return; - } - - const summary: string[] = []; - if (didUseNavigateRewrite) summary.push('useNavigate'); - if (linkResult.rewrote > 0) summary.push(`${linkResult.rewrote / 2} Link tag(s)`); - - reports.transformed.push({ file: rel, reason: summary.join(' + ') }); - - if (!DRY && !CHECK) await writeFile(absPath, next, 'utf8'); -} - -async function main(): Promise { - const files: string[] = []; - for (const root of SCAN_ROOTS) { - const abs = path.join(ROOT, root); - if ( - await stat(abs).then( - () => true, - () => false, - ) - ) { - await walk(abs, files); - } - } - - for (const f of files) await processFile(f); - - const print = (label: string, list: Report[]): void => { - if (list.length === 0) return; - console.log(`\n${label} (${list.length}):`); - for (const r of list) console.log(` ${r.file} — ${r.reason}`); - }; - - print('TRANSFORMED', reports.transformed); - print('WARNINGS', reports.warnings); - if (process.env.VERBOSE) print('SKIPPED', reports.skipped); - - console.log( - `\nSummary: transformed=${reports.transformed.length} warnings=${reports.warnings.length} skipped=${reports.skipped.length} files-scanned=${files.length}`, - ); - - if (CHECK && reports.transformed.length > 0) { - console.error('\n✗ codemod would modify files. Re-run without --check to apply.'); - process.exit(1); - } -} - -await main(); diff --git a/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx b/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx index 0b16db0579..04d3bab9ff 100644 --- a/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx +++ b/src/features/AgentTasks/AgentTaskDetail/TaskDetailHeaderActions.tsx @@ -5,8 +5,10 @@ import { CopyIcon, LinkIcon, MoreHorizontal, Trash } from 'lucide-react'; import { memo, useCallback, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { useActiveWorkspaceSlug } from '@/business/client/hooks/useActiveWorkspaceSlug'; import { useTaskTransferMenuItem } from '@/business/client/hooks/useTaskTransferMenuItem'; import { useWorkspaceAwareNavigate } from '@/features/Workspace/useWorkspaceAwareNavigate'; +import { buildWorkspaceAwarePath } from '@/features/Workspace/workspaceAwarePath'; import { useAppOrigin } from '@/hooks/useAppOrigin'; import { usePermission } from '@/hooks/usePermission'; import { useTaskStore } from '@/store/task'; @@ -19,6 +21,7 @@ const TaskDetailHeaderActions = memo(() => { const { message } = App.useApp(); const navigate = useWorkspaceAwareNavigate(); const appOrigin = useAppOrigin(); + const activeWorkspaceSlug = useActiveWorkspaceSlug(); const { allowed: canEditTask } = usePermission('create_content'); const taskId = useTaskStore(taskDetailSelectors.activeTaskId); const taskAgentId = useTaskStore(taskDetailSelectors.activeTaskAgentId); @@ -43,7 +46,10 @@ const TaskDetailHeaderActions = memo(() => { const menuItems = useMemo(() => { if (!taskId) return []; - const taskUrl = `${appOrigin}${taskDetailPath(taskId, taskAgentId ?? undefined)}`; + const taskUrl = `${appOrigin}${buildWorkspaceAwarePath( + taskDetailPath(taskId, taskAgentId ?? undefined), + activeWorkspaceSlug, + )}`; const baseItems: DropdownItem[] = [ { @@ -78,7 +84,17 @@ const TaskDetailHeaderActions = memo(() => { if (!transferItems || transferItems.length === 0) return baseItems; return [...baseItems.slice(0, 3), ...transferItems, { type: 'divider' }, ...baseItems.slice(3)]; - }, [taskId, taskAgentId, appOrigin, t, message, triggerDelete, canEditTask, transferItems]); + }, [ + taskId, + taskAgentId, + appOrigin, + activeWorkspaceSlug, + t, + message, + triggerDelete, + canEditTask, + transferItems, + ]); if (!taskId) return null; diff --git a/src/features/AgentTasks/features/useTaskItemContextMenu.tsx b/src/features/AgentTasks/features/useTaskItemContextMenu.tsx index 875f46450d..5c107298ea 100644 --- a/src/features/AgentTasks/features/useTaskItemContextMenu.tsx +++ b/src/features/AgentTasks/features/useTaskItemContextMenu.tsx @@ -21,7 +21,9 @@ import { import { useCallback, useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; +import { useActiveWorkspaceSlug } from '@/business/client/hooks/useActiveWorkspaceSlug'; import { useTaskTransferMenuItem } from '@/business/client/hooks/useTaskTransferMenuItem'; +import { buildWorkspaceAwarePath } from '@/features/Workspace/workspaceAwarePath'; import { useAppOrigin } from '@/hooks/useAppOrigin'; import { usePermission } from '@/hooks/usePermission'; import { useAgentStore } from '@/store/agent'; @@ -60,6 +62,7 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => { const { t } = useTranslation(['chat', 'common']); const { message } = App.useApp(); const appOrigin = useAppOrigin(); + const activeWorkspaceSlug = useActiveWorkspaceSlug(); const { allowed: canEditTask } = usePermission('create_content'); const updateTaskStatus = useTaskStore((s) => s.updateTaskStatus); @@ -133,9 +136,9 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => { } as ContextMenuItem; }); - const taskUrl = `${appOrigin}${taskDetailPath( - task.identifier, - task.assigneeAgentId ?? undefined, + const taskUrl = `${appOrigin}${buildWorkspaceAwarePath( + taskDetailPath(task.identifier, task.assigneeAgentId ?? undefined), + activeWorkspaceSlug, )}`; const canRunNow = RUN_NOW_STATUSES.has(currentStatus); @@ -295,6 +298,7 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => { message, t, appOrigin, + activeWorkspaceSlug, updateTaskStatus, updateTask, refreshTaskList, diff --git a/src/features/AgentTasks/shared/taskDetailPath.test.ts b/src/features/AgentTasks/shared/taskDetailPath.test.ts index 7262429a07..4d3532d3bf 100644 --- a/src/features/AgentTasks/shared/taskDetailPath.test.ts +++ b/src/features/AgentTasks/shared/taskDetailPath.test.ts @@ -7,6 +7,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { taskDetailPath, useNavigateToTaskDetail, useTaskDetailPath } from './taskDetailPath'; const mocks = vi.hoisted(() => ({ + activeWorkspaceSlug: null as string | null, navigate: vi.fn(), params: {} as { aid?: string }, })); @@ -16,10 +17,15 @@ vi.mock('react-router-dom', () => ({ useParams: () => mocks.params, })); +vi.mock('@/business/client/hooks/useActiveWorkspaceSlug', () => ({ + useActiveWorkspaceSlug: () => mocks.activeWorkspaceSlug, +})); + describe('taskDetailPath', () => { beforeEach(() => { mocks.navigate.mockClear(); mocks.params = {}; + mocks.activeWorkspaceSlug = null; }); it('builds an agent-scoped path when an agent id is provided', () => { @@ -47,4 +53,14 @@ describe('taskDetailPath', () => { expect(mocks.navigate).toHaveBeenCalledWith('/agent/agt_child/task/T-2'); }); + + it('prefixes the navigation target with the active workspace slug', () => { + mocks.params = { aid: 'agt_current' }; + mocks.activeWorkspaceSlug = 'lobehub'; + + const { result } = renderHook(() => useNavigateToTaskDetail()); + result.current('T-2', 'agt_child'); + + expect(mocks.navigate).toHaveBeenCalledWith('/lobehub/agent/agt_child/task/T-2'); + }); }); diff --git a/src/features/AgentTasks/shared/taskDetailPath.ts b/src/features/AgentTasks/shared/taskDetailPath.ts index 00013551a0..67ecfa59f6 100644 --- a/src/features/AgentTasks/shared/taskDetailPath.ts +++ b/src/features/AgentTasks/shared/taskDetailPath.ts @@ -1,5 +1,7 @@ import { useCallback } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; + +import { useWorkspaceAwareNavigate } from '@/features/Workspace/useWorkspaceAwareNavigate'; export const taskDetailPath = (taskId: string, agentId?: string) => agentId ? `/agent/${agentId}/task/${taskId}` : `/task/${taskId}`; @@ -14,7 +16,7 @@ export const useTaskDetailPath = () => { }; export const useNavigateToTaskDetail = () => { - const navigate = useNavigate(); + const navigate = useWorkspaceAwareNavigate(); const getTaskDetailPath = useTaskDetailPath(); return useCallback( diff --git a/src/features/PageEditor/Header/useMenu.test.tsx b/src/features/PageEditor/Header/useMenu.test.tsx index 95d90a4733..305f7a1dfb 100644 --- a/src/features/PageEditor/Header/useMenu.test.tsx +++ b/src/features/PageEditor/Header/useMenu.test.tsx @@ -18,6 +18,7 @@ vi.mock('react-i18next', () => ({ })); vi.mock('@lobechat/const', () => ({ + CUSTOM_DOCUMENT_FILE_TYPE: 'custom/document', isDesktop: false, })); diff --git a/src/features/PageEditor/Header/useMenu.tsx b/src/features/PageEditor/Header/useMenu.tsx index 3d006c5555..d37e9392cf 100644 --- a/src/features/PageEditor/Header/useMenu.tsx +++ b/src/features/PageEditor/Header/useMenu.tsx @@ -4,10 +4,11 @@ import { Icon } from '@lobehub/ui'; import { App } from 'antd'; import { cssVar, useResponsive } from 'antd-style'; import dayjs from 'dayjs'; -import { Clock3Icon, CopyPlus, Download, Link2, Maximize2, Trash2 } from 'lucide-react'; +import { Clock3Icon, CopyPlus, Download, Link2, Maximize2, Trash2, UserRound } from 'lucide-react'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; +import { useAuthorInfo } from '@/business/client/hooks/useAuthorInfo'; import { useDocumentTransferMenuItem } from '@/business/client/hooks/useDocumentTransferMenuItem'; import { usePermission } from '@/hooks/usePermission'; import { useDocumentStore } from '@/store/document'; @@ -15,6 +16,7 @@ import { editorSelectors } from '@/store/document/slices/editor'; import { useFileStore } from '@/store/file'; import { useGlobalStore } from '@/store/global'; import { systemStatusSelectors } from '@/store/global/selectors'; +import { pageSelectors, usePageStore } from '@/store/page'; import { usePageEditorStore, useStoreApi } from '../store'; @@ -31,11 +33,17 @@ export const useMenu = (): { menuItems: any[] } => { const { allowed: canCreatePage } = usePermission('create_content'); const { allowed: canEditPage } = usePermission('edit_own_content'); - // Get lastUpdatedTime from DocumentStore - const lastUpdatedTime = useDocumentStore((s) => + // Get lastUpdatedTime from DocumentStore (live save status within the session) + const editorUpdatedTime = useDocumentStore((s) => documentId ? editorSelectors.lastUpdatedTime(documentId)(s) : null, ); + const pageDocument = usePageStore(pageSelectors.getDocumentById(documentId)); + const authorName = useAuthorInfo(pageDocument?.userId)?.fullName; + const lastUpdatedTime = + editorUpdatedTime ?? + (pageDocument?.updatedAt ? new Date(pageDocument.updatedAt).toISOString() : null); + const duplicateDocument = useFileStore((s) => s.duplicateDocument); const setRightPanelMode = usePageEditorStore((s) => s.setRightPanelMode); const transferMenuItems = useDocumentTransferMenuItem(documentId) as DropdownItem[] | null; @@ -167,24 +175,28 @@ export const useMenu = (): { menuItems: any[] } => { }, ]; - if (lastUpdatedTime) { + if (lastUpdatedTime || authorName) { items.push( { type: 'divider' as const, }, { disabled: true, + icon: authorName ? : undefined, key: 'page-info', label: ( -
-
- {lastUpdatedTime + + {[ + authorName, + lastUpdatedTime ? t('pageEditor.editedAt', { time: dayjs(lastUpdatedTime).format('MMMM D, YYYY [at] h:mm A'), }) - : ''} -
-
+ : '', + ] + .filter(Boolean) + .join(' · ')} + ), }, ); @@ -192,6 +204,7 @@ export const useMenu = (): { menuItems: any[] } => { return items; }, [ lastUpdatedTime, + authorName, canCreatePage, canEditPage, storeApi, diff --git a/src/features/PageEditor/History/CompareModal/CompareContent.tsx b/src/features/PageEditor/History/CompareModal/CompareContent.tsx index 30a1b4194a..0dc2a1ad1e 100644 --- a/src/features/PageEditor/History/CompareModal/CompareContent.tsx +++ b/src/features/PageEditor/History/CompareModal/CompareContent.tsx @@ -7,6 +7,7 @@ import { RotateCcwIcon } from 'lucide-react'; import { memo, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useAuthorInfo } from '@/business/client/hooks/useAuthorInfo'; import type { DocumentHistoryListItem, DocumentHistorySaveSource, @@ -112,6 +113,8 @@ const CompareContent = memo( [items, selectedHistoryId], ); + const authorInfo = useAuthorInfo(selectedItem?.userId); + if (!selectedItem) return null; const canRestore = !selectedItem.isCurrent; @@ -130,6 +133,11 @@ const CompareContent = memo( {dayjs(selectedItem.savedAt).fromNow()} ·{' '} {saveSourceLabels[selectedItem.saveSource]} + {authorInfo?.fullName && ( + + · {authorInfo.fullName} + + )} {canRestore && (