mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-17 13:06:21 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1d7fc18cdb | |||
| 2c66867f65 | |||
| 8718e1d33f | |||
| 7bc47071c4 | |||
| c66b1fc8fe | |||
| 46439bbd16 | |||
| 25387ada92 | |||
| da3412e202 | |||
| 7ea84a2695 | |||
| 211e8c1f54 | |||
| 0ef1309b68 | |||
| 73907480d7 | |||
| a38437c1da | |||
| de207a65c2 |
@@ -2,31 +2,6 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Version 2.2.6](https://github.com/lobehub/lobe-chat/compare/v2.2.6-canary.8...v2.2.6)
|
||||
|
||||
<sup>Released on **2026-06-17**</sup>
|
||||
|
||||
#### ✨ Features
|
||||
|
||||
- **agent**: improve connector, document, and fleet workflows.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's improved
|
||||
|
||||
- **agent**: improve connector, document, and fleet workflows, closes [#15936](https://github.com/lobehub/lobe-chat/issues/15936) ([3f82033](https://github.com/lobehub/lobe-chat/commit/3f82033))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.2.1](https://github.com/lobehub/lobe-chat/compare/v0.0.0-nightly.pr15228.13999...v2.2.1)
|
||||
|
||||
<sup>Released on **2026-05-29**</sup>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
@@ -77,6 +80,40 @@ describe('lh file - E2E', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── upload (local file) ───────────────────────────────
|
||||
|
||||
describe('upload', () => {
|
||||
it('should upload a local file passed as a positional argument', () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `lh-e2e-upload-${Date.now()}.txt`);
|
||||
fs.writeFileSync(tmpFile, 'hello from lh e2e upload');
|
||||
|
||||
try {
|
||||
const result = runJson<{ id: string }>(`file upload ${tmpFile} --json id`);
|
||||
expect(result).toHaveProperty('id');
|
||||
if (result.id) run(`file delete ${result.id} --yes`);
|
||||
} finally {
|
||||
fs.rmSync(tmpFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should upload a local file passed via --file', () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `lh-e2e-upload-f-${Date.now()}.txt`);
|
||||
fs.writeFileSync(tmpFile, 'hello from lh e2e --file upload');
|
||||
|
||||
try {
|
||||
const result = runJson<{ id: string }>(`file upload --file ${tmpFile} --json id`);
|
||||
expect(result).toHaveProperty('id');
|
||||
if (result.id) run(`file delete ${result.id} --yes`);
|
||||
} finally {
|
||||
fs.rmSync(tmpFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should error when the local file does not exist', () => {
|
||||
expect(() => run('file upload -f /no/such/lh-file.txt')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
// ── recent ────────────────────────────────────────────
|
||||
|
||||
describe('recent', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Code generated by `npm run man:generate`; DO NOT EDIT.
|
||||
.\" Manual command details come from the Commander command tree.
|
||||
.TH LH 1 "" "@lobehub/cli 0.0.29" "User Commands"
|
||||
.TH LH 1 "" "@lobehub/cli 0.0.31" "User Commands"
|
||||
.SH NAME
|
||||
lh \- LobeHub CLI \- manage and connect to LobeHub services
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lobehub/cli",
|
||||
"version": "0.0.29",
|
||||
"version": "0.0.31",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"lh": "./dist/index.js",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
@@ -17,6 +21,9 @@ const { mockTrpcClient } = vi.hoisted(() => ({
|
||||
removeFiles: { mutate: vi.fn() },
|
||||
updateFile: { mutate: vi.fn() },
|
||||
},
|
||||
upload: {
|
||||
createS3PreSignedUrl: { mutate: vi.fn() },
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -38,9 +45,11 @@ describe('file command', () => {
|
||||
exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => {}) as any);
|
||||
consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
mockGetTrpcClient.mockResolvedValue(mockTrpcClient);
|
||||
for (const method of Object.values(mockTrpcClient.file)) {
|
||||
for (const fn of Object.values(method)) {
|
||||
(fn as ReturnType<typeof vi.fn>).mockReset();
|
||||
for (const group of [mockTrpcClient.file, mockTrpcClient.upload]) {
|
||||
for (const method of Object.values(group)) {
|
||||
for (const fn of Object.values(method)) {
|
||||
(fn as ReturnType<typeof vi.fn>).mockReset();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -205,6 +214,111 @@ describe('file command', () => {
|
||||
expect(mockTrpcClient.file.createFile.mutate).not.toHaveBeenCalled();
|
||||
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('already exists'));
|
||||
});
|
||||
|
||||
it('should upload a local file passed as a positional argument', async () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `lh-upload-${process.pid}.txt`);
|
||||
fs.writeFileSync(tmpFile, 'hello world');
|
||||
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockResolvedValue({ ok: true, status: 200, statusText: 'OK' } as Response);
|
||||
mockTrpcClient.file.checkFileHash.mutate.mockResolvedValue({ isExist: false });
|
||||
mockTrpcClient.upload.createS3PreSignedUrl.mutate.mockResolvedValue('https://s3/presigned');
|
||||
mockTrpcClient.file.createFile.mutate.mockResolvedValue({
|
||||
id: 'f-local',
|
||||
url: 'files/x.txt',
|
||||
});
|
||||
|
||||
try {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(['node', 'test', 'file', 'upload', tmpFile]);
|
||||
|
||||
expect(mockTrpcClient.upload.createS3PreSignedUrl.mutate).toHaveBeenCalled();
|
||||
expect(fetchSpy).toHaveBeenCalledWith(
|
||||
'https://s3/presigned',
|
||||
expect.objectContaining({ method: 'PUT' }),
|
||||
);
|
||||
expect(mockTrpcClient.file.createFile.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
fileType: 'text/plain',
|
||||
name: path.basename(tmpFile),
|
||||
url: expect.stringContaining('.txt'),
|
||||
}),
|
||||
);
|
||||
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('File created'));
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
fs.rmSync(tmpFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should upload a local file passed via --file', async () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `lh-upload-f-${process.pid}.json`);
|
||||
fs.writeFileSync(tmpFile, '{}');
|
||||
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockResolvedValue({ ok: true, status: 200, statusText: 'OK' } as Response);
|
||||
mockTrpcClient.file.checkFileHash.mutate.mockResolvedValue({ isExist: false });
|
||||
mockTrpcClient.upload.createS3PreSignedUrl.mutate.mockResolvedValue('https://s3/presigned');
|
||||
mockTrpcClient.file.createFile.mutate.mockResolvedValue({ id: 'f-json' });
|
||||
|
||||
try {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(['node', 'test', 'file', 'upload', '--file', tmpFile]);
|
||||
|
||||
expect(mockTrpcClient.file.createFile.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ fileType: 'application/json' }),
|
||||
);
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
fs.rmSync(tmpFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should skip the S3 upload when the local file hash already exists', async () => {
|
||||
const tmpFile = path.join(os.tmpdir(), `lh-upload-dedup-${process.pid}.txt`);
|
||||
fs.writeFileSync(tmpFile, 'dedup me');
|
||||
|
||||
const fetchSpy = vi.spyOn(globalThis, 'fetch');
|
||||
mockTrpcClient.file.checkFileHash.mutate.mockResolvedValue({
|
||||
isExist: true,
|
||||
url: 'files/2024-01-01/existing.txt',
|
||||
});
|
||||
mockTrpcClient.file.createFile.mutate.mockResolvedValue({ id: 'f-dedup' });
|
||||
|
||||
try {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(['node', 'test', 'file', 'upload', tmpFile]);
|
||||
|
||||
// No pre-sign and no S3 PUT should happen
|
||||
expect(mockTrpcClient.upload.createS3PreSignedUrl.mutate).not.toHaveBeenCalled();
|
||||
expect(fetchSpy).not.toHaveBeenCalled();
|
||||
// The record reuses the existing url
|
||||
expect(mockTrpcClient.file.createFile.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ url: 'files/2024-01-01/existing.txt' }),
|
||||
);
|
||||
} finally {
|
||||
fetchSpy.mockRestore();
|
||||
fs.rmSync(tmpFile, { force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('should error when local file does not exist', async () => {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(['node', 'test', 'file', 'upload', '-f', '/no/such/file.txt']);
|
||||
|
||||
expect(log.error).toHaveBeenCalledWith(expect.stringContaining('File not found'));
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
it('should error when no source is provided', async () => {
|
||||
const program = createProgram();
|
||||
await program.parseAsync(['node', 'test', 'file', 'upload']);
|
||||
|
||||
expect(log.error).toHaveBeenCalledWith(expect.stringContaining('Provide a local file path'));
|
||||
expect(exitSpy).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edit', () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import pc from 'picocolors';
|
||||
import { getTrpcClient } from '../api/client';
|
||||
import { confirm, outputJson, printTable, timeAgo, truncate } from '../utils/format';
|
||||
import { log } from '../utils/logger';
|
||||
import { uploadLocalFile } from '../utils/uploadLocalFile';
|
||||
|
||||
export function registerFileCommand(program: Command) {
|
||||
const file = program.command('file').description('Manage files');
|
||||
@@ -113,18 +114,20 @@ export function registerFileCommand(program: Command) {
|
||||
// ── upload ───────────────────────────────────────────
|
||||
|
||||
file
|
||||
.command('upload <url>')
|
||||
.description('Upload a file by URL (checks hash first)')
|
||||
.option('--hash <hash>', 'File hash for deduplication check')
|
||||
.option('--name <name>', 'File name')
|
||||
.option('--type <type>', 'File MIME type')
|
||||
.option('--size <size>', 'File size in bytes')
|
||||
.command('upload [source]')
|
||||
.description('Upload a file from a local path or a URL')
|
||||
.option('-f, --file <path>', 'Local file path to upload')
|
||||
.option('--hash <hash>', 'File hash for deduplication check (URL mode)')
|
||||
.option('--name <name>', 'File name (URL mode)')
|
||||
.option('--type <type>', 'File MIME type (URL mode)')
|
||||
.option('--size <size>', 'File size in bytes (URL mode)')
|
||||
.option('--parent-id <id>', 'Parent folder ID')
|
||||
.option('--json [fields]', 'Output JSON, optionally specify fields (comma-separated)')
|
||||
.action(
|
||||
async (
|
||||
url: string,
|
||||
source: string | undefined,
|
||||
options: {
|
||||
file?: string;
|
||||
hash?: string;
|
||||
json?: string | boolean;
|
||||
name?: string;
|
||||
@@ -133,8 +136,47 @@ export function registerFileCommand(program: Command) {
|
||||
type?: string;
|
||||
},
|
||||
) => {
|
||||
const isUrl = (value: string) =>
|
||||
value.startsWith('http://') || value.startsWith('https://');
|
||||
|
||||
// Resolve the local file path: explicit --file, or a positional that is
|
||||
// not a URL (e.g. `lh file upload ./games_list.txt`).
|
||||
const localPath = options.file ?? (source && !isUrl(source) ? source : undefined);
|
||||
|
||||
const client = await getTrpcClient();
|
||||
|
||||
// ── Local file upload ──
|
||||
if (localPath) {
|
||||
let result;
|
||||
try {
|
||||
result = await uploadLocalFile(client, localPath, { parentId: options.parentId });
|
||||
} catch (error) {
|
||||
log.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.json !== undefined) {
|
||||
const fields = typeof options.json === 'string' ? options.json : undefined;
|
||||
outputJson(result, fields);
|
||||
return;
|
||||
}
|
||||
|
||||
const r = result as any;
|
||||
console.log(`${pc.green('✓')} File created: ${pc.bold(r.id || '')}`);
|
||||
if (r.url) console.log(` URL: ${pc.dim(r.url)}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── URL upload ──
|
||||
if (!source) {
|
||||
log.error('Provide a local file path, --file <path>, or a URL to upload.');
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = source;
|
||||
|
||||
// Check hash first if provided
|
||||
if (options.hash) {
|
||||
const check = await client.file.checkFileHash.mutate({ hash: options.hash });
|
||||
|
||||
+11
-72
@@ -1,14 +1,12 @@
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import type { Command } from 'commander';
|
||||
import pc from 'picocolors';
|
||||
|
||||
import { getTrpcClient } from '../api/client';
|
||||
import { getAuthInfo } from '../api/http';
|
||||
import { confirm, outputJson, printTable, timeAgo, truncate } from '../utils/format';
|
||||
import { log } from '../utils/logger';
|
||||
import { uploadLocalFile } from '../utils/uploadLocalFile';
|
||||
|
||||
function formatFileType(fileType: string): string {
|
||||
if (!fileType) return '';
|
||||
@@ -324,81 +322,22 @@ export function registerKbCommand(program: Command) {
|
||||
.description('Upload a file to a knowledge base')
|
||||
.option('--parent <parentId>', 'Parent folder ID')
|
||||
.action(async (knowledgeBaseId: string, filePath: string, options: { parent?: string }) => {
|
||||
const resolved = path.resolve(filePath);
|
||||
if (!fs.existsSync(resolved)) {
|
||||
log.error(`File not found: ${resolved}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const stat = fs.statSync(resolved);
|
||||
const fileName = path.basename(resolved);
|
||||
const fileBuffer = fs.readFileSync(resolved);
|
||||
|
||||
// Compute SHA-256 hash
|
||||
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
||||
|
||||
// Detect MIME type from extension
|
||||
const ext = path.extname(fileName).toLowerCase().slice(1);
|
||||
const mimeMap: Record<string, string> = {
|
||||
csv: 'text/csv',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
gif: 'image/gif',
|
||||
jpeg: 'image/jpeg',
|
||||
jpg: 'image/jpeg',
|
||||
json: 'application/json',
|
||||
md: 'text/markdown',
|
||||
mp3: 'audio/mpeg',
|
||||
mp4: 'video/mp4',
|
||||
pdf: 'application/pdf',
|
||||
png: 'image/png',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
svg: 'image/svg+xml',
|
||||
txt: 'text/plain',
|
||||
webp: 'image/webp',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
};
|
||||
const fileType = mimeMap[ext] || 'application/octet-stream';
|
||||
|
||||
const client = await getTrpcClient();
|
||||
const { serverUrl, headers } = await getAuthInfo();
|
||||
|
||||
// 1. Get presigned URL
|
||||
const date = new Date().toLocaleDateString('en-CA'); // YYYY-MM-DD
|
||||
const pathname = `files/${date}/${hash}.${ext}`;
|
||||
const presigned = await client.upload.createS3PreSignedUrl.mutate({ pathname });
|
||||
|
||||
// 2. Upload to S3
|
||||
const presignedUrl = typeof presigned === 'string' ? presigned : (presigned as any).url;
|
||||
const uploadRes = await fetch(presignedUrl, {
|
||||
body: fileBuffer,
|
||||
headers: { 'Content-Type': fileType },
|
||||
method: 'PUT',
|
||||
});
|
||||
if (!uploadRes.ok) {
|
||||
log.error(`Upload failed: ${uploadRes.status} ${uploadRes.statusText}`);
|
||||
let result;
|
||||
try {
|
||||
result = await uploadLocalFile(client, filePath, {
|
||||
knowledgeBaseId,
|
||||
parentId: options.parent,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Create file record
|
||||
const result = await client.file.createFile.mutate({
|
||||
fileType,
|
||||
hash,
|
||||
knowledgeBaseId,
|
||||
metadata: {
|
||||
date,
|
||||
dirname: '',
|
||||
filename: fileName,
|
||||
path: pathname,
|
||||
},
|
||||
name: fileName,
|
||||
parentId: options.parent,
|
||||
size: stat.size,
|
||||
url: pathname,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`${pc.green('✓')} Uploaded ${pc.bold(fileName)} → ${pc.bold((result as any).id)}`,
|
||||
`${pc.green('✓')} Uploaded ${pc.bold(path.basename(filePath))} → ${pc.bold((result as any).id)}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import type { TrpcClient } from '../api/client';
|
||||
|
||||
/**
|
||||
* Minimal extension → MIME map for files uploaded from the local filesystem.
|
||||
* Unknown extensions fall back to `application/octet-stream`.
|
||||
*/
|
||||
const MIME_MAP: Record<string, string> = {
|
||||
csv: 'text/csv',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
gif: 'image/gif',
|
||||
jpeg: 'image/jpeg',
|
||||
jpg: 'image/jpeg',
|
||||
json: 'application/json',
|
||||
md: 'text/markdown',
|
||||
mp3: 'audio/mpeg',
|
||||
mp4: 'video/mp4',
|
||||
pdf: 'application/pdf',
|
||||
png: 'image/png',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
svg: 'image/svg+xml',
|
||||
txt: 'text/plain',
|
||||
webp: 'image/webp',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
};
|
||||
|
||||
/**
|
||||
* Detect a MIME type from a file name's extension.
|
||||
*/
|
||||
export const detectMimeType = (fileName: string): string => {
|
||||
const ext = path.extname(fileName).toLowerCase().slice(1);
|
||||
return MIME_MAP[ext] || 'application/octet-stream';
|
||||
};
|
||||
|
||||
export interface UploadLocalFileOptions {
|
||||
knowledgeBaseId?: string;
|
||||
parentId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file from the local filesystem, upload it to S3 via a pre-signed URL,
|
||||
* and create the corresponding file record. Shared by `file upload` and
|
||||
* `kb upload`.
|
||||
*
|
||||
* @returns the created file record
|
||||
*/
|
||||
export const uploadLocalFile = async (
|
||||
client: TrpcClient,
|
||||
filePath: string,
|
||||
options: UploadLocalFileOptions = {},
|
||||
) => {
|
||||
const resolved = path.resolve(filePath);
|
||||
if (!fs.existsSync(resolved)) {
|
||||
throw new Error(`File not found: ${resolved}`);
|
||||
}
|
||||
|
||||
const stat = fs.statSync(resolved);
|
||||
if (!stat.isFile()) {
|
||||
throw new Error(`Not a file: ${resolved}`);
|
||||
}
|
||||
|
||||
const fileName = path.basename(resolved);
|
||||
const fileBuffer = fs.readFileSync(resolved);
|
||||
|
||||
// Compute SHA-256 hash for deduplication
|
||||
const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
|
||||
|
||||
const ext = path.extname(fileName).toLowerCase().slice(1);
|
||||
const fileType = detectMimeType(fileName);
|
||||
|
||||
const date = new Date().toLocaleDateString('en-CA'); // YYYY-MM-DD
|
||||
|
||||
// 1. Dedup: if the same bytes are already stored (and the object still
|
||||
// exists), skip the S3 upload entirely and reuse the existing url.
|
||||
const existing = (await client.file.checkFileHash.mutate({ hash })) as {
|
||||
isExist?: boolean;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
let pathname: string;
|
||||
if (existing?.isExist && existing.url) {
|
||||
pathname = existing.url;
|
||||
} else {
|
||||
// 2. Get a pre-signed upload URL and PUT the bytes to S3
|
||||
pathname = ext ? `files/${date}/${hash}.${ext}` : `files/${date}/${hash}`;
|
||||
const presigned = await client.upload.createS3PreSignedUrl.mutate({ pathname });
|
||||
|
||||
const presignedUrl = typeof presigned === 'string' ? presigned : (presigned as any).url;
|
||||
const uploadRes = await fetch(presignedUrl, {
|
||||
body: fileBuffer,
|
||||
headers: { 'Content-Type': fileType },
|
||||
method: 'PUT',
|
||||
});
|
||||
if (!uploadRes.ok) {
|
||||
throw new Error(`Upload failed: ${uploadRes.status} ${uploadRes.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Create the file record
|
||||
return await client.file.createFile.mutate({
|
||||
fileType,
|
||||
hash,
|
||||
knowledgeBaseId: options.knowledgeBaseId,
|
||||
metadata: {
|
||||
date,
|
||||
dirname: '',
|
||||
filename: fileName,
|
||||
path: pathname,
|
||||
},
|
||||
name: fileName,
|
||||
parentId: options.parentId,
|
||||
size: stat.size,
|
||||
url: pathname,
|
||||
});
|
||||
};
|
||||
@@ -29,10 +29,12 @@ describe('aiModelRouter', () => {
|
||||
|
||||
it('should create ai model', async () => {
|
||||
const mockCreate = vi.fn().mockResolvedValue({ id: 'model-1' });
|
||||
const mockFindByIdAndProvider = vi.fn().mockResolvedValue(null);
|
||||
vi.mocked(AiModelModel).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
findByIdAndProvider: mockFindByIdAndProvider,
|
||||
}) as any,
|
||||
);
|
||||
|
||||
@@ -44,12 +46,68 @@ describe('aiModelRouter', () => {
|
||||
});
|
||||
|
||||
expect(result).toBe('model-1');
|
||||
expect(mockFindByIdAndProvider).toHaveBeenCalledWith('test-model', 'test-provider');
|
||||
expect(mockCreate).toHaveBeenCalledWith({
|
||||
id: 'test-model',
|
||||
providerId: 'test-provider',
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject duplicate ai model before creating', async () => {
|
||||
const mockCreate = vi.fn();
|
||||
const mockFindByIdAndProvider = vi.fn().mockResolvedValue({ id: 'test-model' });
|
||||
vi.mocked(AiModelModel).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
findByIdAndProvider: mockFindByIdAndProvider,
|
||||
}) as any,
|
||||
);
|
||||
|
||||
const caller = aiModelRouter.createCaller(mockCtx);
|
||||
|
||||
await expect(
|
||||
caller.createAiModel({
|
||||
id: 'test-model',
|
||||
providerId: 'test-provider',
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: 'CONFLICT',
|
||||
message: 'Model "test-model" already exists',
|
||||
});
|
||||
expect(mockCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should convert duplicate insert races to conflict errors', async () => {
|
||||
const duplicateError = Object.assign(new Error('failed query'), {
|
||||
cause: Object.assign(new Error('duplicate key'), {
|
||||
code: '23505',
|
||||
constraint: 'ai_models_id_provider_id_user_id_unique',
|
||||
}),
|
||||
});
|
||||
const mockCreate = vi.fn().mockRejectedValue(duplicateError);
|
||||
const mockFindByIdAndProvider = vi.fn().mockResolvedValue(null);
|
||||
vi.mocked(AiModelModel).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
create: mockCreate,
|
||||
findByIdAndProvider: mockFindByIdAndProvider,
|
||||
}) as any,
|
||||
);
|
||||
|
||||
const caller = aiModelRouter.createCaller(mockCtx);
|
||||
|
||||
await expect(
|
||||
caller.createAiModel({
|
||||
id: 'test-model',
|
||||
providerId: 'test-provider',
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
code: 'CONFLICT',
|
||||
message: 'Model "test-model" already exists',
|
||||
});
|
||||
});
|
||||
|
||||
it('should get ai model by id', async () => {
|
||||
const mockModel = {
|
||||
id: 'model-1',
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { DeviceModel } from '@/database/models/device';
|
||||
|
||||
import { assertWorkspaceRootApproved } from '../deviceWorkspaceGuard';
|
||||
|
||||
const mockModel = (row: { defaultCwd?: string | null; workingDirs?: { path: string }[] } | null) =>
|
||||
({
|
||||
findByDeviceId: vi.fn().mockResolvedValue(row),
|
||||
}) as unknown as DeviceModel;
|
||||
|
||||
describe('assertWorkspaceRootApproved', () => {
|
||||
it('allows a root that exactly matches a bound workingDir', async () => {
|
||||
const model = mockModel({ workingDirs: [{ path: '/Users/me/proj' }] });
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/proj'),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('allows a root nested inside a bound workingDir', async () => {
|
||||
const model = mockModel({ workingDirs: [{ path: '/Users/me/proj' }] });
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/proj/packages/app'),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('allows a root matching defaultCwd when no workingDirs match', async () => {
|
||||
const model = mockModel({ defaultCwd: '/Users/me/default', workingDirs: [] });
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/default'),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('rejects a root that escapes the approved roots (filesystem root)', async () => {
|
||||
const model = mockModel({ workingDirs: [{ path: '/Users/me/proj' }] });
|
||||
await expect(assertWorkspaceRootApproved(model, 'dev-1', '/')).rejects.toMatchObject({
|
||||
code: 'FORBIDDEN',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects a sibling directory that shares a path prefix but is not contained', async () => {
|
||||
const model = mockModel({ workingDirs: [{ path: '/Users/me/proj' }] });
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/proj-evil'),
|
||||
).rejects.toMatchObject({ code: 'FORBIDDEN' });
|
||||
});
|
||||
|
||||
it('rejects when the device has no approved roots at all', async () => {
|
||||
const model = mockModel({ workingDirs: [] });
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/proj'),
|
||||
).rejects.toMatchObject({ code: 'FORBIDDEN' });
|
||||
});
|
||||
|
||||
it('rejects when the device row is missing', async () => {
|
||||
const model = mockModel(null);
|
||||
await expect(
|
||||
assertWorkspaceRootApproved(model, 'dev-1', '/Users/me/proj'),
|
||||
).rejects.toBeInstanceOf(TRPCError);
|
||||
});
|
||||
|
||||
it('rejects an empty workspace root with BAD_REQUEST before hitting the DB', async () => {
|
||||
const model = mockModel({ workingDirs: [{ path: '/Users/me/proj' }] });
|
||||
await expect(assertWorkspaceRootApproved(model, 'dev-1', '')).rejects.toMatchObject({
|
||||
code: 'BAD_REQUEST',
|
||||
});
|
||||
expect(model.findByDeviceId).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
import { type AiProviderModelListItem } from 'model-bank';
|
||||
import {
|
||||
AiModelTypeSchema,
|
||||
@@ -18,6 +19,30 @@ import { getServerGlobalConfig } from '@/server/globalConfig';
|
||||
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
||||
import { type ProviderConfig } from '@/types/user/settings';
|
||||
|
||||
const AI_MODEL_UNIQUE_CONSTRAINT = 'ai_models_id_provider_id_user_id_unique';
|
||||
|
||||
const getPostgresErrorField = (error: unknown, field: 'code' | 'constraint') => {
|
||||
let current = error;
|
||||
|
||||
while (current && typeof current === 'object') {
|
||||
const value = (current as Record<string, unknown>)[field];
|
||||
if (typeof value === 'string') return value;
|
||||
|
||||
current = (current as { cause?: unknown }).cause;
|
||||
}
|
||||
};
|
||||
|
||||
const isDuplicateAiModelError = (error: unknown) =>
|
||||
getPostgresErrorField(error, 'code') === '23505' &&
|
||||
getPostgresErrorField(error, 'constraint') === AI_MODEL_UNIQUE_CONSTRAINT;
|
||||
|
||||
const throwDuplicateAiModelError = (id: string): never => {
|
||||
throw new TRPCError({
|
||||
code: 'CONFLICT',
|
||||
message: `Model "${id}" already exists`,
|
||||
});
|
||||
};
|
||||
|
||||
const aiModelProcedure = wsCompatProcedure.use(serverDatabase).use(async (opts) => {
|
||||
const { ctx } = opts;
|
||||
const wsId = ctx.workspaceId ?? undefined;
|
||||
@@ -82,9 +107,18 @@ export const aiModelRouter = router({
|
||||
.use(withScopedPermission('ai_model:create'))
|
||||
.input(CreateAiModelSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const data = await ctx.aiModelModel.create(input);
|
||||
const existingModel = await ctx.aiModelModel.findByIdAndProvider(input.id, input.providerId);
|
||||
if (existingModel) throwDuplicateAiModelError(input.id);
|
||||
|
||||
return data?.id;
|
||||
try {
|
||||
const data = await ctx.aiModelModel.create(input);
|
||||
|
||||
return data?.id;
|
||||
} catch (error) {
|
||||
if (isDuplicateAiModelError(error)) throwDuplicateAiModelError(input.id);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
|
||||
getAiModelById: aiModelProcedure
|
||||
|
||||
@@ -8,6 +8,7 @@ import { serverDatabase } from '@/libs/trpc/lambda/middleware';
|
||||
import { deviceGateway } from '@/server/services/deviceGateway';
|
||||
|
||||
import { preserveWorkspaceCache } from './deviceWorkingDirs';
|
||||
import { assertWorkspaceRootApproved } from './deviceWorkspaceGuard';
|
||||
|
||||
// Derive the zod enum from the canonical config so new platforms are
|
||||
// automatically covered without touching this file.
|
||||
@@ -29,6 +30,23 @@ const deviceProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
|
||||
});
|
||||
});
|
||||
|
||||
const workspaceFileInput = z.object({
|
||||
deviceId: z.string(),
|
||||
workingDirectory: z.string(),
|
||||
});
|
||||
|
||||
/**
|
||||
* `deviceProcedure` that additionally requires `workingDirectory` to be an
|
||||
* approved workspace root for the device. Builds the guard into the procedure
|
||||
* so every file-mutating route inherits it and can never forget the check —
|
||||
* see {@link assertWorkspaceRootApproved} for why the check is necessary.
|
||||
*/
|
||||
const workspaceFileProcedure = deviceProcedure.input(workspaceFileInput).use(async (opts) => {
|
||||
const { deviceId, workingDirectory } = workspaceFileInput.parse(await opts.getRawInput());
|
||||
await assertWorkspaceRootApproved(opts.ctx.deviceModel, deviceId, workingDirectory);
|
||||
return opts.next();
|
||||
});
|
||||
|
||||
export const deviceRouter = router({
|
||||
/**
|
||||
* Probe whether a specific agent platform (openclaw / hermes) is available
|
||||
@@ -334,24 +352,22 @@ export const deviceRouter = router({
|
||||
* Read-only local file preview for a file on a remote device. The web client
|
||||
* receives render data, not a `localfile://` URL; saving remains unsupported.
|
||||
*/
|
||||
getLocalFilePreview: deviceProcedure
|
||||
getLocalFilePreview: workspaceFileProcedure
|
||||
.input(
|
||||
z.object({
|
||||
accept: z.enum(['image']).optional(),
|
||||
deviceId: z.string(),
|
||||
path: z.string(),
|
||||
workingDirectory: z.string(),
|
||||
}),
|
||||
)
|
||||
.query(async ({ ctx, input }) =>
|
||||
deviceGateway.getLocalFilePreview({
|
||||
.query(async ({ ctx, input }) => {
|
||||
return deviceGateway.getLocalFilePreview({
|
||||
accept: input.accept,
|
||||
deviceId: input.deviceId,
|
||||
path: input.path,
|
||||
userId: ctx.userId,
|
||||
workingDirectory: input.workingDirectory,
|
||||
}),
|
||||
),
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Project skills (`.agents/skills` / `.claude/skills`) for a directory on a
|
||||
@@ -388,68 +404,62 @@ export const deviceRouter = router({
|
||||
* Move files/folders within a directory on a remote device, via the device's
|
||||
* `moveLocalFiles` RPC. Powers the Files tree's drag-to-move in device mode.
|
||||
*/
|
||||
moveProjectFiles: deviceProcedure
|
||||
moveProjectFiles: workspaceFileProcedure
|
||||
.input(
|
||||
z.object({
|
||||
deviceId: z.string(),
|
||||
items: z.array(z.object({ newPath: z.string(), oldPath: z.string() })),
|
||||
workingDirectory: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) =>
|
||||
deviceGateway.moveProjectFiles({
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return deviceGateway.moveProjectFiles({
|
||||
deviceId: input.deviceId,
|
||||
items: input.items,
|
||||
userId: ctx.userId,
|
||||
workingDirectory: input.workingDirectory,
|
||||
}),
|
||||
),
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Rename a single file/folder in a directory on a remote device, via the
|
||||
* device's `renameLocalFile` RPC.
|
||||
*/
|
||||
renameProjectFile: deviceProcedure
|
||||
renameProjectFile: workspaceFileProcedure
|
||||
.input(
|
||||
z.object({
|
||||
deviceId: z.string(),
|
||||
newName: z.string(),
|
||||
path: z.string(),
|
||||
workingDirectory: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) =>
|
||||
deviceGateway.renameProjectFile({
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return deviceGateway.renameProjectFile({
|
||||
deviceId: input.deviceId,
|
||||
newName: input.newName,
|
||||
path: input.path,
|
||||
userId: ctx.userId,
|
||||
workingDirectory: input.workingDirectory,
|
||||
}),
|
||||
),
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Save edited content back to a file on a remote device, via the device's
|
||||
* `writeLocalFile` RPC. Powers remote save in the LocalFile editor.
|
||||
*/
|
||||
writeProjectFile: deviceProcedure
|
||||
writeProjectFile: workspaceFileProcedure
|
||||
.input(
|
||||
z.object({
|
||||
content: z.string(),
|
||||
deviceId: z.string(),
|
||||
path: z.string(),
|
||||
workingDirectory: z.string(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) =>
|
||||
deviceGateway.writeProjectFile({
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
return deviceGateway.writeProjectFile({
|
||||
content: input.content,
|
||||
deviceId: input.deviceId,
|
||||
path: input.path,
|
||||
userId: ctx.userId,
|
||||
workingDirectory: input.workingDirectory,
|
||||
}),
|
||||
),
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* Check whether a path exists on a remote device and is a directory, via the
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import type { DeviceModel } from '@/database/models/device';
|
||||
import { isPathWithinRoot } from '@/server/services/deviceGateway';
|
||||
|
||||
/**
|
||||
* Validate that a client-supplied workspace root is actually one the user has
|
||||
* bound to this device.
|
||||
*
|
||||
* The file routes (move / rename / write / preview) receive `workingDirectory`
|
||||
* from the same untrusted browser session that supplies the file paths. The
|
||||
* gateway's `assertPathsWithinWorkspace` only proves the paths sit *inside that
|
||||
* directory* — it never proves the directory itself is legitimate. So a caller
|
||||
* could set `workingDirectory` to `/` (or `C:\`), pass that containment check
|
||||
* trivially, and reach any path on the device.
|
||||
*
|
||||
* To close that hole we re-derive the approved roots from the *server-owned*
|
||||
* device row — the `workingDirs` recent list and `defaultCwd`, both written only
|
||||
* via `device.updateDevice` / the run path, never trusted from this request —
|
||||
* and require the requested root to equal or nest inside one of them before any
|
||||
* RPC is forwarded. The picker upserts every chosen directory into `workingDirs`
|
||||
* (see `useCommitWorkingDirectory`) and run start upserts the bound cwd, so a
|
||||
* legitimately-selected workspace is always present here.
|
||||
*/
|
||||
export const assertWorkspaceRootApproved = async (
|
||||
deviceModel: DeviceModel,
|
||||
deviceId: string,
|
||||
workingDirectory: string,
|
||||
): Promise<void> => {
|
||||
if (!workingDirectory) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'A workspace root is required for file operations',
|
||||
});
|
||||
}
|
||||
|
||||
const device = await deviceModel.findByDeviceId(deviceId);
|
||||
|
||||
const approvedRoots = [
|
||||
...(device?.workingDirs ?? []).map((dir) => dir.path),
|
||||
...(device?.defaultCwd ? [device.defaultCwd] : []),
|
||||
].filter((root): root is string => Boolean(root));
|
||||
|
||||
const approved = approvedRoots.some((root) => isPathWithinRoot(root, workingDirectory));
|
||||
|
||||
if (!approved) {
|
||||
throw new TRPCError({
|
||||
code: 'FORBIDDEN',
|
||||
message: 'Working directory is not an approved workspace for this device',
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -50,7 +50,7 @@ const log = debug('lobe-server:device-gateway');
|
||||
* path flavour from the root's shape so a Windows device path is still resolved
|
||||
* with Windows semantics rather than being mangled by `path.posix`.
|
||||
*/
|
||||
const isPathWithinRoot = (root: string, target: string): boolean => {
|
||||
export const isPathWithinRoot = (root: string, target: string): boolean => {
|
||||
const p = /^[A-Z]:[/\\]/i.test(root) ? path.win32 : path.posix;
|
||||
if (!p.isAbsolute(root) || !p.isAbsolute(target)) return false;
|
||||
const relative = p.relative(p.resolve(root), p.resolve(target));
|
||||
|
||||
@@ -171,6 +171,9 @@ describe('EditLockService', () => {
|
||||
expect(
|
||||
await new EditLockService('user-2', redis as any).getBlockingHolder('document', 'doc-1'),
|
||||
).toBe('user-1');
|
||||
expect(
|
||||
await new EditLockService('user-1', redis as any).getBlockingHolder('document', 'doc-1'),
|
||||
).toBeNull();
|
||||
expect(
|
||||
await new EditLockService('user-1', redis as any).getBlockingHolder(
|
||||
'document',
|
||||
|
||||
@@ -203,9 +203,13 @@ export class EditLockService {
|
||||
const holder = await this.getActiveLock(type, id);
|
||||
if (!holder) return null;
|
||||
// ownerId is broadcast on lock.changed; it can't authorize on its own.
|
||||
// Bind to userId first, then keep the stale-tab guard (same user, different
|
||||
// active ownerId still blocks so a ghost tab can't save over a newer one).
|
||||
// Bind to userId first. When callers pass an ownerId, also keep the
|
||||
// stale-tab guard (same user, different active ownerId blocks so a ghost tab
|
||||
// can't save over a newer one). Callers without owner-scoped writes only
|
||||
// need to reject other members; otherwise they would block their own generic
|
||||
// metadata updates while holding the edit lock.
|
||||
if (holder.userId !== this.userId) return holder.userId;
|
||||
if (!ownerId) return null;
|
||||
if (holder.ownerId && holder.ownerId !== ownerId) return holder.userId;
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
[
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-06-17",
|
||||
"version": "2.2.6"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-05-29",
|
||||
|
||||
+341
-169
@@ -1,212 +1,384 @@
|
||||
/**
|
||||
* Mock data for Discover/Community module
|
||||
* Mock data for Discover/Community module.
|
||||
*
|
||||
* Community E2E tests should not depend on the live marketplace service. These
|
||||
* fixtures mirror the data shape returned by the app's tRPC market router.
|
||||
*/
|
||||
import type {
|
||||
AssistantListResponse,
|
||||
DiscoverAssistantItem,
|
||||
DiscoverMcpItem,
|
||||
DiscoverModelItem,
|
||||
DiscoverProviderItem,
|
||||
McpListResponse,
|
||||
ModelListResponse,
|
||||
ProviderListResponse,
|
||||
} from './types';
|
||||
|
||||
const CREATED_AT = '2026-01-01T00:00:00.000Z';
|
||||
const UPDATED_AT = '2026-01-10T00:00:00.000Z';
|
||||
|
||||
// ============================================
|
||||
// Assistant Mock Data
|
||||
// ============================================
|
||||
|
||||
export const mockAssistantList: AssistantListResponse = {
|
||||
items: [
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '🤖',
|
||||
backgroundColor: '#1890ff',
|
||||
category: 'general',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
description: 'A versatile AI assistant for general tasks and conversations.',
|
||||
identifier: 'general-assistant',
|
||||
installCount: 1000,
|
||||
knowledgeCount: 5,
|
||||
pluginCount: 3,
|
||||
title: 'General Assistant',
|
||||
tokenUsage: 4096,
|
||||
userName: 'lobehub',
|
||||
export const mockAssistantItems: DiscoverAssistantItem[] = [
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '🤖',
|
||||
backgroundColor: '#1890ff',
|
||||
category: 'general',
|
||||
config: {
|
||||
openingMessage: 'Hello, I am your general assistant.',
|
||||
openingQuestions: ['What can you do?'],
|
||||
params: {},
|
||||
plugins: [],
|
||||
systemRole: 'You are a helpful general-purpose assistant for E2E tests.',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '💻',
|
||||
backgroundColor: '#52c41a',
|
||||
category: 'programming',
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
description: 'Expert coding assistant for software development.',
|
||||
identifier: 'code-assistant',
|
||||
installCount: 800,
|
||||
knowledgeCount: 10,
|
||||
pluginCount: 5,
|
||||
title: 'Code Assistant',
|
||||
tokenUsage: 8192,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '✍️',
|
||||
backgroundColor: '#722ed1',
|
||||
category: 'copywriting',
|
||||
createdAt: '2024-01-03T00:00:00.000Z',
|
||||
description: 'Professional writing assistant for content creation.',
|
||||
identifier: 'writing-assistant',
|
||||
installCount: 600,
|
||||
knowledgeCount: 3,
|
||||
pluginCount: 2,
|
||||
title: 'Writing Assistant',
|
||||
tokenUsage: 4096,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
total: 3,
|
||||
totalPages: 1,
|
||||
createdAt: CREATED_AT,
|
||||
description: 'A versatile AI assistant for general tasks and conversations.',
|
||||
identifier: 'general-assistant',
|
||||
installCount: 1000,
|
||||
knowledgeCount: 1,
|
||||
pluginCount: 0,
|
||||
summary: 'General-purpose assistant fixture.',
|
||||
tags: ['general', 'fixture'],
|
||||
title: 'General Assistant',
|
||||
tokenUsage: 4096,
|
||||
type: 'agent',
|
||||
updatedAt: UPDATED_AT,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '💻',
|
||||
backgroundColor: '#52c41a',
|
||||
category: 'programming',
|
||||
config: {
|
||||
openingMessage: 'Ready to help with development tasks.',
|
||||
openingQuestions: ['Review this function'],
|
||||
params: {},
|
||||
plugins: [],
|
||||
systemRole: 'You are an expert coding assistant for E2E tests.',
|
||||
},
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Developer and coding assistant for software engineering workflows.',
|
||||
identifier: 'code-assistant',
|
||||
installCount: 800,
|
||||
knowledgeCount: 2,
|
||||
pluginCount: 1,
|
||||
summary: 'Developer assistant fixture.',
|
||||
tags: ['developer', 'programming'],
|
||||
title: 'Code Assistant',
|
||||
tokenUsage: 8192,
|
||||
type: 'agent',
|
||||
updatedAt: UPDATED_AT,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '🎓',
|
||||
backgroundColor: '#faad14',
|
||||
category: 'academic',
|
||||
config: {
|
||||
openingMessage: 'Let us study together.',
|
||||
openingQuestions: ['Explain this concept'],
|
||||
params: {},
|
||||
plugins: [],
|
||||
systemRole: 'You are an academic tutor for E2E tests.',
|
||||
},
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Academic research and study assistant for reliable category filtering.',
|
||||
identifier: 'academic-tutor',
|
||||
installCount: 640,
|
||||
knowledgeCount: 3,
|
||||
pluginCount: 0,
|
||||
summary: 'Academic assistant fixture.',
|
||||
tags: ['academic', 'education'],
|
||||
title: 'Academic Tutor',
|
||||
tokenUsage: 4096,
|
||||
type: 'agent',
|
||||
updatedAt: UPDATED_AT,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '✍️',
|
||||
backgroundColor: '#722ed1',
|
||||
category: 'copywriting',
|
||||
config: {
|
||||
openingMessage: 'Tell me what you want to write.',
|
||||
openingQuestions: ['Draft a product intro'],
|
||||
params: {},
|
||||
plugins: [],
|
||||
systemRole: 'You are a writing assistant for E2E tests.',
|
||||
},
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Professional writing assistant for content creation.',
|
||||
identifier: 'writing-assistant',
|
||||
installCount: 600,
|
||||
knowledgeCount: 1,
|
||||
pluginCount: 0,
|
||||
summary: 'Writing assistant fixture.',
|
||||
tags: ['copywriting'],
|
||||
title: 'Writing Assistant',
|
||||
tokenUsage: 4096,
|
||||
type: 'agent',
|
||||
updatedAt: UPDATED_AT,
|
||||
userName: 'lobehub',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockAssistantList: AssistantListResponse = {
|
||||
currentPage: 1,
|
||||
items: mockAssistantItems,
|
||||
pageSize: 21,
|
||||
totalCount: 42,
|
||||
totalPages: 2,
|
||||
};
|
||||
|
||||
export const mockAssistantCategories = [
|
||||
{ id: 'general', name: 'General' },
|
||||
{ id: 'programming', name: 'Programming' },
|
||||
{ id: 'copywriting', name: 'Copywriting' },
|
||||
{ id: 'education', name: 'Education' },
|
||||
{ category: 'general', count: 12 },
|
||||
{ category: 'programming', count: 10 },
|
||||
{ category: 'academic', count: 8 },
|
||||
{ category: 'copywriting', count: 6 },
|
||||
];
|
||||
|
||||
// ============================================
|
||||
// Model Mock Data
|
||||
// ============================================
|
||||
|
||||
export const mockModelList: ModelListResponse = {
|
||||
items: [
|
||||
{
|
||||
abilities: { functionCall: true, reasoning: true, vision: true },
|
||||
contextWindowTokens: 128_000,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
description: 'Most capable model for complex tasks',
|
||||
displayName: 'GPT-4o',
|
||||
id: 'gpt-4o',
|
||||
providerId: 'openai',
|
||||
providerName: 'OpenAI',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: { functionCall: true, reasoning: true, vision: false },
|
||||
contextWindowTokens: 200_000,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
description: 'Advanced AI assistant by Anthropic',
|
||||
displayName: 'Claude 3.5 Sonnet',
|
||||
id: 'claude-3-5-sonnet-20241022',
|
||||
providerId: 'anthropic',
|
||||
providerName: 'Anthropic',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: { functionCall: false, reasoning: false, vision: false },
|
||||
contextWindowTokens: 32_768,
|
||||
createdAt: '2024-01-03T00:00:00.000Z',
|
||||
description: 'Open source language model',
|
||||
displayName: 'Llama 3.1 70B',
|
||||
id: 'llama-3.1-70b',
|
||||
providerId: 'meta',
|
||||
providerName: 'Meta',
|
||||
type: 'chat',
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
total: 3,
|
||||
totalPages: 1,
|
||||
export const mockModelItems: DiscoverModelItem[] = [
|
||||
{
|
||||
abilities: { functionCall: true, reasoning: true, vision: true },
|
||||
contextWindowTokens: 128_000,
|
||||
description: 'Most capable fixture model for complex tasks.',
|
||||
displayName: 'GPT-4o',
|
||||
id: 'gpt-4o',
|
||||
identifier: 'gpt-4o',
|
||||
providerCount: 2,
|
||||
providers: ['openai', 'lobehub'],
|
||||
releasedAt: CREATED_AT,
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: { functionCall: true, reasoning: true, vision: false },
|
||||
contextWindowTokens: 200_000,
|
||||
description: 'Advanced AI assistant fixture by Anthropic.',
|
||||
displayName: 'Claude 3.5 Sonnet',
|
||||
id: 'claude-3-5-sonnet-20241022',
|
||||
identifier: 'claude-3-5-sonnet-20241022',
|
||||
providerCount: 1,
|
||||
providers: ['anthropic'],
|
||||
releasedAt: CREATED_AT,
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: { functionCall: false, reasoning: false, vision: false },
|
||||
contextWindowTokens: 32_768,
|
||||
description: 'Open source language model fixture.',
|
||||
displayName: 'Llama 3.1 70B',
|
||||
id: 'llama-3.1-70b',
|
||||
identifier: 'llama-3.1-70b',
|
||||
providerCount: 1,
|
||||
providers: ['meta'],
|
||||
releasedAt: CREATED_AT,
|
||||
type: 'chat',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockModelList: ModelListResponse = {
|
||||
currentPage: 1,
|
||||
items: mockModelItems,
|
||||
pageSize: 21,
|
||||
totalCount: mockModelItems.length,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// Provider Mock Data
|
||||
// ============================================
|
||||
|
||||
export const mockProviderList: ProviderListResponse = {
|
||||
items: [
|
||||
{
|
||||
description: 'Leading AI research company',
|
||||
id: 'openai',
|
||||
logo: 'https://example.com/openai.png',
|
||||
modelCount: 10,
|
||||
name: 'OpenAI',
|
||||
},
|
||||
{
|
||||
description: 'AI safety focused research company',
|
||||
id: 'anthropic',
|
||||
logo: 'https://example.com/anthropic.png',
|
||||
modelCount: 5,
|
||||
name: 'Anthropic',
|
||||
},
|
||||
{
|
||||
description: 'Open source AI leader',
|
||||
id: 'meta',
|
||||
logo: 'https://example.com/meta.png',
|
||||
modelCount: 8,
|
||||
name: 'Meta',
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
total: 3,
|
||||
totalPages: 1,
|
||||
export const mockProviderItems: DiscoverProviderItem[] = [
|
||||
{
|
||||
description: 'Leading AI research company fixture.',
|
||||
identifier: 'openai',
|
||||
modelCount: 2,
|
||||
models: ['gpt-4o', 'gpt-4o-mini'],
|
||||
name: 'OpenAI',
|
||||
url: 'https://openai.com',
|
||||
},
|
||||
{
|
||||
description: 'AI safety focused research company fixture.',
|
||||
identifier: 'anthropic',
|
||||
modelCount: 1,
|
||||
models: ['claude-3-5-sonnet-20241022'],
|
||||
name: 'Anthropic',
|
||||
url: 'https://anthropic.com',
|
||||
},
|
||||
{
|
||||
description: 'Open source AI leader fixture.',
|
||||
identifier: 'meta',
|
||||
modelCount: 1,
|
||||
models: ['llama-3.1-70b'],
|
||||
name: 'Meta',
|
||||
url: 'https://ai.meta.com',
|
||||
},
|
||||
];
|
||||
|
||||
export const mockProviderList: ProviderListResponse = {
|
||||
currentPage: 1,
|
||||
items: mockProviderItems,
|
||||
pageSize: 21,
|
||||
totalCount: mockProviderItems.length,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// MCP Mock Data
|
||||
// ============================================
|
||||
|
||||
export const mockMcpList: McpListResponse = {
|
||||
items: [
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '🔍',
|
||||
category: 'search',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
description: 'Web search capabilities for AI assistants',
|
||||
identifier: 'web-search',
|
||||
installCount: 500,
|
||||
title: 'Web Search',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '📁',
|
||||
category: 'file',
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
description: 'File system operations and management',
|
||||
identifier: 'file-manager',
|
||||
installCount: 300,
|
||||
title: 'File Manager',
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
avatar: '🗄️',
|
||||
category: 'database',
|
||||
createdAt: '2024-01-03T00:00:00.000Z',
|
||||
description: 'Database query and management tools',
|
||||
identifier: 'db-tools',
|
||||
installCount: 200,
|
||||
title: 'Database Tools',
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
page: 1,
|
||||
pageSize: 12,
|
||||
total: 3,
|
||||
totalPages: 1,
|
||||
export const mockMcpItems: DiscoverMcpItem[] = [
|
||||
{
|
||||
author: 'LobeHub',
|
||||
capabilities: { prompts: false, resources: false, tools: true },
|
||||
category: 'business',
|
||||
connectionType: 'stdio',
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Business automation MCP tool fixture.',
|
||||
github: { stars: 1200, url: 'https://github.com/lobehub/e2e-business-mcp' },
|
||||
icon: '📊',
|
||||
identifier: 'business-automation',
|
||||
installCount: 500,
|
||||
installationMethods: 'npm',
|
||||
isClaimed: true,
|
||||
isFeatured: true,
|
||||
isOfficial: true,
|
||||
isValidated: true,
|
||||
manifestUrl: 'https://example.com/business-automation/manifest.json',
|
||||
name: 'Business Automation',
|
||||
toolsCount: 3,
|
||||
updatedAt: UPDATED_AT,
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
capabilities: { prompts: false, resources: true, tools: true },
|
||||
category: 'developer',
|
||||
connectionType: 'stdio',
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Developer file-system MCP fixture.',
|
||||
github: { stars: 900, url: 'https://github.com/lobehub/e2e-file-mcp' },
|
||||
icon: '📁',
|
||||
identifier: 'file-manager',
|
||||
installCount: 300,
|
||||
installationMethods: 'npm',
|
||||
isClaimed: true,
|
||||
isFeatured: false,
|
||||
isOfficial: false,
|
||||
isValidated: true,
|
||||
manifestUrl: 'https://example.com/file-manager/manifest.json',
|
||||
name: 'File Manager',
|
||||
resourcesCount: 2,
|
||||
toolsCount: 5,
|
||||
updatedAt: UPDATED_AT,
|
||||
},
|
||||
{
|
||||
author: 'LobeHub',
|
||||
capabilities: { prompts: true, resources: false, tools: true },
|
||||
category: 'productivity',
|
||||
connectionType: 'http',
|
||||
createdAt: CREATED_AT,
|
||||
description: 'Productivity search MCP fixture.',
|
||||
github: { stars: 600, url: 'https://github.com/lobehub/e2e-search-mcp' },
|
||||
icon: '🔍',
|
||||
identifier: 'web-search',
|
||||
installCount: 260,
|
||||
installationMethods: 'docker',
|
||||
isClaimed: false,
|
||||
isFeatured: false,
|
||||
isOfficial: false,
|
||||
isValidated: true,
|
||||
manifestUrl: 'https://example.com/web-search/manifest.json',
|
||||
name: 'Web Search',
|
||||
promptsCount: 1,
|
||||
toolsCount: 2,
|
||||
updatedAt: UPDATED_AT,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockMcpList: McpListResponse = {
|
||||
categories: ['business', 'developer', 'productivity'],
|
||||
currentPage: 1,
|
||||
items: mockMcpItems,
|
||||
pageSize: 21,
|
||||
totalCount: mockMcpItems.length,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
export const mockMcpCategories = [
|
||||
{ id: 'search', name: 'Search' },
|
||||
{ id: 'file', name: 'File' },
|
||||
{ id: 'database', name: 'Database' },
|
||||
{ id: 'utility', name: 'Utility' },
|
||||
{ category: 'business', count: 7 },
|
||||
{ category: 'developer', count: 5 },
|
||||
{ category: 'productivity', count: 3 },
|
||||
];
|
||||
|
||||
// ============================================
|
||||
// Detail Mock Data
|
||||
// ============================================
|
||||
|
||||
export const mockAssistantDetails = mockAssistantItems.map((item) => ({
|
||||
...item,
|
||||
currentVersion: '1.0.0',
|
||||
related: mockAssistantItems
|
||||
.filter((related) => related.identifier !== item.identifier)
|
||||
.slice(0, 3),
|
||||
versions: [
|
||||
{
|
||||
createdAt: item.createdAt,
|
||||
isLatest: true,
|
||||
isValidated: true,
|
||||
status: 'published',
|
||||
version: '1.0.0',
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
export const mockMcpDetails = mockMcpItems.map((item) => ({
|
||||
...item,
|
||||
author: { name: item.author ?? 'LobeHub', url: 'https://lobehub.com' },
|
||||
deploymentOptions: [
|
||||
{
|
||||
connection: { command: 'npx', type: item.connectionType ?? 'stdio' },
|
||||
installationMethod: item.installationMethods ?? 'npm',
|
||||
title: 'E2E recommended deployment',
|
||||
},
|
||||
],
|
||||
overview: {
|
||||
readme: `# ${item.name}\n\n${item.description}`,
|
||||
summary: item.description,
|
||||
},
|
||||
related: mockMcpItems.filter((related) => related.identifier !== item.identifier).slice(0, 2),
|
||||
tools: [{ description: 'Fixture tool for E2E tests', name: 'fixtureTool' }],
|
||||
version: '1.0.0',
|
||||
versions: [{ isLatest: true, version: '1.0.0' }],
|
||||
}));
|
||||
|
||||
export const mockModelDetails = mockModelItems.map((item) => ({
|
||||
...item,
|
||||
providers: mockProviderItems.map((provider) => ({
|
||||
...provider,
|
||||
id: provider.identifier,
|
||||
model: item,
|
||||
})),
|
||||
related: mockModelItems.filter((related) => related.identifier !== item.identifier).slice(0, 2),
|
||||
}));
|
||||
|
||||
export const mockProviderDetails = mockProviderItems.map((item) => ({
|
||||
...item,
|
||||
models: mockModelItems
|
||||
.filter((model) => item.models.includes(model.identifier))
|
||||
.map((model) => ({ ...model, maxOutput: 4096 })),
|
||||
readme: `# ${item.name}\n\n${item.description}`,
|
||||
related: mockProviderItems
|
||||
.filter((related) => related.identifier !== item.identifier)
|
||||
.slice(0, 2),
|
||||
}));
|
||||
|
||||
+312
-145
@@ -1,179 +1,346 @@
|
||||
/**
|
||||
* Mock handlers for Discover/Community API endpoints
|
||||
* Mock handlers for Discover/Community API endpoints.
|
||||
*/
|
||||
import type { Route } from 'playwright';
|
||||
import type { Request, Route } from 'playwright';
|
||||
import superjson from 'superjson';
|
||||
|
||||
import { type MockHandler, createTrpcResponse } from '../index';
|
||||
import type { MockHandler } from '../index';
|
||||
import {
|
||||
mockAssistantCategories,
|
||||
mockAssistantDetails,
|
||||
mockAssistantItems,
|
||||
mockAssistantList,
|
||||
mockMcpCategories,
|
||||
mockMcpDetails,
|
||||
mockMcpItems,
|
||||
mockMcpList,
|
||||
mockModelDetails,
|
||||
mockModelItems,
|
||||
mockModelList,
|
||||
mockProviderDetails,
|
||||
mockProviderItems,
|
||||
mockProviderList,
|
||||
} from './data';
|
||||
|
||||
// ============================================
|
||||
// Helper to parse tRPC batch requests
|
||||
// ============================================
|
||||
interface IdentifierEntry {
|
||||
identifier: string;
|
||||
lastModified: string;
|
||||
}
|
||||
|
||||
function parseTrpcUrl(url: string): { input?: Record<string, unknown>; procedure: string } {
|
||||
const urlObj = new URL(url);
|
||||
const pathname = urlObj.pathname;
|
||||
const SUCCESS_RESPONSE = { success: true };
|
||||
|
||||
// Extract procedure name from path like /trpc/lambda.market.getAssistantList
|
||||
const procedureMatch = pathname.match(/lambda\.market\.(\w+)/);
|
||||
const procedure = procedureMatch ? procedureMatch[1] : '';
|
||||
const createTrpcResult = <T>(data: T) => ({
|
||||
result: {
|
||||
data: superjson.serialize(data),
|
||||
},
|
||||
});
|
||||
|
||||
// Parse input from query string
|
||||
let input: Record<string, unknown> | undefined;
|
||||
const inputParam = urlObj.searchParams.get('input');
|
||||
if (inputParam) {
|
||||
const createTrpcResponse = <T>(data: T): string => JSON.stringify(createTrpcResult(data));
|
||||
|
||||
const createTrpcBatchResponse = <T>(data: T[]): string =>
|
||||
JSON.stringify(data.map((item) => createTrpcResult(item)));
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
|
||||
const getStringInput = (input: unknown, key: string): string | undefined => {
|
||||
if (!isRecord(input)) return undefined;
|
||||
const value = input[key];
|
||||
return typeof value === 'string' ? value : undefined;
|
||||
};
|
||||
|
||||
const getNumberInput = (input: unknown, key: string): number | undefined => {
|
||||
if (!isRecord(input)) return undefined;
|
||||
const value = input[key];
|
||||
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
||||
if (typeof value !== 'string') return undefined;
|
||||
|
||||
const numberValue = Number(value);
|
||||
return Number.isFinite(numberValue) ? numberValue : undefined;
|
||||
};
|
||||
|
||||
const createIdentifiers = (
|
||||
items: { identifier: string; updatedAt?: string }[],
|
||||
): IdentifierEntry[] =>
|
||||
items.map((item) => ({ identifier: item.identifier, lastModified: item.updatedAt ?? '' }));
|
||||
|
||||
const unwrapTrpcInput = (input: unknown): unknown => {
|
||||
if (!isRecord(input)) return input;
|
||||
|
||||
if ('json' in input) return input.json;
|
||||
|
||||
return input;
|
||||
};
|
||||
|
||||
const parseRequestInput = (request: Request, url: URL): unknown => {
|
||||
const input = url.searchParams.get('input');
|
||||
|
||||
if (input) {
|
||||
try {
|
||||
input = JSON.parse(inputParam);
|
||||
return JSON.parse(input);
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return { input, procedure };
|
||||
}
|
||||
try {
|
||||
return request.postDataJSON();
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// Mock Handlers
|
||||
// ============================================
|
||||
const getProcedureInputs = (request: Request, url: URL, count: number): unknown[] => {
|
||||
const rawInput = parseRequestInput(request, url);
|
||||
const isBatch = url.searchParams.get('batch') === '1' || count > 1;
|
||||
|
||||
/**
|
||||
* Handler for assistant list endpoint
|
||||
*/
|
||||
const assistantListHandler: MockHandler = {
|
||||
if (isBatch && isRecord(rawInput)) {
|
||||
return Array.from({ length: count }, (_, index) => unwrapTrpcInput(rawInput[String(index)]));
|
||||
}
|
||||
|
||||
return [unwrapTrpcInput(rawInput)];
|
||||
};
|
||||
|
||||
const getProcedures = (url: URL): string[] => {
|
||||
const marker = '/trpc/lambda/';
|
||||
const pathname = decodeURIComponent(url.pathname);
|
||||
const markerIndex = pathname.indexOf(marker);
|
||||
|
||||
if (markerIndex === -1) return [];
|
||||
|
||||
const procedureSegment = pathname.slice(markerIndex + marker.length);
|
||||
return procedureSegment.split(',').filter(Boolean);
|
||||
};
|
||||
|
||||
const isMarketProcedure = (procedure: string): boolean => procedure.startsWith('market.');
|
||||
|
||||
const matchesText = (value: string | undefined, query: string) =>
|
||||
value?.toLowerCase().includes(query.toLowerCase()) ?? false;
|
||||
|
||||
const paginate = <T>(items: T[], input: unknown, fallbackTotal = items.length) => {
|
||||
const page = getNumberInput(input, 'page') ?? 1;
|
||||
const pageSize = getNumberInput(input, 'pageSize') ?? 21;
|
||||
|
||||
return {
|
||||
currentPage: page,
|
||||
items,
|
||||
pageSize,
|
||||
totalCount: Math.max(fallbackTotal, items.length),
|
||||
totalPages: Math.max(1, Math.ceil(Math.max(fallbackTotal, items.length) / pageSize)),
|
||||
};
|
||||
};
|
||||
|
||||
const getAssistantList = (input: unknown) => {
|
||||
const category = getStringInput(input, 'category');
|
||||
const query = getStringInput(input, 'q');
|
||||
|
||||
let items = mockAssistantItems;
|
||||
|
||||
if (category && !['all', 'discover'].includes(category)) {
|
||||
const filtered = items.filter((item) => item.category === category);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
const filtered = items.filter(
|
||||
(item) =>
|
||||
matchesText(item.title, query) ||
|
||||
matchesText(item.description, query) ||
|
||||
matchesText(item.identifier, query) ||
|
||||
matchesText(item.tags?.join(' '), query),
|
||||
);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
return { ...mockAssistantList, ...paginate(items, input, 42) };
|
||||
};
|
||||
|
||||
const getMcpList = (input: unknown) => {
|
||||
const category = getStringInput(input, 'category');
|
||||
const query = getStringInput(input, 'q');
|
||||
|
||||
let items = mockMcpItems;
|
||||
|
||||
if (category && !['all', 'discover'].includes(category)) {
|
||||
const filtered = items.filter((item) => item.category === category);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
if (query) {
|
||||
const filtered = items.filter(
|
||||
(item) =>
|
||||
matchesText(item.name, query) ||
|
||||
matchesText(item.description, query) ||
|
||||
matchesText(item.identifier, query),
|
||||
);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
return { ...mockMcpList, ...paginate(items, input), categories: mockMcpList.categories };
|
||||
};
|
||||
|
||||
const getModelList = (input: unknown) => {
|
||||
const query = getStringInput(input, 'q');
|
||||
|
||||
let items = mockModelItems;
|
||||
if (query) {
|
||||
const filtered = items.filter(
|
||||
(item) =>
|
||||
matchesText(item.displayName, query) ||
|
||||
matchesText(item.description, query) ||
|
||||
matchesText(item.identifier, query),
|
||||
);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
return { ...mockModelList, ...paginate(items, input) };
|
||||
};
|
||||
|
||||
const getProviderList = (input: unknown) => {
|
||||
const query = getStringInput(input, 'q');
|
||||
|
||||
let items = mockProviderItems;
|
||||
if (query) {
|
||||
const filtered = items.filter(
|
||||
(item) =>
|
||||
matchesText(item.name, query) ||
|
||||
matchesText(item.description, query) ||
|
||||
matchesText(item.identifier, query),
|
||||
);
|
||||
if (filtered.length > 0) items = filtered;
|
||||
}
|
||||
|
||||
return { ...mockProviderList, ...paginate(items, input) };
|
||||
};
|
||||
|
||||
const findByIdentifier = <T extends { identifier: string }>(items: T[], input: unknown): T => {
|
||||
const identifier = getStringInput(input, 'identifier');
|
||||
return items.find((item) => item.identifier === identifier) ?? items[0];
|
||||
};
|
||||
|
||||
const getMockResponse = (procedure: string, input: unknown): unknown => {
|
||||
switch (procedure) {
|
||||
case 'market.getAssistantCategories': {
|
||||
return mockAssistantCategories;
|
||||
}
|
||||
|
||||
case 'market.getAssistantDetail': {
|
||||
return findByIdentifier(mockAssistantDetails, input);
|
||||
}
|
||||
|
||||
case 'market.getAssistantIdentifiers': {
|
||||
return createIdentifiers(mockAssistantItems);
|
||||
}
|
||||
|
||||
case 'market.getAssistantList': {
|
||||
return getAssistantList(input);
|
||||
}
|
||||
|
||||
case 'market.getMcpCategories': {
|
||||
return mockMcpCategories;
|
||||
}
|
||||
|
||||
case 'market.getMcpDetail': {
|
||||
return findByIdentifier(mockMcpDetails, input);
|
||||
}
|
||||
|
||||
case 'market.getMcpList': {
|
||||
return getMcpList(input);
|
||||
}
|
||||
|
||||
case 'market.getModelCategories': {
|
||||
return [];
|
||||
}
|
||||
|
||||
case 'market.getModelDetail': {
|
||||
return findByIdentifier(mockModelDetails, input);
|
||||
}
|
||||
|
||||
case 'market.getModelIdentifiers': {
|
||||
return createIdentifiers(mockModelItems);
|
||||
}
|
||||
|
||||
case 'market.getModelList': {
|
||||
return getModelList(input);
|
||||
}
|
||||
|
||||
case 'market.getProviderDetail': {
|
||||
return findByIdentifier(mockProviderDetails, input);
|
||||
}
|
||||
|
||||
case 'market.getProviderIdentifiers': {
|
||||
return createIdentifiers(mockProviderItems);
|
||||
}
|
||||
|
||||
case 'market.getProviderList': {
|
||||
return getProviderList(input);
|
||||
}
|
||||
|
||||
case 'market.registerClientInMarketplace': {
|
||||
return { clientId: 'e2e-market-client', clientSecret: 'e2e-market-secret' };
|
||||
}
|
||||
|
||||
case 'market.registerM2MToken': {
|
||||
return SUCCESS_RESPONSE;
|
||||
}
|
||||
|
||||
case 'market.reportAgentEvent':
|
||||
case 'market.reportAgentInstall':
|
||||
case 'market.reportCall':
|
||||
case 'market.reportGroupAgentEvent':
|
||||
case 'market.reportGroupAgentInstall':
|
||||
case 'market.reportMcpEvent':
|
||||
case 'market.reportMcpInstallResult': {
|
||||
return SUCCESS_RESPONSE;
|
||||
}
|
||||
|
||||
case 'plugin.getPlugins': {
|
||||
return [];
|
||||
}
|
||||
|
||||
default: {
|
||||
console.log(` ⚠️ Unhandled mocked lambda endpoint: ${procedure}`);
|
||||
return SUCCESS_RESPONSE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const marketHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
const request = route.request();
|
||||
const url = new URL(request.url());
|
||||
const procedures = getProcedures(url);
|
||||
|
||||
if (!procedures.some(isMarketProcedure)) {
|
||||
await route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
const inputs = getProcedureInputs(request, url, procedures.length);
|
||||
|
||||
// Keep tRPC batch positions intact. Community pages can batch mocked
|
||||
// market.* calls with normal app calls, such as plugin.getPlugins on the MCP
|
||||
// detail page; returning only market responses would make the batch client
|
||||
// read the wrong result for subsequent procedures.
|
||||
const responses = procedures.map((procedure, index) =>
|
||||
getMockResponse(procedure, inputs[index]),
|
||||
);
|
||||
const isBatch = url.searchParams.get('batch') === '1' || procedures.length > 1;
|
||||
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockAssistantList),
|
||||
body: isBatch ? createTrpcBatchResponse(responses) : createTrpcResponse(responses[0]),
|
||||
contentType: 'application/json',
|
||||
headers: {
|
||||
'Set-Cookie': 'mp_token_status=active; Path=/; SameSite=Lax',
|
||||
},
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getAssistantList**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for assistant categories endpoint
|
||||
*/
|
||||
const assistantCategoriesHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockAssistantCategories),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getAssistantCategories**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for model list endpoint
|
||||
*/
|
||||
const modelListHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockModelList),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getModelList**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for provider list endpoint
|
||||
*/
|
||||
const providerListHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockProviderList),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getProviderList**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for MCP list endpoint
|
||||
*/
|
||||
const mcpListHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockMcpList),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getMcpList**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for MCP categories endpoint
|
||||
*/
|
||||
const mcpCategoriesHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse(mockMcpCategories),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.getMcpCategories**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Debug handler to log all trpc requests
|
||||
*/
|
||||
const trpcDebugHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
const url = route.request().url();
|
||||
console.log(` 🔍 TRPC Request: ${url}`);
|
||||
await route.continue();
|
||||
},
|
||||
pattern: '**/trpc/**',
|
||||
};
|
||||
|
||||
/**
|
||||
* Fallback handler for any unhandled market endpoints
|
||||
* Returns empty data to prevent hanging requests
|
||||
*/
|
||||
const marketFallbackHandler: MockHandler = {
|
||||
handler: async (route: Route) => {
|
||||
const url = route.request().url();
|
||||
const { procedure } = parseTrpcUrl(url);
|
||||
|
||||
console.log(` ⚠️ Unhandled market endpoint: ${procedure}`);
|
||||
|
||||
// Return empty response to prevent timeout
|
||||
await route.fulfill({
|
||||
body: createTrpcResponse({ items: [], pagination: { page: 1, pageSize: 12, total: 0 } }),
|
||||
contentType: 'application/json',
|
||||
status: 200,
|
||||
});
|
||||
},
|
||||
pattern: '**/trpc/lambda/market.**',
|
||||
pattern: '**/trpc/lambda/**',
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// Export all handlers
|
||||
// ============================================
|
||||
|
||||
export const discoverHandlers: MockHandler[] = [
|
||||
// Debug handler first to log all requests
|
||||
trpcDebugHandler,
|
||||
// Specific handlers (order matters - more specific first)
|
||||
assistantListHandler,
|
||||
assistantCategoriesHandler,
|
||||
modelListHandler,
|
||||
providerListHandler,
|
||||
mcpListHandler,
|
||||
mcpCategoriesHandler,
|
||||
// Fallback handler (should be last)
|
||||
marketFallbackHandler,
|
||||
];
|
||||
export const discoverHandlers: MockHandler[] = [marketHandler];
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/**
|
||||
* Type definitions for Discover mock data
|
||||
* These mirror the actual types from the application
|
||||
* Type definitions for Discover mock data.
|
||||
*
|
||||
* Keep these small and E2E-focused: they only include fields the Community UI
|
||||
* reads while rendering list and detail pages.
|
||||
*/
|
||||
|
||||
export interface PaginationInfo {
|
||||
page: number;
|
||||
export interface ListResponse<T> {
|
||||
currentPage: number;
|
||||
items: T[];
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalCount: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
@@ -19,21 +22,25 @@ export interface DiscoverAssistantItem {
|
||||
avatar: string;
|
||||
backgroundColor?: string;
|
||||
category: string;
|
||||
config?: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
description: string;
|
||||
examples?: Record<string, unknown>[];
|
||||
identifier: string;
|
||||
installCount?: number;
|
||||
knowledgeCount?: number;
|
||||
pluginCount?: number;
|
||||
related?: DiscoverAssistantItem[];
|
||||
summary?: string;
|
||||
tags?: string[];
|
||||
title: string;
|
||||
tokenUsage?: number;
|
||||
type?: 'agent' | 'agent-group';
|
||||
updatedAt?: string;
|
||||
userName?: string;
|
||||
}
|
||||
|
||||
export interface AssistantListResponse {
|
||||
items: DiscoverAssistantItem[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
export type AssistantListResponse = ListResponse<DiscoverAssistantItem>;
|
||||
|
||||
// ============================================
|
||||
// Model Types
|
||||
@@ -46,19 +53,17 @@ export interface DiscoverModelItem {
|
||||
vision?: boolean;
|
||||
};
|
||||
contextWindowTokens: number;
|
||||
createdAt: string;
|
||||
description: string;
|
||||
displayName: string;
|
||||
id: string;
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
identifier: string;
|
||||
providerCount: number;
|
||||
providers: string[];
|
||||
releasedAt?: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface ModelListResponse {
|
||||
items: DiscoverModelItem[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
export type ModelListResponse = ListResponse<DiscoverModelItem>;
|
||||
|
||||
// ============================================
|
||||
// Provider Types
|
||||
@@ -66,33 +71,50 @@ export interface ModelListResponse {
|
||||
|
||||
export interface DiscoverProviderItem {
|
||||
description: string;
|
||||
id: string;
|
||||
logo?: string;
|
||||
identifier: string;
|
||||
modelCount: number;
|
||||
models: string[];
|
||||
name: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface ProviderListResponse {
|
||||
items: DiscoverProviderItem[];
|
||||
pagination: PaginationInfo;
|
||||
}
|
||||
export type ProviderListResponse = ListResponse<DiscoverProviderItem>;
|
||||
|
||||
// ============================================
|
||||
// MCP Types
|
||||
// ============================================
|
||||
|
||||
export interface DiscoverMcpItem {
|
||||
author: string;
|
||||
avatar: string;
|
||||
author?: string;
|
||||
capabilities: {
|
||||
prompts: boolean;
|
||||
resources: boolean;
|
||||
tools: boolean;
|
||||
};
|
||||
category: string;
|
||||
connectionType?: 'http' | 'stdio';
|
||||
createdAt: string;
|
||||
description: string;
|
||||
github?: {
|
||||
stars?: number;
|
||||
url: string;
|
||||
};
|
||||
icon?: string;
|
||||
identifier: string;
|
||||
installationMethods?: string;
|
||||
installCount?: number;
|
||||
title: string;
|
||||
isClaimed?: boolean;
|
||||
isFeatured?: boolean;
|
||||
isOfficial?: boolean;
|
||||
isValidated?: boolean;
|
||||
manifestUrl: string;
|
||||
name: string;
|
||||
promptsCount?: number;
|
||||
resourcesCount?: number;
|
||||
toolsCount?: number;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface McpListResponse {
|
||||
items: DiscoverMcpItem[];
|
||||
pagination: PaginationInfo;
|
||||
export interface McpListResponse extends ListResponse<DiscoverMcpItem> {
|
||||
categories: string[];
|
||||
}
|
||||
|
||||
+16
-4
@@ -5,6 +5,7 @@
|
||||
* It uses Playwright's route interception to mock tRPC and REST API calls.
|
||||
*/
|
||||
import type { Page, Route } from 'playwright';
|
||||
import superjson from 'superjson';
|
||||
|
||||
import { discoverMocks } from './community';
|
||||
|
||||
@@ -124,12 +125,23 @@ export class MockManager {
|
||||
/**
|
||||
* Create a JSON response for tRPC endpoints
|
||||
*/
|
||||
export function createTrpcResponse<T>(data: T): string {
|
||||
return JSON.stringify({
|
||||
export function createTrpcResult<T>(data: T) {
|
||||
return {
|
||||
result: {
|
||||
data,
|
||||
data: superjson.serialize(data),
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createTrpcResponse<T>(data: T): string {
|
||||
return JSON.stringify(createTrpcResult(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JSON response for batched tRPC endpoints
|
||||
*/
|
||||
export function createTrpcBatchResponse<T>(data: T[]): string {
|
||||
return JSON.stringify(data.map((item) => createTrpcResult(item)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-2
@@ -1,6 +1,7 @@
|
||||
import { After, AfterAll, Before, BeforeAll, setDefaultTimeout, Status } from '@cucumber/cucumber';
|
||||
import { chromium, type Cookie } from 'playwright';
|
||||
|
||||
import { mockManager } from '../mocks';
|
||||
import { seedTestUser, TEST_USER } from '../support/seedTestUser';
|
||||
import { startWebServer, stopWebServer } from '../support/webServer';
|
||||
import type { CustomWorld } from '../support/world';
|
||||
@@ -106,8 +107,16 @@ Before(async function (this: CustomWorld, { pickle }) {
|
||||
);
|
||||
console.log(`\n📝 Running: ${pickle.name}${testId ? ` (${testId.name.replace('@', '')})` : ''}`);
|
||||
|
||||
// Setup API mocks before any page navigation
|
||||
// await mockManager.setup(this.page);
|
||||
// Setup Community API mocks before any page navigation. These PR E2E scenarios
|
||||
// are the user-experience baseline for Community UI flows (list/search/filter/
|
||||
// detail navigation), not a live marketplace availability check. The live
|
||||
// marketplace rate-limits anonymous CI traffic, so Community scenarios use
|
||||
// deterministic fixtures while the rest of the E2E suite keeps real app APIs.
|
||||
// If we need to validate the real marketplace contract, cover that in a
|
||||
// separate integration/nightly suite with dedicated credentials and SLA.
|
||||
if (pickle.tags.some((tag) => tag.name === '@community')) {
|
||||
await mockManager.setup(this.page);
|
||||
}
|
||||
|
||||
// Set cached session cookies to skip login
|
||||
if (sessionCookies.length > 0) {
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "مرحبًا، أنا **{{name}}**. جملة واحدة تكفي.\n\nهل ترغب في أن أتناسب مع سير عملك بشكل أفضل؟ انتقل إلى [إعدادات الوكيل]({{url}}) واملأ ملف تعريف الوكيل (يمكنك تعديله في أي وقت).",
|
||||
"agentDefaultMessageWithSystemRole": "مرحبًا، أنا **{{name}}**. جملة واحدة تكفي — أنت المتحكم.",
|
||||
"agentDefaultMessageWithoutEdit": "مرحبًا، أنا **{{name}}**. جملة واحدة تكفي — أنت المتحكم.",
|
||||
"agentDocument.backToChat": "العودة إلى الدردشة",
|
||||
"agentDocument.linkCopied": "تم نسخ الرابط",
|
||||
"agentDocument.openAsPage": "افتح كصفحة كاملة",
|
||||
"agentProfile.files_one": "{{count}} ملف",
|
||||
"agentProfile.files_other": "{{count}} ملفات",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} قاعدة معرفة",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "استخراج محتوى رابط الويب",
|
||||
"followUpPlaceholder": "متابعة. @ لإسناد مهام لوكلاء آخرين.",
|
||||
"followUpPlaceholderHeterogeneous": "تابع.",
|
||||
"gatewayMode.title": "وضع البوابة",
|
||||
"gatewayMode.beta": "تجريبي",
|
||||
"gatewayMode.cardTitle": "وضع بوابة الوكيل",
|
||||
"gatewayMode.desc": "قم بتشغيل الوكلاء في السحابة من خلال بوابة الوكلاء الخاصة بـ LobeHub. تستمر المهام في العمل حتى بعد إغلاق الصفحة.",
|
||||
"group.desc": "ادفع المهمة للأمام مع عدة وكلاء في مساحة مشتركة واحدة.",
|
||||
"group.memberTooltip": "يوجد {{count}} عضو في المجموعة",
|
||||
"group.orchestratorThinking": "المنسق يفكر...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "تفويض",
|
||||
"toolAuth.authorizing": "جارٍ التفويض...",
|
||||
"toolAuth.hint": "بدون التفويض أو الإعداد، قد لا تعمل المهارات. قد يؤدي ذلك إلى تقييد الوكيل أو حدوث أخطاء.",
|
||||
"toolAuth.remove": "إزالة",
|
||||
"toolAuth.signIn": "تسجيل الدخول",
|
||||
"toolAuth.title": "تفويض المهارات لهذا الوكيل",
|
||||
"topic.checkOpenNewTopic": "هل تريد بدء موضوع جديد؟",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "المهارات",
|
||||
"workingPanel.space": "مسافة",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "أنت",
|
||||
"zenMode": "وضع التركيز"
|
||||
"you": "أنت"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "تم عرض جميع المهام الجارية",
|
||||
"fleet.backToHome": "العودة إلى الصفحة الرئيسية",
|
||||
"fleet.closeColumn": "إغلاق العمود",
|
||||
"fleet.closeIdleColumns": "إغلاق الأعمدة الخاملة",
|
||||
"fleet.closeIdleColumnsCount": "إغلاق {{count}} من الأعمدة الخاملة",
|
||||
"fleet.collapseReply": "طي",
|
||||
"fleet.createTask": "إنشاء مهمة",
|
||||
"fleet.dragHint": "اسحب لإعادة الترتيب",
|
||||
"fleet.empty": "لا توجد مهام مفتوحة",
|
||||
"fleet.emptyDesc": "اختر مهمة جارية على اليسار، أو استخدم + لإضافة عمود.",
|
||||
"fleet.noRunningTasks": "لا توجد مهام جارية",
|
||||
"fleet.openInChat": "فتح في الدردشة",
|
||||
"fleet.pin": "تثبيت العمود",
|
||||
"fleet.reply": "رد",
|
||||
"fleet.runningTasks": "المهام الجارية",
|
||||
"fleet.rows.one": "صف واحد",
|
||||
"fleet.rows.two": "صفان",
|
||||
"fleet.runningBoard": "لوحة التشغيل",
|
||||
"fleet.status.idle": "خامل",
|
||||
"fleet.status.paused": "متوقف مؤقتًا",
|
||||
"fleet.status.running": "قيد التشغيل",
|
||||
"fleet.status.scheduled": "مجدول",
|
||||
"fleet.tooltip": "عرض جميع الوكلاء جنبًا إلى جنب",
|
||||
"fleet.unpin": "إلغاء تثبيت العمود",
|
||||
"gateway.description": "الوصف",
|
||||
"gateway.descriptionPlaceholder": "اختياري",
|
||||
"gateway.deviceName": "اسم الجهاز",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "اكتشف MCP",
|
||||
"navigation.discoverModels": "اكتشف النماذج",
|
||||
"navigation.discoverProviders": "اكتشف المزودين",
|
||||
"navigation.document": "مستند",
|
||||
"navigation.group": "مجموعة",
|
||||
"navigation.groupChat": "محادثة جماعية",
|
||||
"navigation.home": "الرئيسية",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "المنتهية الصلاحية",
|
||||
"credits.packages.tabs.expiredCount": "منتهية الصلاحية ({{count}})",
|
||||
"credits.packages.title": "حزم الأرصدة الخاصة بي",
|
||||
"credits.topUp.bestValue.cta": "عرض السنوي النهائي",
|
||||
"credits.topUp.bestValue.savings": "وفر ${{savings}} على هذا الشراء",
|
||||
"credits.topUp.bestValue.title": "الخطة السنوية {{plan}} تتيح أقل معدل شحن: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "إلغاء",
|
||||
"credits.topUp.custom": "مخصص",
|
||||
"credits.topUp.freeFeeHint": "تشمل تعبئة الخطة المجانية رسوم خدمة بقيمة {{fee}} لكل مليون رصيد.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "معالجة المكافآت: سيتم توزيع الأرصدة خلال ساعة واحدة بعد أن يكمل المدعو الدفع ويجتاز التحقق",
|
||||
"referral.rules.title": "قواعد البرنامج",
|
||||
"referral.rules.validInvitation": "دعوة صالحة: يسجل المدعو باستخدام رمز الإحالة الخاص بك، وينفذ إجراءً صالحًا، ويكمل الدفع (اشتراك أو شحن أرصدة)",
|
||||
"referral.rules.validOperation": "معايير الإجراء الصالح: إرسال رسالة واحدة أو إنشاء صورة واحدة",
|
||||
"referral.stats.availableBalance": "الرصيد المتاح",
|
||||
"referral.stats.description": "عرض إحصائيات الإحالة الخاصة بك",
|
||||
"referral.stats.title": "نظرة عامة على الإحالة",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "مؤرشف",
|
||||
"management.status.completed": "مكتمل",
|
||||
"management.status.failed": "فشل",
|
||||
"management.status.idle": "خامل",
|
||||
"management.status.paused": "متوقف مؤقتًا",
|
||||
"management.status.running": "قيد التشغيل",
|
||||
"management.status.waitingForHuman": "في انتظار الإدخال",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} مواضيع فشلت",
|
||||
"projectStatus.loading_one": "{{count}} موضوع قيد التحميل",
|
||||
"projectStatus.loading_other": "{{count}} مواضيع قيد التحميل",
|
||||
"projectStatus.unread_one": "{{count}} موضوع يحتوي على رد غير مقروء",
|
||||
"projectStatus.unread_other": "{{count}} مواضيع تحتوي على ردود غير مقروءة",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} موضوع ينتظر الإدخال",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} مواضيع تنتظر الإدخال",
|
||||
"renameModal.description": "يُفضَّل أن يكون قصيرًا وسهل التعرّف.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Здравей, аз съм **{{name}}**. Едно изречение е достатъчно.\n\nИскате да се адаптирам по-добре към вашия работен процес? Отидете в [Настройки на Агента]({{url}}) и попълнете Профила на Агента (можете да го редактирате по всяко време).",
|
||||
"agentDefaultMessageWithSystemRole": "Здравей, аз съм **{{name}}**. Едно изречение е достатъчно — вие контролирате.",
|
||||
"agentDefaultMessageWithoutEdit": "Здравей, аз съм **{{name}}**. Едно изречение е достатъчно — вие контролирате.",
|
||||
"agentDocument.backToChat": "Обратно към чата",
|
||||
"agentDocument.linkCopied": "Връзката е копирана",
|
||||
"agentDocument.openAsPage": "Отвори като цяла страница",
|
||||
"agentProfile.files_one": "{{count}} файл",
|
||||
"agentProfile.files_other": "{{count}} файла",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} база знания",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Извличане на съдържание от уеб връзки",
|
||||
"followUpPlaceholder": "Последващо действие. Използвайте @, за да възлагате задачи на други агенти.",
|
||||
"followUpPlaceholderHeterogeneous": "Последващ въпрос.",
|
||||
"gatewayMode.title": "Режим на шлюз",
|
||||
"gatewayMode.beta": "Бета",
|
||||
"gatewayMode.cardTitle": "Режим на шлюза за агенти",
|
||||
"gatewayMode.desc": "Стартирайте агенти в облака чрез шлюза за агенти на LobeHub. Задачите продължават да се изпълняват дори след като затворите страницата.",
|
||||
"group.desc": "Придвижете задача напред с няколко Агента в едно споделено пространство.",
|
||||
"group.memberTooltip": "Групата има {{count}} член(а)",
|
||||
"group.orchestratorThinking": "Оркестраторът мисли...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Упълномощи",
|
||||
"toolAuth.authorizing": "Упълномощаване...",
|
||||
"toolAuth.hint": "Без упълномощаване или конфигурация, уменията може да не работят. Това може да ограничи агента или да доведе до грешки.",
|
||||
"toolAuth.remove": "Премахни",
|
||||
"toolAuth.signIn": "Вход",
|
||||
"toolAuth.title": "Упълномощи уменията за този агент",
|
||||
"topic.checkOpenNewTopic": "Да започнем нова тема?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Умения",
|
||||
"workingPanel.space": "Пространство",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Вие",
|
||||
"zenMode": "Режим Зен"
|
||||
"you": "Вие"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Всички текущи задачи са показани",
|
||||
"fleet.backToHome": "Обратно към началната страница",
|
||||
"fleet.closeColumn": "Затваряне на колона",
|
||||
"fleet.closeIdleColumns": "Затвори неактивни колони",
|
||||
"fleet.closeIdleColumnsCount": "Затвори {{count}} неактивни колони",
|
||||
"fleet.collapseReply": "Свий",
|
||||
"fleet.createTask": "Създаване на задача",
|
||||
"fleet.dragHint": "Плъзнете, за да пренаредите",
|
||||
"fleet.empty": "Няма отворени задачи",
|
||||
"fleet.emptyDesc": "Изберете текуща задача отляво или използвайте +, за да добавите колона.",
|
||||
"fleet.noRunningTasks": "Няма текущи задачи",
|
||||
"fleet.openInChat": "Отваряне в чата",
|
||||
"fleet.pin": "Закачи колона",
|
||||
"fleet.reply": "Отговор",
|
||||
"fleet.runningTasks": "Текущи задачи",
|
||||
"fleet.rows.one": "Един ред",
|
||||
"fleet.rows.two": "Два реда",
|
||||
"fleet.runningBoard": "Текуща дъска",
|
||||
"fleet.status.idle": "Неактивен",
|
||||
"fleet.status.paused": "Пауза",
|
||||
"fleet.status.running": "В процес на изпълнение",
|
||||
"fleet.status.scheduled": "Планирано",
|
||||
"fleet.tooltip": "Преглед на всички агенти един до друг",
|
||||
"fleet.unpin": "Откачи колона",
|
||||
"gateway.description": "Описание",
|
||||
"gateway.descriptionPlaceholder": "По избор",
|
||||
"gateway.deviceName": "Име на устройството",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Откриване на MCP",
|
||||
"navigation.discoverModels": "Откриване на Модели",
|
||||
"navigation.discoverProviders": "Откриване на Доставчици",
|
||||
"navigation.document": "Документ",
|
||||
"navigation.group": "Група",
|
||||
"navigation.groupChat": "Групов Чат",
|
||||
"navigation.home": "Начало",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Изтекли",
|
||||
"credits.packages.tabs.expiredCount": "Изтекли ({{count}})",
|
||||
"credits.packages.title": "Моите пакети с кредити",
|
||||
"credits.topUp.bestValue.cta": "Вижте Ultimate годишен",
|
||||
"credits.topUp.bestValue.savings": "Спестете ${{savings}} при тази покупка",
|
||||
"credits.topUp.bestValue.title": "{{plan}} годишен отключва най-ниската ставка за зареждане: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Отказ",
|
||||
"credits.topUp.custom": "По избор",
|
||||
"credits.topUp.freeFeeHint": "Зарежданията на безплатния план включват такса за услуга от {{fee}} на 1M кредити.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Обработка на наградите: Кредитите ще бъдат разпределени в рамките на 1 час след като поканеният завърши плащане и премине проверка.",
|
||||
"referral.rules.title": "Правила на програмата",
|
||||
"referral.rules.validInvitation": "Валидна покана: Поканеният се регистрира с вашия код за препоръка, извършва едно валидно действие и завършва плащане (абонамент или зареждане на кредити).",
|
||||
"referral.rules.validOperation": "Критерии за валидно действие: Изпращане на съобщение в Chat страницата или генериране на изображение",
|
||||
"referral.stats.availableBalance": "Налично салдо",
|
||||
"referral.stats.description": "Вижте статистиката на вашите покани",
|
||||
"referral.stats.title": "Обзор на поканите",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Архивирани",
|
||||
"management.status.completed": "Завършени",
|
||||
"management.status.failed": "Неуспешни",
|
||||
"management.status.idle": "Неактивен",
|
||||
"management.status.paused": "Паузирани",
|
||||
"management.status.running": "В процес",
|
||||
"management.status.waitingForHuman": "Очаква въвеждане",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} неуспешни теми",
|
||||
"projectStatus.loading_one": "{{count}} зареждаща се тема",
|
||||
"projectStatus.loading_other": "{{count}} зареждащи се теми",
|
||||
"projectStatus.unread_one": "{{count}} тема с непрочетен отговор",
|
||||
"projectStatus.unread_other": "{{count}} теми с непрочетени отговори",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} тема, очакваща въвеждане",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} теми, очакващи въвеждане",
|
||||
"renameModal.description": "Поддържайте го кратко и лесно за разпознаване.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Hallo, ich bin **{{name}}**. Ein Satz genügt.\n\nMöchten Sie, dass ich besser zu Ihrem Arbeitsablauf passe? Gehen Sie zu [Agenteneinstellungen]({{url}}) und füllen Sie das Agentenprofil aus (Sie können es jederzeit bearbeiten).",
|
||||
"agentDefaultMessageWithSystemRole": "Hallo, ich bin **{{name}}**. Ein Satz genügt – Sie haben die Kontrolle.",
|
||||
"agentDefaultMessageWithoutEdit": "Hallo, ich bin **{{name}}**. Ein Satz genügt – Sie haben die Kontrolle.",
|
||||
"agentDocument.backToChat": "Zurück zum Chat",
|
||||
"agentDocument.linkCopied": "Link kopiert",
|
||||
"agentDocument.openAsPage": "Als vollständige Seite öffnen",
|
||||
"agentProfile.files_one": "{{count}} Datei",
|
||||
"agentProfile.files_other": "{{count}} Dateien",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} Wissensbasis",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Webseiteninhalte extrahieren",
|
||||
"followUpPlaceholder": "Folgen Sie nach. @, um Aufgaben anderen Agenten zuzuweisen.",
|
||||
"followUpPlaceholderHeterogeneous": "Weiter ausführen.",
|
||||
"gatewayMode.title": "Gateway-Modus",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Agent-Gateway-Modus",
|
||||
"gatewayMode.desc": "Führen Sie Agenten in der Cloud über LobeHubs Agent-Gateway aus. Aufgaben laufen weiter, auch nachdem Sie die Seite geschlossen haben.",
|
||||
"group.desc": "Bringen Sie eine Aufgabe mit mehreren Agenten in einem gemeinsamen Raum voran.",
|
||||
"group.memberTooltip": "Es gibt {{count}} Mitglieder in der Gruppe",
|
||||
"group.orchestratorThinking": "Orchestrator denkt nach...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autorisieren",
|
||||
"toolAuth.authorizing": "Autorisierung läuft...",
|
||||
"toolAuth.hint": "Ohne Autorisierung oder Konfiguration funktionieren Skills möglicherweise nicht. Dies kann den Agenten einschränken oder zu Fehlern führen.",
|
||||
"toolAuth.remove": "Entfernen",
|
||||
"toolAuth.signIn": "Anmelden",
|
||||
"toolAuth.title": "Skills für diesen Agenten autorisieren",
|
||||
"topic.checkOpenNewTopic": "Neues Thema starten?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Fähigkeiten",
|
||||
"workingPanel.space": "Leerzeichen",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Du",
|
||||
"zenMode": "Zen-Modus"
|
||||
"you": "Du"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Alle laufenden Aufgaben werden angezeigt",
|
||||
"fleet.backToHome": "Zurück zur Startseite",
|
||||
"fleet.closeColumn": "Spalte schließen",
|
||||
"fleet.closeIdleColumns": "Leere Spalten schließen",
|
||||
"fleet.closeIdleColumnsCount": "{{count}} leere Spalten schließen",
|
||||
"fleet.collapseReply": "Einklappen",
|
||||
"fleet.createTask": "Aufgabe erstellen",
|
||||
"fleet.dragHint": "Ziehen, um neu anzuordnen",
|
||||
"fleet.empty": "Keine offenen Aufgaben",
|
||||
"fleet.emptyDesc": "Wählen Sie eine laufende Aufgabe links aus oder verwenden Sie +, um eine Spalte hinzuzufügen.",
|
||||
"fleet.noRunningTasks": "Keine laufenden Aufgaben",
|
||||
"fleet.openInChat": "Im Chat öffnen",
|
||||
"fleet.pin": "Spalte anheften",
|
||||
"fleet.reply": "Antworten",
|
||||
"fleet.runningTasks": "Laufende Aufgaben",
|
||||
"fleet.rows.one": "Einzelne Zeile",
|
||||
"fleet.rows.two": "Zwei Zeilen",
|
||||
"fleet.runningBoard": "Laufendes Board",
|
||||
"fleet.status.idle": "Leerlauf",
|
||||
"fleet.status.paused": "Pausiert",
|
||||
"fleet.status.running": "Läuft",
|
||||
"fleet.status.scheduled": "Geplant",
|
||||
"fleet.tooltip": "Alle Agenten nebeneinander anzeigen",
|
||||
"fleet.unpin": "Spalte lösen",
|
||||
"gateway.description": "Beschreibung",
|
||||
"gateway.descriptionPlaceholder": "Optional",
|
||||
"gateway.deviceName": "Gerätename",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "MCP entdecken",
|
||||
"navigation.discoverModels": "Modelle entdecken",
|
||||
"navigation.discoverProviders": "Anbieter entdecken",
|
||||
"navigation.document": "Dokument",
|
||||
"navigation.group": "Gruppe",
|
||||
"navigation.groupChat": "Gruppen-Chat",
|
||||
"navigation.home": "Startseite",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Abgelaufen",
|
||||
"credits.packages.tabs.expiredCount": "Abgelaufen ({{count}})",
|
||||
"credits.packages.title": "Meine Guthabenpakete",
|
||||
"credits.topUp.bestValue.cta": "Ultimatives Jahresabo ansehen",
|
||||
"credits.topUp.bestValue.savings": "Sparen Sie ${{savings}} bei diesem Kauf",
|
||||
"credits.topUp.bestValue.title": "{{plan}} Jahresabo bietet den niedrigsten Aufladepreis: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Abbrechen",
|
||||
"credits.topUp.custom": "Benutzerdefiniert",
|
||||
"credits.topUp.freeFeeHint": "Aufladungen im kostenlosen Tarif beinhalten eine {{fee}} Servicegebühr pro 1M Credits.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Belohnungsverarbeitung: Credits werden innerhalb von 1 Stunde verteilt, nachdem der Eingeladene eine Zahlung abgeschlossen und die Verifizierung bestanden hat.",
|
||||
"referral.rules.title": "Programmregeln",
|
||||
"referral.rules.validInvitation": "Gültige Einladung: Der Eingeladene registriert sich mit Ihrem Einladungscode, führt eine gültige Aktion aus und schließt eine Zahlung ab (Abonnement oder Credit-Aufladung).",
|
||||
"referral.rules.validOperation": "Gültige Aktion: Eine Nachricht senden oder ein Bild generieren",
|
||||
"referral.stats.availableBalance": "Verfügbares Guthaben",
|
||||
"referral.stats.description": "Sehen Sie Ihre Empfehlungsstatistiken",
|
||||
"referral.stats.title": "Empfehlungsübersicht",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Archiviert",
|
||||
"management.status.completed": "Abgeschlossen",
|
||||
"management.status.failed": "Fehlgeschlagen",
|
||||
"management.status.idle": "Leerlauf",
|
||||
"management.status.paused": "Pausiert",
|
||||
"management.status.running": "Laufend",
|
||||
"management.status.waitingForHuman": "Wartet auf Eingabe",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} fehlgeschlagene Themen",
|
||||
"projectStatus.loading_one": "{{count}} ladendes Thema",
|
||||
"projectStatus.loading_other": "{{count}} ladende Themen",
|
||||
"projectStatus.unread_one": "{{count}} Thema mit ungelesener Antwort",
|
||||
"projectStatus.unread_other": "{{count}} Themen mit ungelesenen Antworten",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} Thema wartet auf Eingabe",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} Themen warten auf Eingabe",
|
||||
"renameModal.description": "Kurz und leicht erkennbar halten.",
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"channel.devWebhookProxyUrl": "HTTPS Tunnel URL",
|
||||
"channel.devWebhookProxyUrlHint": "Optional. HTTPS tunnel URL for forwarding webhook requests to local dev server.",
|
||||
"channel.disabled": "Disabled",
|
||||
"channel.discord.description": "Connect this assistant to Discord server for channel chat and direct messages.",
|
||||
"channel.discord.description": "Connect this agent to Discord server for channel chat and direct messages.",
|
||||
"channel.displayToolCalls": "Display Tool Calls",
|
||||
"channel.displayToolCallsHint": "Show tool call details during AI responses. When disabled, only the final response is displayed for a cleaner experience.",
|
||||
"channel.dm": "Direct Messages",
|
||||
@@ -79,7 +79,7 @@
|
||||
"channel.endpointUrl": "Webhook URL",
|
||||
"channel.endpointUrlHint": "Please copy this URL and paste it into the <bold>{{fieldName}}</bold> field in the {{name}} Developer Portal.",
|
||||
"channel.exportConfig": "Export Configuration",
|
||||
"channel.feishu.description": "Connect this assistant to Feishu for private and group chats.",
|
||||
"channel.feishu.description": "Connect this agent to Feishu for private and group chats.",
|
||||
"channel.feishu.webhookMigrationDesc": "WebSocket mode provides real-time event delivery without needing a public callback URL. To migrate, switch the Connection Mode to WebSocket in Advanced Settings. No additional configuration is needed on the Feishu/Lark Open Platform.",
|
||||
"channel.feishu.webhookMigrationTitle": "Consider migrating to WebSocket mode",
|
||||
"channel.groupAllowFrom": "Allowed Channels",
|
||||
@@ -135,7 +135,7 @@
|
||||
"channel.imessage.bridgeTestDisabledHint": "Enable the bridge service first.",
|
||||
"channel.imessage.bridgeTestFailed": "BlueBubbles test failed",
|
||||
"channel.imessage.bridgeTestSuccess": "BlueBubbles connection passed",
|
||||
"channel.imessage.description": "Connect this assistant to iMessage through the local LobeHub Desktop BlueBubbles bridge.",
|
||||
"channel.imessage.description": "Connect this agent to iMessage through the local LobeHub Desktop BlueBubbles bridge.",
|
||||
"channel.imessage.desktopBridge": "Desktop Bridge",
|
||||
"channel.imessage.desktopDeviceId": "Desktop Device ID",
|
||||
"channel.imessage.desktopDeviceIdHint": "The LobeHub Desktop device that runs the local BlueBubbles bridge. Find it in Desktop Gateway settings.",
|
||||
@@ -145,12 +145,12 @@
|
||||
"channel.importFailed": "Failed to import configuration",
|
||||
"channel.importInvalidFormat": "Invalid configuration file format",
|
||||
"channel.importSuccess": "Configuration imported successfully",
|
||||
"channel.lark.description": "Connect this assistant to Lark for private and group chats.",
|
||||
"channel.lark.description": "Connect this agent to Lark for private and group chats.",
|
||||
"channel.line.channelAccessToken": "Channel Access Token",
|
||||
"channel.line.channelAccessTokenHint": "Long-lived token issued under the Messaging API tab. Token will be encrypted and stored securely.",
|
||||
"channel.line.channelSecret": "Channel Secret",
|
||||
"channel.line.channelSecretHint": "From the Basic settings tab. Required — used to verify X-Line-Signature on every inbound webhook.",
|
||||
"channel.line.description": "Connect this assistant to LINE Messaging API for direct and group chats.",
|
||||
"channel.line.description": "Connect this agent to LINE Messaging API for direct and group chats.",
|
||||
"channel.line.destinationUserId": "Destination User ID",
|
||||
"channel.line.destinationUserIdHint": "The bot's own user ID (`U` + 32 chars) — click \"Fetch from LINE\" below to auto-fill. Not the personal \"Your user ID\" shown in LINE's Basic settings.",
|
||||
"channel.line.destinationUserIdPlaceholder": "e.g. U1234567890abcdef1234567890abcdef",
|
||||
@@ -169,7 +169,7 @@
|
||||
"channel.publicKeyHint": "Optional. Used to verify interaction requests from Discord.",
|
||||
"channel.publicKeyPlaceholder": "Required for interaction verification",
|
||||
"channel.qq.appIdHint": "Your QQ Bot App ID from QQ Open Platform",
|
||||
"channel.qq.description": "Connect this assistant to QQ for group chats and direct messages.",
|
||||
"channel.qq.description": "Connect this agent to QQ for group chats and direct messages.",
|
||||
"channel.qq.webhookMigrationDesc": "WebSocket mode provides real-time event delivery and automatic reconnection without needing a callback URL. To migrate, create a new bot on QQ Open Platform without configuring a callback URL, then switch the Connection Mode to WebSocket in Advanced Settings.",
|
||||
"channel.qq.webhookMigrationTitle": "Consider migrating to WebSocket mode",
|
||||
"channel.refreshStatus": "Refresh status",
|
||||
@@ -199,7 +199,7 @@
|
||||
"channel.slack.appIdHint": "Your Slack App ID from the Slack API dashboard (starts with A).",
|
||||
"channel.slack.appToken": "App-Level Token",
|
||||
"channel.slack.appTokenHint": "Required for Socket Mode (WebSocket). Generate an app-level token (xapp-...) under Basic Information in your Slack app settings.",
|
||||
"channel.slack.description": "Connect this assistant to Slack for channel conversations and direct messages.",
|
||||
"channel.slack.description": "Connect this agent to Slack for channel conversations and direct messages.",
|
||||
"channel.slack.webhookMigrationDesc": "Socket Mode provides real-time event delivery via WebSocket without exposing a public HTTP endpoint. To migrate, enable Socket Mode in your Slack app settings, generate an App-Level Token, then switch the Connection Mode to WebSocket in Advanced Settings.",
|
||||
"channel.slack.webhookMigrationTitle": "Consider migrating to Socket Mode (WebSocket)",
|
||||
"channel.statusConnected": "Connected",
|
||||
@@ -208,7 +208,7 @@
|
||||
"channel.statusFailed": "Failed",
|
||||
"channel.statusQueued": "Queued",
|
||||
"channel.statusStarting": "Starting",
|
||||
"channel.telegram.description": "Connect this assistant to Telegram for private and group chats.",
|
||||
"channel.telegram.description": "Connect this agent to Telegram for private and group chats.",
|
||||
"channel.testConnection": "Test Connection",
|
||||
"channel.testFailed": "Connection test failed",
|
||||
"channel.testSuccess": "Connection test passed",
|
||||
@@ -236,7 +236,7 @@
|
||||
"channel.watchKeywordsAdd": "Add keyword",
|
||||
"channel.watchKeywordsEmpty": "No keywords added yet — bot only wakes on @mention or DM in subscribed channels.",
|
||||
"channel.watchKeywordsHint": "A keyword match wakes the bot without an @mention; its instruction is prepended to the user message. Whole-word, case-insensitive.",
|
||||
"channel.wechat.description": "Connect this assistant to WeChat via iLink Bot for private and group chats.",
|
||||
"channel.wechat.description": "Connect this agent to WeChat via iLink Bot for private and group chats.",
|
||||
"channel.wechatBotId": "Bot ID",
|
||||
"channel.wechatBotIdHint": "Bot identifier assigned after QR code authorization.",
|
||||
"channel.wechatConnectedInfo": "Connected WeChat Account",
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"apikey.list.columns.status": "Enabled Status",
|
||||
"apikey.list.title": "API Key List",
|
||||
"apikey.validation.required": "This field cannot be empty",
|
||||
"authModal.description": "Your login session has expired. Please sign in again to continue using cloud sync features.",
|
||||
"authModal.description": "Your sign-in session has expired. Please sign in again to continue using cloud sync features.",
|
||||
"authModal.later": "Later",
|
||||
"authModal.signIn": "Sign In Again",
|
||||
"authModal.signingIn": "Signing in...",
|
||||
@@ -45,7 +45,7 @@
|
||||
"betterAuth.errors.emailRequired": "Please enter your email address or username",
|
||||
"betterAuth.errors.firstNameRequired": "Please enter your first name",
|
||||
"betterAuth.errors.lastNameRequired": "Please enter your last name",
|
||||
"betterAuth.errors.loginFailed": "Login failed, please check your email and password",
|
||||
"betterAuth.errors.loginFailed": "Sign in failed, please check your email and password",
|
||||
"betterAuth.errors.passwordFormat": "Password must contain both letters and numbers",
|
||||
"betterAuth.errors.passwordMaxLength": "Password must not exceed 64 characters",
|
||||
"betterAuth.errors.passwordMinLength": "Password must be at least 8 characters",
|
||||
@@ -155,13 +155,13 @@
|
||||
"heatmaps.tooltipTokens": "{{count}} tokens were used on {{date}}",
|
||||
"heatmaps.totalCount": "A total of {{count}} messages sent in the past year",
|
||||
"heatmaps.totalCountTokens": "A total of {{count}} tokens used in the past year",
|
||||
"login": "Log In",
|
||||
"login": "Sign In",
|
||||
"loginGuide.f1": "Get free usage",
|
||||
"loginGuide.f2": "Sync messages across devices",
|
||||
"loginGuide.f3": "Access a wealth of agents",
|
||||
"loginGuide.f4": "Explore powerful plugins",
|
||||
"loginGuide.title": "After logging in, you can:",
|
||||
"loginOrSignup": "Log In / Sign Up",
|
||||
"loginGuide.title": "After signing in, you can:",
|
||||
"loginOrSignup": "Sign In / Sign Up",
|
||||
"profile.account": "Account",
|
||||
"profile.authorizations.actions.revoke": "Revoke",
|
||||
"profile.authorizations.revoke.description": "After revoking, the tool will no longer have access to your data. Re-authorization is required to use it again.",
|
||||
@@ -186,7 +186,7 @@
|
||||
"profile.sso.loading": "Loading linked third-party accounts",
|
||||
"profile.sso.providers": "Connected Accounts",
|
||||
"profile.sso.unlink.description": "Re-authorization or re-linking is required to sign in with {{provider}} again after unlinking.",
|
||||
"profile.sso.unlink.forbidden": "You must retain at least one login method.",
|
||||
"profile.sso.unlink.forbidden": "You must retain at least one sign-in method.",
|
||||
"profile.sso.unlink.title": "Unlink {{provider}} account?",
|
||||
"profile.title": "Profile",
|
||||
"profile.updateAvatar": "Update avatar",
|
||||
@@ -201,9 +201,9 @@
|
||||
"profile.usernameRule": "Username can only contain letters, numbers, or underscores",
|
||||
"profile.usernameTooLong": "Username cannot exceed 64 characters",
|
||||
"profile.usernameUpdateFailed": "Failed to update username, please try again later",
|
||||
"signin.subtitle": "Sign up or log in to your {{appName}} account",
|
||||
"signin.subtitle": "Sign up or sign in to your {{appName}} account",
|
||||
"signin.title": "Agent teammates that grow with you",
|
||||
"signout": "Log Out",
|
||||
"signout": "Sign Out",
|
||||
"signup": "Sign Up",
|
||||
"stats.aiheatmaps": "Activity Index",
|
||||
"stats.assistants": "Agents",
|
||||
@@ -224,7 +224,7 @@
|
||||
"stats.loginGuide.f2": "Sync messages across devices",
|
||||
"stats.loginGuide.f3": "Access a wealth of agents",
|
||||
"stats.loginGuide.f4": "Explore powerful skills",
|
||||
"stats.loginGuide.title": "After logging in, you can:",
|
||||
"stats.loginGuide.title": "After signing in, you can:",
|
||||
"stats.messages": "Messages",
|
||||
"stats.modelsRank.left": "Model",
|
||||
"stats.modelsRank.right": "Messages",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"actions.discord": "Go to Discord for feedback",
|
||||
"actions.home": "Return to Home",
|
||||
"actions.retry": "Log in Again",
|
||||
"actions.retry": "Sign in Again",
|
||||
"codes.ACCOUNT_ALREADY_LINKED_TO_DIFFERENT_USER": "This account is already linked to another user",
|
||||
"codes.ACCOUNT_NOT_FOUND": "Account not found",
|
||||
"codes.CREDENTIAL_ACCOUNT_NOT_FOUND": "Credential account does not exist",
|
||||
@@ -25,7 +25,7 @@
|
||||
"codes.PASSWORD_TOO_SHORT": "Password is too short",
|
||||
"codes.PROVIDER_NOT_FOUND": "Identity provider configuration not found",
|
||||
"codes.RATE_LIMIT_EXCEEDED": "Too many requests, please try again later",
|
||||
"codes.SESSION_EXPIRED": "Session has expired, please log in again",
|
||||
"codes.SESSION_EXPIRED": "Session has expired, please sign in again",
|
||||
"codes.SOCIAL_ACCOUNT_ALREADY_LINKED": "This social account is already linked to another user",
|
||||
"codes.TEMPORARY_EMAIL_NOT_ALLOWED": "Temporary email addresses are not supported. Please use a regular email address. Repeated attempts may block this network.",
|
||||
"codes.UNEXPECTED_ERROR": "An unexpected error occurred, please try again",
|
||||
|
||||
+12
-13
@@ -25,8 +25,8 @@
|
||||
"agentDocument.openAsPage": "Open as full page",
|
||||
"agentProfile.files_one": "{{count}} file",
|
||||
"agentProfile.files_other": "{{count}} files",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} knowledge base",
|
||||
"agentProfile.knowledgeBases_other": "{{count}} knowledge bases",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} library",
|
||||
"agentProfile.knowledgeBases_other": "{{count}} libraries",
|
||||
"agentProfile.skills_one": "{{count}} skill",
|
||||
"agentProfile.skills_other": "{{count}} skills",
|
||||
"agentSignal.receipts.agentSignalLabel": "Agent Signal",
|
||||
@@ -67,7 +67,7 @@
|
||||
"claudeCodeInstallGuide.menuNotification.title": "Claude Code CLI not found",
|
||||
"claudeCodeInstallGuide.reason": "LobeHub could not start Claude Code: {{message}}",
|
||||
"claudeCodeInstallGuide.title": "Install Claude Code CLI",
|
||||
"clearCurrentMessages": "Clear current session messages",
|
||||
"clearCurrentMessages": "Clear current conversation messages",
|
||||
"cliAuthGuide.actions.openDocs": "Open Sign-in Guide",
|
||||
"cliAuthGuide.actions.openSystemTools": "Open System Tools",
|
||||
"cliAuthGuide.afterLogin": "After signing in again or refreshing credentials, retry your message. You can also re-detect in System Tools.",
|
||||
@@ -110,7 +110,7 @@
|
||||
"compression.cancelConfirm": "Are you sure you want to uncompress? This will restore the original messages.",
|
||||
"compression.history": "History",
|
||||
"compression.summary": "Summary",
|
||||
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be retrieved. Please confirm your action.",
|
||||
"confirmClearCurrentMessages": "You are about to clear the current conversation messages. Once cleared, they cannot be retrieved. Please confirm your action.",
|
||||
"confirmRemoveChatGroupItemAlert": "This Group will be deleted. Group-specific assistants will also be deleted, while external assistants will not be affected.",
|
||||
"confirmRemoveGroupItemAlert": "You are about to delete this category. After deletion, its agents will be moved to the default list. Please confirm your action.",
|
||||
"confirmRemoveGroupSuccess": "Group deleted successfully",
|
||||
@@ -169,8 +169,8 @@
|
||||
"followUpPlaceholder": "Follow up.",
|
||||
"followUpPlaceholderHeterogeneous": "Follow up.",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Agent Gateway Mode",
|
||||
"gatewayMode.desc": "Run agents in the cloud through LobeHub's Agent Gateway. Tasks keep running even after you close the page.",
|
||||
"gatewayMode.title": "Agent Gateway Mode",
|
||||
"group.desc": "Move a task forward with multiple Agents in one shared space.",
|
||||
"group.memberTooltip": "There are {{count}} members in the group",
|
||||
"group.orchestratorThinking": "Orchestrator is thinking...",
|
||||
@@ -178,8 +178,8 @@
|
||||
"group.profile.external": "External",
|
||||
"group.profile.externalAgentWarning": "This is an external agent. Changes made here will directly modify the original agent configuration.",
|
||||
"group.profile.groupSettings": "Group Settings",
|
||||
"group.profile.supervisor": "Supervisor",
|
||||
"group.profile.supervisorPlaceholder": "The supervisor coordinates different agents. Setting supervisor information here enables more precise workflow coordination.",
|
||||
"group.profile.supervisor": "Orchestrator",
|
||||
"group.profile.supervisorPlaceholder": "The Orchestrator coordinates different agents. Setting Orchestrator information here enables more precise workflow coordination.",
|
||||
"group.removeMember": "Remove Member",
|
||||
"group.title": "Group",
|
||||
"groupDescription": "Group description",
|
||||
@@ -586,7 +586,7 @@
|
||||
"stt.action": "Voice Input",
|
||||
"stt.loading": "Recognizing...",
|
||||
"stt.prettifying": "Polishing...",
|
||||
"supervisor.label": "Supervisor",
|
||||
"supervisor.label": "Orchestrator",
|
||||
"supervisor.todoList.allComplete": "All tasks completed",
|
||||
"supervisor.todoList.title": "Tasks Completed",
|
||||
"tab.groupProfile": "Group Profile",
|
||||
@@ -839,7 +839,7 @@
|
||||
"tokenDetails.chats": "Chat Messages",
|
||||
"tokenDetails.historySummary": "History Summary",
|
||||
"tokenDetails.rest": "Remaining",
|
||||
"tokenDetails.supervisor": "Group Host",
|
||||
"tokenDetails.supervisor": "Orchestrator",
|
||||
"tokenDetails.systemRole": "Role Settings",
|
||||
"tokenDetails.title": "Context Details",
|
||||
"tokenDetails.tools": "Skill Settings",
|
||||
@@ -890,7 +890,7 @@
|
||||
"topic.defaultTitle": "Untitled Topic",
|
||||
"topic.openNewTopic": "Open New Topic",
|
||||
"topic.recent": "Recent Topics",
|
||||
"topic.saveCurrentMessages": "Save current session as topic",
|
||||
"topic.saveCurrentMessages": "Save current conversation as topic",
|
||||
"topic.viewAll": "View All Topics",
|
||||
"translate.action": "Translate",
|
||||
"translate.clear": "Clear Translation",
|
||||
@@ -986,7 +986,7 @@
|
||||
"workflow.toolDisplayName.saveUserQuestion": "Recorded info",
|
||||
"workflow.toolDisplayName.search": "Searched the web",
|
||||
"workflow.toolDisplayName.searchAgent": "Searched agents",
|
||||
"workflow.toolDisplayName.searchKnowledgeBase": "Searched knowledge base",
|
||||
"workflow.toolDisplayName.searchKnowledgeBase": "Searched library",
|
||||
"workflow.toolDisplayName.searchLocalFiles": "Searched files",
|
||||
"workflow.toolDisplayName.searchSkill": "Searched skills",
|
||||
"workflow.toolDisplayName.searchUserMemory": "Searched memory",
|
||||
@@ -1124,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Skills",
|
||||
"workingPanel.space": "Space",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "You",
|
||||
"zenMode": "Zen Mode"
|
||||
"you": "You"
|
||||
}
|
||||
|
||||
@@ -168,8 +168,8 @@
|
||||
"cmdk.search.agents": "Agents",
|
||||
"cmdk.search.assistant": "Agent",
|
||||
"cmdk.search.assistants": "Agents",
|
||||
"cmdk.search.chatGroup": "Agent Team",
|
||||
"cmdk.search.chatGroups": "Agent Teams",
|
||||
"cmdk.search.chatGroup": "Group",
|
||||
"cmdk.search.chatGroups": "Groups",
|
||||
"cmdk.search.communityAgent": "Community Agent",
|
||||
"cmdk.search.file": "File",
|
||||
"cmdk.search.files": "Files",
|
||||
@@ -370,7 +370,7 @@
|
||||
"navPanel.visible": "Visible",
|
||||
"new": "New",
|
||||
"noContent": "No content",
|
||||
"oauth": "SSO Login",
|
||||
"oauth": "SSO Sign-in",
|
||||
"officialSite": "Official Website",
|
||||
"ok": "OK",
|
||||
"or": "or",
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
"LocalFile.action.open": "Open",
|
||||
"LocalFile.action.showInFolder": "Show in Folder",
|
||||
"MaxTokenSlider.unlimited": "Unlimited",
|
||||
"ModelSelect.featureTag.custom": "Custom model, by default, supports both function calls and visual recognition. Please verify the availability of the above capabilities based on actual situations.",
|
||||
"ModelSelect.featureTag.custom": "Custom model, by default, supports both tool calls and visual recognition. Please verify the availability of the above capabilities based on actual situations.",
|
||||
"ModelSelect.featureTag.file": "This model supports file upload for reading and recognition.",
|
||||
"ModelSelect.featureTag.functionCall": "This model supports tool calls.",
|
||||
"ModelSelect.featureTag.imageOutput": "This model supports image generation.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"authResult.failed.desc": "Please try again or switch to a different login method",
|
||||
"authResult.failed.desc": "Please try again or switch to a different sign-in method",
|
||||
"authResult.failed.title": "Authorization Failed",
|
||||
"authResult.success.desc": "Please click the Start button below to continue using LobeHub Desktop",
|
||||
"authResult.success.title": "Authorization Successful",
|
||||
|
||||
@@ -620,7 +620,7 @@
|
||||
"user.githubUrlInvalid": "Please enter a valid GitHub repository URL",
|
||||
"user.githubUrlRequired": "Please enter a GitHub repository URL",
|
||||
"user.login": "Become a Creator",
|
||||
"user.logout": "Logout",
|
||||
"user.logout": "Sign out",
|
||||
"user.myProfile": "My Profile",
|
||||
"user.noAgents": "This user hasn’t published any Agents yet",
|
||||
"user.noAgents.ownerDescription": "Create your first Agent and share it with the Community.",
|
||||
@@ -630,11 +630,11 @@
|
||||
"user.noForkedAgentGroups": "No forked Agent Groups yet",
|
||||
"user.noForkedAgents": "No forked Agents yet",
|
||||
"user.noGroups.title": "No Agent Groups yet",
|
||||
"user.noPlugins": "This user hasn't published any Plugins yet",
|
||||
"user.noPlugins": "This user hasn't published any Skills yet",
|
||||
"user.noSkills": "This user hasn't published any Skills yet",
|
||||
"user.openWorkspacePublicProfile": "Open Public Link",
|
||||
"user.org.noAgents": "This organization hasn’t published any Agents yet",
|
||||
"user.plugins": "Plugins",
|
||||
"user.plugins": "Skills",
|
||||
"user.publishedAgents": "Created Agents",
|
||||
"user.publishedGroups": "Created Groups",
|
||||
"user.searchPlaceholder": "Search by name or description...",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"gateway.title": "Device Gateway",
|
||||
"navigation.chat": "Chat",
|
||||
"navigation.discover": "Discover",
|
||||
"navigation.discoverAssistants": "Discover Assistants",
|
||||
"navigation.discoverAssistants": "Discover Agents",
|
||||
"navigation.discoverMcp": "Discover MCP",
|
||||
"navigation.discoverModels": "Discover Models",
|
||||
"navigation.discoverProviders": "Discover Providers",
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
"import.importConfigFile.title": "Import Failed",
|
||||
"import.incompatible.description": "This file was exported from a higher version. Please try upgrading to the latest version and then re-importing.",
|
||||
"import.incompatible.title": "Current application does not support importing this file",
|
||||
"loginRequired.desc": "You will be redirected to the login page shortly",
|
||||
"loginRequired.title": "Please log in to use this feature",
|
||||
"loginRequired.desc": "You will be redirected to the sign-in page shortly",
|
||||
"loginRequired.title": "Please sign in to use this feature",
|
||||
"notFound.backHome": "Back to Home",
|
||||
"notFound.check": "Please check if your URL is correct.",
|
||||
"notFound.desc": "We couldn't find the page you were looking for.",
|
||||
@@ -124,9 +124,9 @@
|
||||
"unlock.comfyui.title": "Verify your {{name}} credentials",
|
||||
"unlock.confirm": "Confirm and Retry",
|
||||
"unlock.goToSettings": "Go to Settings",
|
||||
"unlock.oauth.description": "The administrator has enabled unified login authentication. Click the button below to log in and unlock the application.",
|
||||
"unlock.oauth.success": "Login successful",
|
||||
"unlock.oauth.title": "Log in to your account",
|
||||
"unlock.oauth.description": "The administrator has enabled unified sign-in authentication. Click the button below to sign in and unlock the application.",
|
||||
"unlock.oauth.success": "Sign-in successful",
|
||||
"unlock.oauth.title": "Sign in to your account",
|
||||
"unlock.oauth.welcome": "Welcome!",
|
||||
"unlock.password.description": "The application encryption has been enabled by the administrator. Enter the application password to unlock the application. The password only needs to be filled in once.",
|
||||
"unlock.password.placeholder": "Please enter password",
|
||||
|
||||
@@ -56,9 +56,9 @@
|
||||
"library.hierarchy.empty.desc": "Add files or create a folder to get started",
|
||||
"library.hierarchy.empty.title": "Nothing here yet",
|
||||
"library.import.action": "Import to workspace…",
|
||||
"library.import.failed": "Failed to import knowledge base.",
|
||||
"library.import.success": "Knowledge base imported to {{name}}.",
|
||||
"library.import.tooltip": "Fork this knowledge base into a workspace. Files are shared by reference; the original stays in your personal space.",
|
||||
"library.import.failed": "Failed to import library.",
|
||||
"library.import.success": "Library imported to {{name}}.",
|
||||
"library.import.tooltip": "Fork this library into a workspace. Files are shared by reference; the original stays in your personal space.",
|
||||
"library.list.confirmRemoveLibrary": "You are about to delete this library. The files within it will not be deleted but moved to All Files. This action cannot be undone, so please proceed with caution.",
|
||||
"library.list.copyDescription": "Clone this library and all of its contents into another workspace.",
|
||||
"library.list.copyFailed": "Failed to copy library",
|
||||
|
||||
@@ -38,7 +38,5 @@
|
||||
"toggleLeftPanel.desc": "Show or hide the left panel",
|
||||
"toggleLeftPanel.title": "Toggle Left Panel",
|
||||
"toggleRightPanel.desc": "Show or hide the right panel",
|
||||
"toggleRightPanel.title": "Toggle Right Panel",
|
||||
"toggleZenMode.desc": "In focus mode, only display the current conversation and hide other UI elements",
|
||||
"toggleZenMode.title": "Toggle Focus Mode"
|
||||
"toggleRightPanel.title": "Toggle Right Panel"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"features.agentDocumentFloatingChatPanel.desc": "Show the floating chat panel in agent document preview only when this lab feature is enabled.",
|
||||
"features.agentDocumentFloatingChatPanel.title": "Agent Document Floating Chat Panel",
|
||||
"features.agentSelfIteration.desc": "Allow the assistant to reflect, build self-awareness, and continuously iterate through ongoing attempts and interactions.",
|
||||
"features.agentSelfIteration.desc": "Allow the agent to reflect, build self-awareness, and continuously iterate through ongoing attempts and interactions.",
|
||||
"features.agentSelfIteration.title": "Agent Self-iteration",
|
||||
"features.assistantMessageGroup.desc": "Group agent messages and their tool call results together for display",
|
||||
"features.assistantMessageGroup.title": "Agent Message Grouping",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"errors.authorizationFailed": "Authorization failed, please try again.",
|
||||
"errors.browserOnly": "The authorization process can only be initiated in a browser.",
|
||||
"errors.codeConsumed": "The authorization code has already been used. Please try again.",
|
||||
"errors.codeVerifierMissing": "Invalid authorization session. Please restart the login process.",
|
||||
"errors.codeVerifierMissing": "Invalid authorization session. Please restart the sign-in process.",
|
||||
"errors.general": "An error occurred during authorization. Please try again.",
|
||||
"errors.handoffFailed": "Failed to retrieve authorization result. Please try again.",
|
||||
"errors.handoffTimeout": "Authorization timed out. Please complete the process in your browser and try again.",
|
||||
@@ -42,7 +42,7 @@
|
||||
"errors.openBrowserFailed": "Failed to open the system browser. Please try again.",
|
||||
"errors.openPopupFailed": "Failed to open authorization popup. Please check your browser's popup blocker settings.",
|
||||
"errors.popupClosed": "The authorization window was closed before completion.",
|
||||
"errors.sessionExpired": "Authorization session has expired. Please log in again.",
|
||||
"errors.sessionExpired": "Authorization session has expired. Please sign in again.",
|
||||
"errors.stateMismatch": "Authorization state mismatch. Please try again.",
|
||||
"errors.stateMissing": "Authorization state not found. Please try again.",
|
||||
"messages.authorizationFailed": "Authorization ran into an issue. Retry, or check if you finished signing in in your browser.",
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
"activity.notes": "Notes",
|
||||
"analysis.action.button": "Request memory analysis",
|
||||
"analysis.modal.cancel": "Cancel",
|
||||
"analysis.modal.helper": "By default Lobe AI will analyze all unprocessed chats. It's optional to select a date range to analyze.",
|
||||
"analysis.modal.helper": "By default Lobe AI will analyze all unprocessed conversations. It's optional to select a date range to analyze.",
|
||||
"analysis.modal.rangePlaceholder": "No range selected; all conversations will be analyzed.",
|
||||
"analysis.modal.rangeSelected": "Analyzing chats from {{start}} to {{end}}",
|
||||
"analysis.modal.rangeSelected": "Analyzing conversations from {{start}} to {{end}}",
|
||||
"analysis.modal.submit": "Request memory analysis",
|
||||
"analysis.modal.title": "Analyze chats to generate memories",
|
||||
"analysis.modal.title": "Analyze conversations to generate memories",
|
||||
"analysis.range.all": "All conversations",
|
||||
"analysis.range.end": "Today",
|
||||
"analysis.range.start": "Beginning",
|
||||
"analysis.status.errorTitle": "Memory analysis request failed",
|
||||
"analysis.status.progress": "Processed {{completed}} / {{total}} topics",
|
||||
"analysis.status.progressUnknown": "Processed {{completed}} topics so far",
|
||||
"analysis.status.progress": "Processed {{completed}} / {{total}} conversations",
|
||||
"analysis.status.progressUnknown": "Processed {{completed}} conversations so far",
|
||||
"analysis.status.tip": "We are processing your conversations to build personal memories. This may take a few minutes.",
|
||||
"analysis.status.title": "Memory analysis in progress",
|
||||
"analysis.toast.deduped": "A memory request is already running, continuing progress…",
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
"createNewAiProvider.createSuccess": "Creation successful",
|
||||
"createNewAiProvider.description.placeholder": "Provider description (optional)",
|
||||
"createNewAiProvider.description.title": "Provider Description",
|
||||
"createNewAiProvider.id.desc": "Unique identifier for the service provider, which cannot be modified after creation",
|
||||
"createNewAiProvider.id.desc": "Unique identifier for the provider, which cannot be modified after creation",
|
||||
"createNewAiProvider.id.duplicate": "Provider ID already exists",
|
||||
"createNewAiProvider.id.format": "Can only contain numbers, lowercase letters, hyphens (-), and underscores (_) ",
|
||||
"createNewAiProvider.id.placeholder": "Suggested all lowercase, e.g., openai, cannot be modified after creation",
|
||||
@@ -222,6 +222,7 @@
|
||||
"providerModels.item.modelConfig.extendParams.options.effort.hint": "For Claude Opus 4.6; controls effort level (low/medium/high/max).",
|
||||
"providerModels.item.modelConfig.extendParams.options.enableAdaptiveThinking.hint": "For Claude Opus 4.6; toggles adaptive thinking on or off.",
|
||||
"providerModels.item.modelConfig.extendParams.options.enableReasoning.hint": "For Claude, DeepSeek and other reasoning models; unlock deeper thinking.",
|
||||
"providerModels.item.modelConfig.extendParams.options.glm5_2ReasoningEffort.hint": "For GLM-5.2; controls reasoning effort with High and Max levels.",
|
||||
"providerModels.item.modelConfig.extendParams.options.gpt5ReasoningEffort.hint": "For GPT-5 series; controls reasoning intensity.",
|
||||
"providerModels.item.modelConfig.extendParams.options.gpt5_1ReasoningEffort.hint": "For GPT-5.1 series; controls reasoning intensity.",
|
||||
"providerModels.item.modelConfig.extendParams.options.gpt5_2ProReasoningEffort.hint": "For GPT-5.2 Pro series; controls reasoning intensity.",
|
||||
@@ -255,7 +256,8 @@
|
||||
"providerModels.item.modelConfig.files.extra": "The current file upload implementation is just a hack solution, limited to self-experimentation. Please wait for complete file upload capabilities in future implementations.",
|
||||
"providerModels.item.modelConfig.files.title": "File Upload Support",
|
||||
"providerModels.item.modelConfig.functionCall.extra": "This configuration will only enable the model's ability to use tools, allowing for the addition of tool-type skills. However, whether the model can truly use the tools depends entirely on the model itself; please test for usability on your own.",
|
||||
"providerModels.item.modelConfig.functionCall.title": "Support for Tool Usage",
|
||||
"providerModels.item.modelConfig.functionCall.title": "Support for Tool Calling",
|
||||
"providerModels.item.modelConfig.id.duplicate": "A model with this ID already exists. Use a different model ID.",
|
||||
"providerModels.item.modelConfig.id.extra": "This cannot be modified after creation and will be used as the model ID when calling AI",
|
||||
"providerModels.item.modelConfig.id.placeholder": "Please enter the model ID, e.g., gpt-4o or claude-3.5-sonnet",
|
||||
"providerModels.item.modelConfig.id.title": "Model ID",
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
"handoff.desc.success": "An attempt has been made to open the desktop application. If it does not open automatically, please switch manually. You can close this browser window later.",
|
||||
"handoff.title.processing": "Authorization in progress...",
|
||||
"handoff.title.success": "Authorization completed",
|
||||
"login.button": "Confirm Login",
|
||||
"login.description": "The application {{clientName}} is requesting to use your account for login",
|
||||
"login.title": "Login to {{clientName}}",
|
||||
"login.button": "Confirm Sign In",
|
||||
"login.description": "The application {{clientName}} is requesting to use your account for sign-in",
|
||||
"login.title": "Sign in to {{clientName}}",
|
||||
"login.userWelcome": "Welcome back, ",
|
||||
"success.subTitle": "You have successfully authorized the application to access your account. You may now close this page.",
|
||||
"success.title": "Authorization Successful"
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"builtins.lobe-agent-management.apiName.deleteAgent": "Delete agent",
|
||||
"builtins.lobe-agent-management.apiName.duplicateAgent": "Duplicate agent",
|
||||
"builtins.lobe-agent-management.apiName.getAgentDetail": "Get agent detail",
|
||||
"builtins.lobe-agent-management.apiName.installPlugin": "Install plugin",
|
||||
"builtins.lobe-agent-management.apiName.installPlugin": "Install Skill",
|
||||
"builtins.lobe-agent-management.apiName.searchAgent": "Search agents",
|
||||
"builtins.lobe-agent-management.apiName.updateAgent": "Update agent",
|
||||
"builtins.lobe-agent-management.apiName.updatePrompt": "Update prompt",
|
||||
@@ -84,7 +84,7 @@
|
||||
"builtins.lobe-agent-management.inspector.createAgent.title": "Creating agent:",
|
||||
"builtins.lobe-agent-management.inspector.duplicateAgent.title": "Duplicating agent:",
|
||||
"builtins.lobe-agent-management.inspector.getAgentDetail.title": "Getting details:",
|
||||
"builtins.lobe-agent-management.inspector.installPlugin.title": "Installing plugin:",
|
||||
"builtins.lobe-agent-management.inspector.installPlugin.title": "Installing Skill:",
|
||||
"builtins.lobe-agent-management.inspector.searchAgent.all": "Search agents:",
|
||||
"builtins.lobe-agent-management.inspector.searchAgent.market": "Search market:",
|
||||
"builtins.lobe-agent-management.inspector.searchAgent.results": "{{count}} results",
|
||||
@@ -94,7 +94,7 @@
|
||||
"builtins.lobe-agent-management.render.duplicateAgent.newId": "New Agent ID",
|
||||
"builtins.lobe-agent-management.render.duplicateAgent.sourceId": "Source Agent ID",
|
||||
"builtins.lobe-agent-management.render.installPlugin.failed": "Installation failed",
|
||||
"builtins.lobe-agent-management.render.installPlugin.plugin": "Plugin",
|
||||
"builtins.lobe-agent-management.render.installPlugin.plugin": "Skill",
|
||||
"builtins.lobe-agent-management.render.installPlugin.success": "Installed successfully",
|
||||
"builtins.lobe-agent-management.title": "Agent Manager",
|
||||
"builtins.lobe-agent.apiName.analyzeVisualMedia": "Analyze visual media",
|
||||
|
||||
+15
-15
@@ -267,8 +267,8 @@
|
||||
"danger.clear.action": "Clear Now",
|
||||
"danger.clear.confirm": "Clear all chat data? This can't be undone.",
|
||||
"danger.clear.desc": "Delete all data, including agents, files, messages, and skills. Your account will NOT be deleted.",
|
||||
"danger.clear.success": "All session messages have been cleared",
|
||||
"danger.clear.title": "Wipe Data",
|
||||
"danger.clear.success": "All conversation messages have been cleared",
|
||||
"danger.clear.title": "Clear Data",
|
||||
"danger.reset.action": "Reset Now",
|
||||
"danger.reset.confirm": "Reset all settings?",
|
||||
"danger.reset.currentVersion": "Current Version",
|
||||
@@ -348,9 +348,9 @@
|
||||
"header.global": "Global Settings",
|
||||
"header.group": "Group Settings",
|
||||
"header.groupDesc": "Manage group and chat preferences",
|
||||
"header.session": "Session Settings",
|
||||
"header.sessionDesc": "Agent Profile and session preferences",
|
||||
"header.sessionWithName": "Session Settings · {{name}}",
|
||||
"header.session": "Agent Settings",
|
||||
"header.sessionDesc": "Agent Profile and chat preferences",
|
||||
"header.sessionWithName": "Agent Settings · {{name}}",
|
||||
"header.title": "Settings",
|
||||
"heterogeneousStatus.account.label": "Account",
|
||||
"heterogeneousStatus.auth.api": "API",
|
||||
@@ -432,7 +432,7 @@
|
||||
"llm.fetcher.latestTime": "Last Updated: {{time}}",
|
||||
"llm.fetcher.noLatestTime": "No list available yet",
|
||||
"llm.helpDoc": "Configuration Guide",
|
||||
"llm.modelList.desc": "Select the models to display in the session. The selected models will be displayed in the model list.",
|
||||
"llm.modelList.desc": "Select the models to display in conversations. The selected models will be displayed in the model list.",
|
||||
"llm.modelList.placeholder": "Please select a model from the list",
|
||||
"llm.modelList.title": "Model List",
|
||||
"llm.modelList.total": "{{count}} models available in total",
|
||||
@@ -701,12 +701,12 @@
|
||||
"settingGroup.scene.options.productive": "Productive",
|
||||
"settingGroup.scene.title": "Group scenario",
|
||||
"settingGroup.submit": "Update Group",
|
||||
"settingGroup.systemPrompt.placeholder": "Please enter the host system prompt",
|
||||
"settingGroup.systemPrompt.title": "Host System Prompt",
|
||||
"settingGroup.systemPrompt.placeholder": "Please enter the Orchestrator system prompt",
|
||||
"settingGroup.systemPrompt.title": "Orchestrator System Prompt",
|
||||
"settingGroup.title": "Group Information",
|
||||
"settingGroupChat.allowDM.desc": "When turned off, you can still send direct messages to the agent",
|
||||
"settingGroupChat.allowDM.title": "Allow Direct Messages from Agent",
|
||||
"settingGroupChat.enableSupervisor.desc": "Enable the moderator feature to manage Group conversations",
|
||||
"settingGroupChat.enableSupervisor.desc": "Enable the Orchestrator feature to manage Group conversations",
|
||||
"settingGroupChat.enableSupervisor.title": "Enable Orchestrator",
|
||||
"settingGroupChat.maxResponseInRow.desc": "Select how many consecutive messages a member can reply with. Set to 0 to disable this limit.",
|
||||
"settingGroupChat.maxResponseInRow.title": "Consecutive Reply Count",
|
||||
@@ -727,9 +727,9 @@
|
||||
"settingGroupChat.revealDM.desc": "Make private messages sent to other members visible to you.",
|
||||
"settingGroupChat.revealDM.title": "Show Private Messages",
|
||||
"settingGroupChat.submit": "Update Settings",
|
||||
"settingGroupChat.systemPrompt.desc": "Custom system prompt for the group chat host. This may affect the default host behavior.",
|
||||
"settingGroupChat.systemPrompt.placeholder": "Please enter a custom host system prompt...",
|
||||
"settingGroupChat.systemPrompt.title": "Host System Prompt",
|
||||
"settingGroupChat.systemPrompt.desc": "Custom system prompt for the group chat Orchestrator. This may affect the default Orchestrator behavior.",
|
||||
"settingGroupChat.systemPrompt.placeholder": "Please enter a custom Orchestrator system prompt...",
|
||||
"settingGroupChat.systemPrompt.title": "Orchestrator System Prompt",
|
||||
"settingGroupChat.title": "Chat Settings",
|
||||
"settingGroupMembers.addToGroup": "Add to Group",
|
||||
"settingGroupMembers.availableAgents": "Available Agents",
|
||||
@@ -1634,13 +1634,13 @@
|
||||
"workspace.general.delete.confirm.title": "Delete Workspace",
|
||||
"workspace.general.delete.confirm.warning.items.agents": "All agents, skills, and their configurations",
|
||||
"workspace.general.delete.confirm.warning.items.billing": "Subscription, budget settings, and auto top-up",
|
||||
"workspace.general.delete.confirm.warning.items.conversations": "All sessions, messages, topics, and tasks",
|
||||
"workspace.general.delete.confirm.warning.items.conversations": "All messages, topics, and tasks",
|
||||
"workspace.general.delete.confirm.warning.items.files": "Uploaded files, generations, and knowledge base data",
|
||||
"workspace.general.delete.confirm.warning.items.members": "Members, pending invitations, and audit logs",
|
||||
"workspace.general.delete.confirm.warning.lead": "The {{name}} workspace will be permanently deleted, along with:",
|
||||
"workspace.general.delete.confirm.warning.tail": "This cannot be undone. Spend and top-up history will be retained for audit only.",
|
||||
"workspace.general.delete.cta": "Delete Workspace",
|
||||
"workspace.general.delete.description": "Permanently delete this workspace and everything inside it — agents, sessions, messages, files, members, and invitations. This action cannot be reversed.",
|
||||
"workspace.general.delete.description": "Permanently delete this workspace and everything inside it — agents, messages, files, members, and invitations. This action cannot be reversed.",
|
||||
"workspace.general.delete.failed": "Failed to delete workspace",
|
||||
"workspace.general.delete.hint": "Cancel any active subscription before deletion. Billing history is kept for audit.",
|
||||
"workspace.general.delete.notOwner": "Only the workspace owner can delete this workspace.",
|
||||
@@ -2053,7 +2053,7 @@
|
||||
"workspaceSetting.group.subscription": "Plans",
|
||||
"workspaceSetting.storage.comingSoon": "Workspace-scoped data import & export is coming soon.",
|
||||
"workspaceSetting.storage.danger.clear.desc": "Delete all data in this workspace, including agents, files, messages, and skills. The workspace itself will NOT be deleted.",
|
||||
"workspaceSetting.storage.danger.clear.title": "Wipe Workspace Data",
|
||||
"workspaceSetting.storage.danger.clear.title": "Clear Workspace Data",
|
||||
"workspaceSetting.storage.danger.reset.desc": "Restore all workspace settings to defaults. Workspace data will not be deleted.",
|
||||
"workspaceSetting.storage.danger.reset.title": "Reset Workspace Settings",
|
||||
"workspaceSetting.storage.telemetry.desc": "Help us improve {{appName}} with anonymous workspace usage data",
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
"actions.confirmRemoveUnstarred": "You are about to delete unstarred topics. This action cannot be undone.",
|
||||
"actions.copyLink": "Copy Link",
|
||||
"actions.copyLinkSuccess": "Link copied",
|
||||
"actions.copySessionId": "Copy Session ID",
|
||||
"actions.copySessionIdSuccess": "Session ID copied",
|
||||
"actions.copySessionId": "Copy Topic ID",
|
||||
"actions.copySessionIdSuccess": "Topic ID copied",
|
||||
"actions.copyWorkingDirectory": "Copy Working Directory",
|
||||
"actions.copyWorkingDirectorySuccess": "Working directory copied",
|
||||
"actions.duplicate": "Duplicate",
|
||||
"actions.export": "Export Topics",
|
||||
"actions.favorite": "Favorite",
|
||||
"actions.import": "Import Conversation",
|
||||
"actions.import": "Import Topics",
|
||||
"actions.markCompleted": "Mark as Completed",
|
||||
"actions.moveToAgent": "Move to another assistant",
|
||||
"actions.moveToAgent": "Move to another agent",
|
||||
"actions.openInNewTab": "Open in New Tab",
|
||||
"actions.openInNewWindow": "Open in a new window",
|
||||
"actions.removeAll": "Delete All Topics",
|
||||
@@ -56,7 +56,7 @@
|
||||
"guide.title": "Topic List",
|
||||
"importError": "Import Failed",
|
||||
"importInvalidFormat": "Invalid file format. Please ensure it is a valid JSON file.",
|
||||
"importLoading": "Importing conversation...",
|
||||
"importLoading": "Importing topics...",
|
||||
"importSuccess": "Successfully imported {{count}} messages",
|
||||
"inPopup.description": "This topic is currently open in a separate window. Continue the conversation there to keep messages in sync.",
|
||||
"inPopup.focus": "Focus Popup Window",
|
||||
@@ -81,9 +81,9 @@
|
||||
"management.bulk.deleteConfirm": "You are about to delete {{count}} topics. This action cannot be undone.",
|
||||
"management.bulk.deleteTitle": "Delete topics?",
|
||||
"management.bulk.favorite": "Favorite",
|
||||
"management.bulk.move": "Move to assistant",
|
||||
"management.bulk.moveEmpty": "No other assistants",
|
||||
"management.bulk.moveSearchPlaceholder": "Search assistants…",
|
||||
"management.bulk.move": "Move to agent",
|
||||
"management.bulk.moveEmpty": "No other agents",
|
||||
"management.bulk.moveSearchPlaceholder": "Search agents…",
|
||||
"management.bulk.selectedCount_one": "{{count}} selected",
|
||||
"management.bulk.selectedCount_other": "{{count}} selected",
|
||||
"management.card.noPreview": "No preview available",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Hola, soy **{{name}}**. Una frase es suficiente.\n\n¿Quieres que me adapte mejor a tu flujo de trabajo? Ve a [Configuración del Agente]({{url}}) y completa el Perfil del Agente (puedes editarlo en cualquier momento).",
|
||||
"agentDefaultMessageWithSystemRole": "Hola, soy **{{name}}**. Una frase es suficiente—tú tienes el control.",
|
||||
"agentDefaultMessageWithoutEdit": "Hola, soy **{{name}}**. Una frase es suficiente—tú tienes el control.",
|
||||
"agentDocument.backToChat": "Volver al chat",
|
||||
"agentDocument.linkCopied": "Enlace copiado",
|
||||
"agentDocument.openAsPage": "Abrir como página completa",
|
||||
"agentProfile.files_one": "{{count}} archivo",
|
||||
"agentProfile.files_other": "{{count}} archivos",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} base de conocimiento",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Extraer contenido de enlaces web",
|
||||
"followUpPlaceholder": "Seguimiento. Usa @ para asignar tareas a otros agentes.",
|
||||
"followUpPlaceholderHeterogeneous": "Continuar.",
|
||||
"gatewayMode.title": "Modo Gateway",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Modo de Puerta de Enlace del Agente",
|
||||
"gatewayMode.desc": "Ejecuta agentes en la nube a través de la Puerta de Enlace de Agentes de LobeHub. Las tareas continúan ejecutándose incluso después de cerrar la página.",
|
||||
"group.desc": "Avanza una tarea con múltiples Agentes en un espacio compartido.",
|
||||
"group.memberTooltip": "Hay {{count}} miembros en el grupo",
|
||||
"group.orchestratorThinking": "El Orquestador está pensando...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autorizar",
|
||||
"toolAuth.authorizing": "Autorizando...",
|
||||
"toolAuth.hint": "Sin autorización o configuración, las habilidades pueden no funcionar. Esto puede limitar al agente o causar errores.",
|
||||
"toolAuth.remove": "Eliminar",
|
||||
"toolAuth.signIn": "Iniciar sesión",
|
||||
"toolAuth.title": "Autorizar habilidades para este agente",
|
||||
"topic.checkOpenNewTopic": "¿Iniciar un nuevo tema?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Habilidades",
|
||||
"workingPanel.space": "Espacio",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Tú",
|
||||
"zenMode": "Modo Zen"
|
||||
"you": "Tú"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Se muestran todas las tareas en ejecución",
|
||||
"fleet.backToHome": "Volver a inicio",
|
||||
"fleet.closeColumn": "Cerrar columna",
|
||||
"fleet.closeIdleColumns": "Cerrar columnas inactivas",
|
||||
"fleet.closeIdleColumnsCount": "Cerrar {{count}} columnas inactivas",
|
||||
"fleet.collapseReply": "Colapsar",
|
||||
"fleet.createTask": "Crear tarea",
|
||||
"fleet.dragHint": "Arrastrar para reordenar",
|
||||
"fleet.empty": "No hay tareas abiertas",
|
||||
"fleet.emptyDesc": "Selecciona una tarea en ejecución a la izquierda o usa + para agregar una columna.",
|
||||
"fleet.noRunningTasks": "No hay tareas en ejecución",
|
||||
"fleet.openInChat": "Abrir en el chat",
|
||||
"fleet.pin": "Fijar columna",
|
||||
"fleet.reply": "Responder",
|
||||
"fleet.runningTasks": "Tareas en ejecución",
|
||||
"fleet.rows.one": "Una fila",
|
||||
"fleet.rows.two": "Dos filas",
|
||||
"fleet.runningBoard": "Tablero en ejecución",
|
||||
"fleet.status.idle": "Inactivo",
|
||||
"fleet.status.paused": "Pausado",
|
||||
"fleet.status.running": "En ejecución",
|
||||
"fleet.status.scheduled": "Programado",
|
||||
"fleet.tooltip": "Ver todos los agentes uno al lado del otro",
|
||||
"fleet.unpin": "Desfijar columna",
|
||||
"gateway.description": "Descripción",
|
||||
"gateway.descriptionPlaceholder": "Opcional",
|
||||
"gateway.deviceName": "Nombre del Dispositivo",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Descubrir MCP",
|
||||
"navigation.discoverModels": "Descubrir Modelos",
|
||||
"navigation.discoverProviders": "Descubrir Proveedores",
|
||||
"navigation.document": "Documento",
|
||||
"navigation.group": "Grupo",
|
||||
"navigation.groupChat": "Chat de Grupo",
|
||||
"navigation.home": "Inicio",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Expirados",
|
||||
"credits.packages.tabs.expiredCount": "Expirados ({{count}})",
|
||||
"credits.packages.title": "Mis Paquetes de Créditos",
|
||||
"credits.topUp.bestValue.cta": "Ver Ultimate anual",
|
||||
"credits.topUp.bestValue.savings": "Ahorra ${{savings}} en esta compra",
|
||||
"credits.topUp.bestValue.title": "{{plan}} anual desbloquea la tarifa de recarga más baja: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Cancelar",
|
||||
"credits.topUp.custom": "Personalizado",
|
||||
"credits.topUp.freeFeeHint": "Las recargas del plan gratuito incluyen una tarifa de servicio de {{fee}} por cada 1M de créditos.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Procesamiento de recompensas: Los créditos se distribuirán dentro de 1 hora después de que el invitado complete un pago y pase la verificación",
|
||||
"referral.rules.title": "Reglas del Programa",
|
||||
"referral.rules.validInvitation": "Invitación válida: El invitado se registra con tu código de referencia, realiza una acción válida y completa un pago (suscripción o recarga de créditos)",
|
||||
"referral.rules.validOperation": "Criterios de acción válida: enviar un mensaje en la página de chat o generar una imagen",
|
||||
"referral.stats.availableBalance": "Saldo Disponible",
|
||||
"referral.stats.description": "Consulta tus estadísticas de referidos",
|
||||
"referral.stats.title": "Resumen de Referidos",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Archivado",
|
||||
"management.status.completed": "Completado",
|
||||
"management.status.failed": "Fallido",
|
||||
"management.status.idle": "Inactivo",
|
||||
"management.status.paused": "Pausado",
|
||||
"management.status.running": "En ejecución",
|
||||
"management.status.waitingForHuman": "Esperando entrada",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} temas fallidos",
|
||||
"projectStatus.loading_one": "{{count}} tema cargando",
|
||||
"projectStatus.loading_other": "{{count}} temas cargando",
|
||||
"projectStatus.unread_one": "{{count}} tema con respuesta no leída",
|
||||
"projectStatus.unread_other": "{{count}} temas con respuestas no leídas",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} tema esperando entrada",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} temas esperando entrada",
|
||||
"renameModal.description": "Mantenlo breve y fácil de reconocer.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "سلام، من **{{name}}** هستم. یک جمله کافی است.\n\nمیخواهی بهتر با جریان کاریات هماهنگ شوم؟ به [تنظیمات عامل]({{url}}) برو و نمایه عامل را پر کن (هر زمان میتوانی ویرایشش کنی).",
|
||||
"agentDefaultMessageWithSystemRole": "سلام، من **{{name}}** هستم. یک جمله کافی است—کنترل با توست.",
|
||||
"agentDefaultMessageWithoutEdit": "سلام، من **{{name}}** هستم. یک جمله کافی است—کنترل با توست.",
|
||||
"agentDocument.backToChat": "بازگشت به چت",
|
||||
"agentDocument.linkCopied": "لینک کپی شد",
|
||||
"agentDocument.openAsPage": "باز کردن به صورت صفحه کامل",
|
||||
"agentProfile.files_one": "{{count}} فایل",
|
||||
"agentProfile.files_other": "{{count}} فایل",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} پایگاهدانش",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "استخراج محتوای پیوند وب",
|
||||
"followUpPlaceholder": "پیگیری. برای واگذاری وظیفه به عاملهای دیگر از @ استفاده کنید.",
|
||||
"followUpPlaceholderHeterogeneous": "پیگیری.",
|
||||
"gatewayMode.title": "حالت دروازه",
|
||||
"gatewayMode.beta": "بتا",
|
||||
"gatewayMode.cardTitle": "حالت دروازه نماینده",
|
||||
"gatewayMode.desc": "اجرای نمایندگان در فضای ابری از طریق دروازه نماینده LobeHub. وظایف حتی پس از بستن صفحه ادامه مییابند.",
|
||||
"group.desc": "با چند عامل در یک فضای مشترک، یک وظیفه را پیش ببرید.",
|
||||
"group.memberTooltip": "{{count}} عضو در گروه وجود دارد",
|
||||
"group.orchestratorThinking": "هماهنگکننده در حال تفکر است...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "مجازسازی",
|
||||
"toolAuth.authorizing": "در حال مجازسازی...",
|
||||
"toolAuth.hint": "بدون مجازسازی یا پیکربندی، مهارتها ممکن است کار نکنند. این میتواند باعث محدودیت یا خطا شود.",
|
||||
"toolAuth.remove": "حذف",
|
||||
"toolAuth.signIn": "ورود",
|
||||
"toolAuth.title": "مجازسازی مهارتها برای این نماینده",
|
||||
"topic.checkOpenNewTopic": "موضوع جدیدی آغاز شود؟",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "مهارتها",
|
||||
"workingPanel.space": "فضا",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "شما",
|
||||
"zenMode": "حالت تمرکز"
|
||||
"you": "شما"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "تمام وظایف در حال اجرا نمایش داده شدهاند",
|
||||
"fleet.backToHome": "بازگشت به خانه",
|
||||
"fleet.closeColumn": "بستن ستون",
|
||||
"fleet.closeIdleColumns": "بستن ستونهای بیکار",
|
||||
"fleet.closeIdleColumnsCount": "بستن {{count}} ستون بیکار",
|
||||
"fleet.collapseReply": "جمع کردن",
|
||||
"fleet.createTask": "ایجاد وظیفه",
|
||||
"fleet.dragHint": "برای تغییر ترتیب بکشید",
|
||||
"fleet.empty": "هیچ وظیفهای باز نیست",
|
||||
"fleet.emptyDesc": "یک وظیفه در حال اجرا را از سمت چپ انتخاب کنید یا از + برای افزودن ستون استفاده کنید.",
|
||||
"fleet.noRunningTasks": "هیچ وظیفهای در حال اجرا نیست",
|
||||
"fleet.openInChat": "باز کردن در چت",
|
||||
"fleet.pin": "سنجاق کردن ستون",
|
||||
"fleet.reply": "پاسخ",
|
||||
"fleet.runningTasks": "وظایف در حال اجرا",
|
||||
"fleet.rows.one": "یک ردیف",
|
||||
"fleet.rows.two": "دو ردیف",
|
||||
"fleet.runningBoard": "تخته در حال اجرا",
|
||||
"fleet.status.idle": "بیکار",
|
||||
"fleet.status.paused": "متوقف شده",
|
||||
"fleet.status.running": "در حال اجرا",
|
||||
"fleet.status.scheduled": "برنامهریزی شده",
|
||||
"fleet.tooltip": "مشاهده تمام عوامل در کنار یکدیگر",
|
||||
"fleet.unpin": "برداشتن سنجاق ستون",
|
||||
"gateway.description": "توضیحات",
|
||||
"gateway.descriptionPlaceholder": "اختیاری",
|
||||
"gateway.deviceName": "نام دستگاه",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "کشف MCP",
|
||||
"navigation.discoverModels": "کشف مدلها",
|
||||
"navigation.discoverProviders": "کشف ارائهدهندگان",
|
||||
"navigation.document": "سند",
|
||||
"navigation.group": "گروه",
|
||||
"navigation.groupChat": "چت گروهی",
|
||||
"navigation.home": "خانه",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "منقضی شده",
|
||||
"credits.packages.tabs.expiredCount": "منقضی شده ({{count}})",
|
||||
"credits.packages.title": "بستههای اعتباری من",
|
||||
"credits.topUp.bestValue.cta": "مشاهده سالانه نهایی",
|
||||
"credits.topUp.bestValue.savings": "در این خرید ${{savings}} صرفهجویی کنید",
|
||||
"credits.topUp.bestValue.title": "{{plan}} سالانه کمترین نرخ شارژ را باز میکند: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "لغو",
|
||||
"credits.topUp.custom": "سفارشی",
|
||||
"credits.topUp.freeFeeHint": "شارژ طرح رایگان شامل هزینه خدمات {{fee}} به ازای هر ۱ میلیون اعتبار است.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "پردازش پاداش: اعتبارها ظرف 1 ساعت پس از تکمیل پرداخت و تأیید توسط دعوتشونده توزیع میشوند.",
|
||||
"referral.rules.title": "قوانین برنامه",
|
||||
"referral.rules.validInvitation": "دعوت معتبر: دعوتشونده با کد ارجاع شما ثبتنام میکند، یک اقدام معتبر انجام میدهد و پرداخت را تکمیل میکند (اشتراک یا شارژ اعتبار).",
|
||||
"referral.rules.validOperation": "معیار اقدام معتبر: ارسال یک پیام در صفحه چت یا تولید یک تصویر در صفحه تصویر",
|
||||
"referral.stats.availableBalance": "موجودی قابل استفاده",
|
||||
"referral.stats.description": "آمار دعوتهای خود را مشاهده کنید",
|
||||
"referral.stats.title": "نمای کلی دعوتها",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "بایگانیشده",
|
||||
"management.status.completed": "تکمیلشده",
|
||||
"management.status.failed": "ناموفق",
|
||||
"management.status.idle": "بیکار",
|
||||
"management.status.paused": "متوقفشده",
|
||||
"management.status.running": "در حال اجرا",
|
||||
"management.status.waitingForHuman": "در انتظار ورودی",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} موضوعات ناموفق",
|
||||
"projectStatus.loading_one": "{{count}} موضوع در حال بارگذاری",
|
||||
"projectStatus.loading_other": "{{count}} موضوعات در حال بارگذاری",
|
||||
"projectStatus.unread_one": "{{count}} موضوع با پاسخ خوانده نشده",
|
||||
"projectStatus.unread_other": "{{count}} موضوع با پاسخهای خوانده نشده",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} موضوع منتظر ورودی",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} موضوعات منتظر ورودی",
|
||||
"renameModal.description": "کوتاه و قابل تشخیص نگه دارید.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Bonjour, je suis **{{name}}**. Une phrase suffit.\n\nVous souhaitez que je m’adapte mieux à votre flux de travail ? Allez dans [Paramètres de l’agent]({{url}}) et complétez le profil de l’agent (modifiable à tout moment).",
|
||||
"agentDefaultMessageWithSystemRole": "Bonjour, je suis **{{name}}**. Une phrase suffit — vous avez le contrôle.",
|
||||
"agentDefaultMessageWithoutEdit": "Bonjour, je suis **{{name}}**. Une phrase suffit — vous avez le contrôle.",
|
||||
"agentDocument.backToChat": "Retour au chat",
|
||||
"agentDocument.linkCopied": "Lien copié",
|
||||
"agentDocument.openAsPage": "Ouvrir en pleine page",
|
||||
"agentProfile.files_one": "{{count}} fichier",
|
||||
"agentProfile.files_other": "{{count}} fichiers",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} base de connaissances",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Extraire le contenu des liens web",
|
||||
"followUpPlaceholder": "Donner suite. @ pour attribuer des tâches à d’autres agents.",
|
||||
"followUpPlaceholderHeterogeneous": "Poursuivre.",
|
||||
"gatewayMode.title": "Mode Passerelle",
|
||||
"gatewayMode.beta": "Bêta",
|
||||
"gatewayMode.cardTitle": "Mode Passerelle Agent",
|
||||
"gatewayMode.desc": "Exécutez des agents dans le cloud via la Passerelle Agent de LobeHub. Les tâches continuent de s'exécuter même après la fermeture de la page.",
|
||||
"group.desc": "Faites avancer une tâche avec plusieurs agents dans un espace partagé.",
|
||||
"group.memberTooltip": "Il y a {{count}} membres dans le groupe",
|
||||
"group.orchestratorThinking": "L’orchestrateur réfléchit...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autoriser",
|
||||
"toolAuth.authorizing": "Autorisation en cours...",
|
||||
"toolAuth.hint": "Sans autorisation ou configuration, les compétences peuvent ne pas fonctionner. Cela peut limiter l'agent ou provoquer des erreurs.",
|
||||
"toolAuth.remove": "Supprimer",
|
||||
"toolAuth.signIn": "Se connecter",
|
||||
"toolAuth.title": "Autoriser les compétences pour cet agent",
|
||||
"topic.checkOpenNewTopic": "Commencer un nouveau sujet ?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Compétences",
|
||||
"workingPanel.space": "Espace",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Vous",
|
||||
"zenMode": "Mode Zen"
|
||||
"you": "Vous"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Toutes les tâches en cours sont affichées",
|
||||
"fleet.backToHome": "Retour à l'accueil",
|
||||
"fleet.closeColumn": "Fermer la colonne",
|
||||
"fleet.closeIdleColumns": "Fermer les colonnes inactives",
|
||||
"fleet.closeIdleColumnsCount": "Fermer {{count}} colonnes inactives",
|
||||
"fleet.collapseReply": "Réduire",
|
||||
"fleet.createTask": "Créer une tâche",
|
||||
"fleet.dragHint": "Glisser pour réorganiser",
|
||||
"fleet.empty": "Aucune tâche ouverte",
|
||||
"fleet.emptyDesc": "Choisissez une tâche en cours à gauche, ou utilisez + pour ajouter une colonne.",
|
||||
"fleet.noRunningTasks": "Aucune tâche en cours",
|
||||
"fleet.openInChat": "Ouvrir dans le chat",
|
||||
"fleet.pin": "Épingler la colonne",
|
||||
"fleet.reply": "Répondre",
|
||||
"fleet.runningTasks": "Tâches en cours",
|
||||
"fleet.rows.one": "Une seule ligne",
|
||||
"fleet.rows.two": "Deux lignes",
|
||||
"fleet.runningBoard": "Tableau de bord en cours",
|
||||
"fleet.status.idle": "Inactif",
|
||||
"fleet.status.paused": "En pause",
|
||||
"fleet.status.running": "En cours",
|
||||
"fleet.status.scheduled": "Planifié",
|
||||
"fleet.tooltip": "Voir tous les agents côte à côte",
|
||||
"fleet.unpin": "Désépingler la colonne",
|
||||
"gateway.description": "Description",
|
||||
"gateway.descriptionPlaceholder": "Optionnel",
|
||||
"gateway.deviceName": "Nom de l'appareil",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Découvrir MCP",
|
||||
"navigation.discoverModels": "Découvrir les Modèles",
|
||||
"navigation.discoverProviders": "Découvrir les Fournisseurs",
|
||||
"navigation.document": "Document",
|
||||
"navigation.group": "Groupe",
|
||||
"navigation.groupChat": "Chat de Groupe",
|
||||
"navigation.home": "Accueil",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Expirés",
|
||||
"credits.packages.tabs.expiredCount": "Expirés ({{count}})",
|
||||
"credits.packages.title": "Mes forfaits de crédits",
|
||||
"credits.topUp.bestValue.cta": "Voir l'abonnement annuel Ultimate",
|
||||
"credits.topUp.bestValue.savings": "Économisez ${{savings}} sur cet achat",
|
||||
"credits.topUp.bestValue.title": "L'abonnement annuel {{plan}} offre le tarif de recharge le plus bas : ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Annuler",
|
||||
"credits.topUp.custom": "Personnalisé",
|
||||
"credits.topUp.freeFeeHint": "Les rechargements du plan gratuit incluent des frais de service de {{fee}} par 1M de crédits.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Traitement des récompenses : Les crédits seront distribués dans l'heure suivant le paiement et la vérification de l'invité.",
|
||||
"referral.rules.title": "Règles du programme",
|
||||
"referral.rules.validInvitation": "Invitation valide : L'invité s'inscrit avec votre code de parrainage, effectue une action valide et finalise un paiement (abonnement ou recharge de crédits).",
|
||||
"referral.rules.validOperation": "Critères d’action valide : envoyer un message ou générer une image",
|
||||
"referral.stats.availableBalance": "Solde disponible",
|
||||
"referral.stats.description": "Consultez vos statistiques de parrainage",
|
||||
"referral.stats.title": "Aperçu du parrainage",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Archivé",
|
||||
"management.status.completed": "Terminé",
|
||||
"management.status.failed": "Échoué",
|
||||
"management.status.idle": "Inactif",
|
||||
"management.status.paused": "En pause",
|
||||
"management.status.running": "En cours",
|
||||
"management.status.waitingForHuman": "En attente d'une intervention",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} sujets échoués",
|
||||
"projectStatus.loading_one": "{{count}} sujet en cours de chargement",
|
||||
"projectStatus.loading_other": "{{count}} sujets en cours de chargement",
|
||||
"projectStatus.unread_one": "{{count}} sujet avec une réponse non lue",
|
||||
"projectStatus.unread_other": "{{count}} sujets avec des réponses non lues",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} sujet en attente d'entrée",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} sujets en attente d'entrée",
|
||||
"renameModal.description": "Gardez-le court et facile à reconnaître.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Ciao, sono **{{name}}**. Una frase è sufficiente.\n\nVuoi che mi adatti meglio al tuo flusso di lavoro? Vai su [Impostazioni Agente]({{url}}) e compila il Profilo Agente (puoi modificarlo in qualsiasi momento).",
|
||||
"agentDefaultMessageWithSystemRole": "Ciao, sono **{{name}}**. Una frase è sufficiente—sei tu al comando.",
|
||||
"agentDefaultMessageWithoutEdit": "Ciao, sono **{{name}}**. Una frase è sufficiente—sei tu al comando.",
|
||||
"agentDocument.backToChat": "Torna alla chat",
|
||||
"agentDocument.linkCopied": "Link copiato",
|
||||
"agentDocument.openAsPage": "Apri come pagina intera",
|
||||
"agentProfile.files_one": "{{count}} file",
|
||||
"agentProfile.files_other": "{{count}} file",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} base di conoscenza",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Estrai Contenuto da Link Web",
|
||||
"followUpPlaceholder": "Follow-up. Usa @ per assegnare attività ad altri agenti.",
|
||||
"followUpPlaceholderHeterogeneous": "Continua.",
|
||||
"gatewayMode.title": "Modalità Gateway",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Modalità Gateway Agente",
|
||||
"gatewayMode.desc": "Esegui agenti nel cloud tramite l'Agent Gateway di LobeHub. Le attività continuano a funzionare anche dopo aver chiuso la pagina.",
|
||||
"group.desc": "Fai avanzare un'attività con più Agenti in uno spazio condiviso.",
|
||||
"group.memberTooltip": "Ci sono {{count}} membri nel gruppo",
|
||||
"group.orchestratorThinking": "L'Orchestratore sta pensando...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autorizza",
|
||||
"toolAuth.authorizing": "Autorizzazione in corso...",
|
||||
"toolAuth.hint": "Senza autorizzazione o configurazione, le Skill potrebbero non funzionare. Questo può limitare l'agente o causare errori.",
|
||||
"toolAuth.remove": "Rimuovi",
|
||||
"toolAuth.signIn": "Accedi",
|
||||
"toolAuth.title": "Autorizza le Skill per questo agente",
|
||||
"topic.checkOpenNewTopic": "Iniziare un nuovo argomento?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Competenze",
|
||||
"workingPanel.space": "Spazio",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Tu",
|
||||
"zenMode": "Modalità Zen"
|
||||
"you": "Tu"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Tutti i compiti in esecuzione sono mostrati",
|
||||
"fleet.backToHome": "Torna alla home",
|
||||
"fleet.closeColumn": "Chiudi colonna",
|
||||
"fleet.closeIdleColumns": "Chiudi colonne inattive",
|
||||
"fleet.closeIdleColumnsCount": "Chiudi {{count}} colonne inattive",
|
||||
"fleet.collapseReply": "Comprimi",
|
||||
"fleet.createTask": "Crea compito",
|
||||
"fleet.dragHint": "Trascina per riordinare",
|
||||
"fleet.empty": "Nessun compito aperto",
|
||||
"fleet.emptyDesc": "Seleziona un compito in esecuzione a sinistra o usa + per aggiungere una colonna.",
|
||||
"fleet.noRunningTasks": "Nessun compito in esecuzione",
|
||||
"fleet.openInChat": "Apri nella chat",
|
||||
"fleet.pin": "Fissa colonna",
|
||||
"fleet.reply": "Rispondi",
|
||||
"fleet.runningTasks": "Compiti in esecuzione",
|
||||
"fleet.rows.one": "Singola riga",
|
||||
"fleet.rows.two": "Due righe",
|
||||
"fleet.runningBoard": "Pannello in esecuzione",
|
||||
"fleet.status.idle": "Inattivo",
|
||||
"fleet.status.paused": "In pausa",
|
||||
"fleet.status.running": "In esecuzione",
|
||||
"fleet.status.scheduled": "Programmato",
|
||||
"fleet.tooltip": "Visualizza tutti gli agenti fianco a fianco",
|
||||
"fleet.unpin": "Sblocca colonna",
|
||||
"gateway.description": "Descrizione",
|
||||
"gateway.descriptionPlaceholder": "Facoltativo",
|
||||
"gateway.deviceName": "Nome Dispositivo",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Scopri MCP",
|
||||
"navigation.discoverModels": "Scopri Modelli",
|
||||
"navigation.discoverProviders": "Scopri Provider",
|
||||
"navigation.document": "Documento",
|
||||
"navigation.group": "Gruppo",
|
||||
"navigation.groupChat": "Chat di Gruppo",
|
||||
"navigation.home": "Home",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Scaduti",
|
||||
"credits.packages.tabs.expiredCount": "Scaduti ({{count}})",
|
||||
"credits.packages.title": "I Miei Pacchetti Crediti",
|
||||
"credits.topUp.bestValue.cta": "Visualizza Ultimate annuale",
|
||||
"credits.topUp.bestValue.savings": "Risparmia ${{savings}} su questo acquisto",
|
||||
"credits.topUp.bestValue.title": "{{plan}} annuale sblocca la tariffa di ricarica più bassa: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Annulla",
|
||||
"credits.topUp.custom": "Personalizzato",
|
||||
"credits.topUp.freeFeeHint": "Le ricariche del piano gratuito includono una commissione di servizio di {{fee}} per 1M di crediti.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Elaborazione delle ricompense: I crediti saranno distribuiti entro 1 ora dopo che l'invitato completa un pagamento e supera la verifica",
|
||||
"referral.rules.title": "Regole del Programma",
|
||||
"referral.rules.validInvitation": "Invito valido: L'invitato si registra con il tuo codice di riferimento, esegue un'azione valida e completa un pagamento (abbonamento o ricarica crediti)",
|
||||
"referral.rules.validOperation": "Criteri di azione valida: invia un messaggio nella pagina Chat o genera un'immagine nella pagina immagini",
|
||||
"referral.stats.availableBalance": "Saldo Disponibile",
|
||||
"referral.stats.description": "Visualizza le tue statistiche di invito",
|
||||
"referral.stats.title": "Panoramica Inviti",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Archiviato",
|
||||
"management.status.completed": "Completato",
|
||||
"management.status.failed": "Fallito",
|
||||
"management.status.idle": "Inattivo",
|
||||
"management.status.paused": "In pausa",
|
||||
"management.status.running": "In esecuzione",
|
||||
"management.status.waitingForHuman": "In attesa di input",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} argomenti falliti",
|
||||
"projectStatus.loading_one": "{{count}} argomento in caricamento",
|
||||
"projectStatus.loading_other": "{{count}} argomenti in caricamento",
|
||||
"projectStatus.unread_one": "{{count}} argomento con risposta non letta",
|
||||
"projectStatus.unread_other": "{{count}} argomenti con risposte non lette",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} argomento in attesa di input",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} argomenti in attesa di input",
|
||||
"renameModal.description": "Mantienilo breve e facile da riconoscere.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "こんにちは、私は **{{name}}** です。一文から始めましょう。\n\nよりあなたの働き方に合わせるには:[アシスタント設定]({{url}}) でアシスタントプロフィールを補完してください(いつでも変更可能)",
|
||||
"agentDefaultMessageWithSystemRole": "こんにちは、私は **{{name}}** です。一文から始めましょう—判断はあなたにあります",
|
||||
"agentDefaultMessageWithoutEdit": "こんにちは、私は **{{name}}** です。一文から始めましょう—判断はあなたにあります",
|
||||
"agentDocument.backToChat": "チャットに戻る",
|
||||
"agentDocument.linkCopied": "リンクがコピーされました",
|
||||
"agentDocument.openAsPage": "全画面で開く",
|
||||
"agentProfile.files_one": "{{count}} 件のファイル",
|
||||
"agentProfile.files_other": "{{count}} 件のファイル",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} 件のナレッジベース",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "ウェブリンクコンテンツの抽出",
|
||||
"followUpPlaceholder": "フォローアップ。@で他のエージェントにタスクを割り当てできます。",
|
||||
"followUpPlaceholderHeterogeneous": "フォローアップ。",
|
||||
"gatewayMode.title": "ゲートウェイモード",
|
||||
"gatewayMode.beta": "ベータ版",
|
||||
"gatewayMode.cardTitle": "エージェントゲートウェイモード",
|
||||
"gatewayMode.desc": "LobeHubのエージェントゲートウェイを通じてクラウドでエージェントを実行します。ページを閉じた後もタスクは実行され続けます。",
|
||||
"group.desc": "同一の対話空間で、複数のアシスタントが一緒にタスクを推進します",
|
||||
"group.memberTooltip": "グループに {{count}} 名のメンバーがいます",
|
||||
"group.orchestratorThinking": "ホストが思考中…",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "承認",
|
||||
"toolAuth.authorizing": "承認中…",
|
||||
"toolAuth.hint": "未承認または未設定の場合、関連スキルは使用できません。これによりアシスタントの機能が制限されたりエラーが発生したりする可能性があります",
|
||||
"toolAuth.remove": "削除",
|
||||
"toolAuth.signIn": "ログイン",
|
||||
"toolAuth.title": "アシスタントのスキル承認を完了してください",
|
||||
"topic.checkOpenNewTopic": "新しいトピックを開きますか?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "スキル",
|
||||
"workingPanel.space": "スペース",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "あなた",
|
||||
"zenMode": "集中モード"
|
||||
"you": "あなた"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "すべての実行中のタスクが表示されています",
|
||||
"fleet.backToHome": "ホームに戻る",
|
||||
"fleet.closeColumn": "列を閉じる",
|
||||
"fleet.closeIdleColumns": "アイドル状態の列を閉じる",
|
||||
"fleet.closeIdleColumnsCount": "{{count}} 個のアイドル状態の列を閉じる",
|
||||
"fleet.collapseReply": "折りたたむ",
|
||||
"fleet.createTask": "タスクを作成",
|
||||
"fleet.dragHint": "ドラッグして並べ替え",
|
||||
"fleet.empty": "開いているタスクはありません",
|
||||
"fleet.emptyDesc": "左側の実行中のタスクを選択するか、+を使用して列を追加してください。",
|
||||
"fleet.noRunningTasks": "実行中のタスクはありません",
|
||||
"fleet.openInChat": "チャットで開く",
|
||||
"fleet.pin": "列を固定",
|
||||
"fleet.reply": "返信",
|
||||
"fleet.runningTasks": "実行中のタスク",
|
||||
"fleet.rows.one": "1行",
|
||||
"fleet.rows.two": "2行",
|
||||
"fleet.runningBoard": "ランニングボード",
|
||||
"fleet.status.idle": "待機中",
|
||||
"fleet.status.paused": "一時停止中",
|
||||
"fleet.status.running": "実行中",
|
||||
"fleet.status.scheduled": "スケジュール済み",
|
||||
"fleet.tooltip": "すべてのエージェントを並べて表示",
|
||||
"fleet.unpin": "列の固定を解除",
|
||||
"gateway.description": "説明",
|
||||
"gateway.descriptionPlaceholder": "任意",
|
||||
"gateway.deviceName": "デバイス名",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "MCP を発見",
|
||||
"navigation.discoverModels": "モデルを発見",
|
||||
"navigation.discoverProviders": "プロバイダーを発見",
|
||||
"navigation.document": "ドキュメント",
|
||||
"navigation.group": "グループ",
|
||||
"navigation.groupChat": "グループチャット",
|
||||
"navigation.home": "ホーム",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "期限切れ",
|
||||
"credits.packages.tabs.expiredCount": "期限切れ({{count}})",
|
||||
"credits.packages.title": "マイクレジットパッケージ",
|
||||
"credits.topUp.bestValue.cta": "究極の年間プランを見る",
|
||||
"credits.topUp.bestValue.savings": "この購入で${{savings}}節約",
|
||||
"credits.topUp.bestValue.title": "{{plan}}年間プランで最安のチャージレートを解放: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "キャンセル",
|
||||
"credits.topUp.custom": "カスタム",
|
||||
"credits.topUp.freeFeeHint": "無料プランのチャージには、1Mクレジットごとに{{fee}}のサービス料が含まれます。",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "報酬処理: 招待者が支払いを完了し、認証を通過した後、クレジットは1時間以内に配布されます。",
|
||||
"referral.rules.title": "プログラムルール",
|
||||
"referral.rules.validInvitation": "有効な招待: 招待者があなたの招待コードで登録し、有効なアクションを1つ実行し、支払い(サブスクリプションまたはクレジット追加)を完了すること。",
|
||||
"referral.rules.validOperation": "有効な操作の条件:チャットページで1回メッセージ送信、または画像ページで1枚生成",
|
||||
"referral.stats.availableBalance": "利用可能残高",
|
||||
"referral.stats.description": "紹介統計を確認",
|
||||
"referral.stats.title": "紹介概要",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "アーカイブ済み",
|
||||
"management.status.completed": "完了済み",
|
||||
"management.status.failed": "失敗",
|
||||
"management.status.idle": "アイドル",
|
||||
"management.status.paused": "一時停止",
|
||||
"management.status.running": "実行中",
|
||||
"management.status.waitingForHuman": "入力待ち",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} 件のトピックが失敗しました",
|
||||
"projectStatus.loading_one": "{{count}} 件のトピックを読み込み中",
|
||||
"projectStatus.loading_other": "{{count}} 件のトピックを読み込み中",
|
||||
"projectStatus.unread_one": "{{count}} 件の未読返信があるトピック",
|
||||
"projectStatus.unread_other": "{{count}} 件の未読返信があるトピック",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} 件のトピックが入力待ちです",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} 件のトピックが入力待ちです",
|
||||
"renameModal.description": "短く、わかりやすい名前にしてください。",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "안녕하세요, 저는 **{{name}}**입니다. 한 문장으로 시작하세요.\n\n더 당신의 업무 방식에 맞추려면: [도우미 설정]({{url}})에서 도우미 프로필을 보완하세요(언제든 변경 가능)",
|
||||
"agentDefaultMessageWithSystemRole": "안녕하세요, 저는 **{{name}}**입니다. 한 문장으로 시작하세요—결정은 당신에게 있습니다",
|
||||
"agentDefaultMessageWithoutEdit": "안녕하세요, 저는 **{{name}}**입니다. 한 문장으로 시작하세요—결정은 당신에게 있습니다",
|
||||
"agentDocument.backToChat": "채팅으로 돌아가기",
|
||||
"agentDocument.linkCopied": "링크가 복사되었습니다",
|
||||
"agentDocument.openAsPage": "전체 페이지로 열기",
|
||||
"agentProfile.files_one": "{{count}}개 파일",
|
||||
"agentProfile.files_other": "{{count}}개 파일",
|
||||
"agentProfile.knowledgeBases_one": "{{count}}개 지식 베이스",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "웹 링크 컨텐츠 추출",
|
||||
"followUpPlaceholder": "후속 작업. 다른 에이전트에게 작업을 할당하려면 @를 사용하세요.",
|
||||
"followUpPlaceholderHeterogeneous": "후속 메시지를 입력하세요.",
|
||||
"gatewayMode.title": "게이트웨이 모드",
|
||||
"gatewayMode.beta": "베타",
|
||||
"gatewayMode.cardTitle": "에이전트 게이트웨이 모드",
|
||||
"gatewayMode.desc": "LobeHub의 에이전트 게이트웨이를 통해 클라우드에서 에이전트를 실행하세요. 페이지를 닫아도 작업이 계속 실행됩니다.",
|
||||
"group.desc": "동일한 대화 공간에서 여러 도우미가 함께 작업을 추진합니다",
|
||||
"group.memberTooltip": "그룹에 {{count}}명의 구성원이 있습니다",
|
||||
"group.orchestratorThinking": "호스트가 생각 중…",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "승인",
|
||||
"toolAuth.authorizing": "승인 중…",
|
||||
"toolAuth.hint": "승인 또는 설정을 하지 않으면 이 기능들을 정상적으로 사용할 수 없어 도우미 기능 누락이나 오류가 발생할 수 있습니다",
|
||||
"toolAuth.remove": "제거",
|
||||
"toolAuth.signIn": "로그인",
|
||||
"toolAuth.title": "도우미의 기능 승인을 완료하세요",
|
||||
"topic.checkOpenNewTopic": "새 주제를 열겠습니까?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "스킬",
|
||||
"workingPanel.space": "공간",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "당신",
|
||||
"zenMode": "집중 모드"
|
||||
"you": "당신"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "모든 실행 중인 작업이 표시됩니다",
|
||||
"fleet.backToHome": "홈으로 돌아가기",
|
||||
"fleet.closeColumn": "열 닫기",
|
||||
"fleet.closeIdleColumns": "유휴 열 닫기",
|
||||
"fleet.closeIdleColumnsCount": "{{count}}개의 유휴 열 닫기",
|
||||
"fleet.collapseReply": "접기",
|
||||
"fleet.createTask": "작업 생성",
|
||||
"fleet.dragHint": "끌어서 순서 변경",
|
||||
"fleet.empty": "열린 작업 없음",
|
||||
"fleet.emptyDesc": "왼쪽에서 실행 중인 작업을 선택하거나 +를 사용하여 열을 추가하세요.",
|
||||
"fleet.noRunningTasks": "실행 중인 작업 없음",
|
||||
"fleet.openInChat": "채팅에서 열기",
|
||||
"fleet.pin": "열 고정",
|
||||
"fleet.reply": "답장",
|
||||
"fleet.runningTasks": "실행 중인 작업",
|
||||
"fleet.rows.one": "단일 행",
|
||||
"fleet.rows.two": "두 행",
|
||||
"fleet.runningBoard": "실행 보드",
|
||||
"fleet.status.idle": "대기 중",
|
||||
"fleet.status.paused": "일시 중지됨",
|
||||
"fleet.status.running": "실행 중",
|
||||
"fleet.status.scheduled": "예약됨",
|
||||
"fleet.tooltip": "모든 에이전트를 나란히 보기",
|
||||
"fleet.unpin": "열 고정 해제",
|
||||
"gateway.description": "설명",
|
||||
"gateway.descriptionPlaceholder": "선택 사항",
|
||||
"gateway.deviceName": "장치 이름",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "MCP 발견",
|
||||
"navigation.discoverModels": "모델 발견",
|
||||
"navigation.discoverProviders": "프로바이더 발견",
|
||||
"navigation.document": "문서",
|
||||
"navigation.group": "그룹",
|
||||
"navigation.groupChat": "그룹 채팅",
|
||||
"navigation.home": "홈",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "만료됨",
|
||||
"credits.packages.tabs.expiredCount": "만료됨 ({{count}})",
|
||||
"credits.packages.title": "내 크레딧 패키지",
|
||||
"credits.topUp.bestValue.cta": "궁극의 연간 보기",
|
||||
"credits.topUp.bestValue.savings": "이번 구매에서 ${{savings}} 절약",
|
||||
"credits.topUp.bestValue.title": "{{plan}} 연간 플랜은 최저 충전 요금을 제공합니다: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "취소",
|
||||
"credits.topUp.custom": "사용자 지정",
|
||||
"credits.topUp.freeFeeHint": "무료 플랜 충전에는 1M 크레딧당 {{fee}} 서비스 수수료가 포함됩니다.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "보상 처리: 초대받은 사용자가 결제를 완료하고 검증을 통과한 후 1시간 이내에 크레딧이 분배됩니다.",
|
||||
"referral.rules.title": "프로그램 규칙",
|
||||
"referral.rules.validInvitation": "유효한 초대: 초대받은 사용자가 귀하의 초대 코드를 사용하여 등록하고, 유효한 작업을 한 번 수행하며, 결제(구독 또는 크레딧 충전)를 완료합니다.",
|
||||
"referral.rules.validOperation": "유효한 행동 기준: 채팅 페이지에서 메시지 1회 전송 또는 이미지 생성 1회",
|
||||
"referral.stats.availableBalance": "사용 가능 잔액",
|
||||
"referral.stats.description": "추천 통계를 확인하세요",
|
||||
"referral.stats.title": "추천 개요",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "보관됨",
|
||||
"management.status.completed": "완료됨",
|
||||
"management.status.failed": "실패",
|
||||
"management.status.idle": "대기 중",
|
||||
"management.status.paused": "일시 중지됨",
|
||||
"management.status.running": "진행 중",
|
||||
"management.status.waitingForHuman": "입력 대기 중",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}}개의 실패한 주제들",
|
||||
"projectStatus.loading_one": "{{count}}개의 로딩 중인 주제",
|
||||
"projectStatus.loading_other": "{{count}}개의 로딩 중인 주제들",
|
||||
"projectStatus.unread_one": "{{count}}개의 읽지 않은 답글이 있는 주제",
|
||||
"projectStatus.unread_other": "{{count}}개의 읽지 않은 답글이 있는 주제들",
|
||||
"projectStatus.waitingForHuman_one": "{{count}}개의 입력 대기 중인 주제",
|
||||
"projectStatus.waitingForHuman_other": "{{count}}개의 입력 대기 중인 주제들",
|
||||
"renameModal.description": "간단하고 알아보기 쉽게 설정하세요.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Hoi, ik ben **{{name}}**. Eén zin is genoeg.\n\nWil je dat ik beter aansluit op jouw workflow? Ga naar [Agentinstellingen]({{url}}) en vul het Agentprofiel in (je kunt dit altijd aanpassen).",
|
||||
"agentDefaultMessageWithSystemRole": "Hoi, ik ben **{{name}}**. Eén zin is genoeg—jij hebt de controle.",
|
||||
"agentDefaultMessageWithoutEdit": "Hoi, ik ben **{{name}}**. Eén zin is genoeg—jij hebt de controle.",
|
||||
"agentDocument.backToChat": "Terug naar chat",
|
||||
"agentDocument.linkCopied": "Link gekopieerd",
|
||||
"agentDocument.openAsPage": "Openen als volledige pagina",
|
||||
"agentProfile.files_one": "{{count}} bestand",
|
||||
"agentProfile.files_other": "{{count}} bestanden",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} kennisbank",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Webpagina-inhoud ophalen",
|
||||
"followUpPlaceholder": "Vervolgen. @ om taken toe te wijzen aan andere agenten.",
|
||||
"followUpPlaceholderHeterogeneous": "Vervolgbericht.",
|
||||
"gatewayMode.title": "Gateway-modus",
|
||||
"gatewayMode.beta": "Bèta",
|
||||
"gatewayMode.cardTitle": "Agent Gateway-modus",
|
||||
"gatewayMode.desc": "Voer agents uit in de cloud via LobeHub's Agent Gateway. Taken blijven doorgaan, zelfs nadat u de pagina hebt gesloten.",
|
||||
"group.desc": "Werk samen met meerdere Agents in één gedeelde ruimte.",
|
||||
"group.memberTooltip": "Er zijn {{count}} leden in de groep",
|
||||
"group.orchestratorThinking": "Orchestrator is aan het denken...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autoriseren",
|
||||
"toolAuth.authorizing": "Bezig met autoriseren...",
|
||||
"toolAuth.hint": "Zonder autorisatie of configuratie werken Skills mogelijk niet. Dit kan de agent beperken of fouten veroorzaken.",
|
||||
"toolAuth.remove": "Verwijderen",
|
||||
"toolAuth.signIn": "Inloggen",
|
||||
"toolAuth.title": "Autoriseer Skills voor deze agent",
|
||||
"topic.checkOpenNewTopic": "Nieuw onderwerp starten?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Vaardigheden",
|
||||
"workingPanel.space": "Ruimte",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Jij",
|
||||
"zenMode": "Zen-modus"
|
||||
"you": "Jij"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Alle lopende taken worden weergegeven",
|
||||
"fleet.backToHome": "Terug naar startpagina",
|
||||
"fleet.closeColumn": "Kolom sluiten",
|
||||
"fleet.closeIdleColumns": "Sluit ongebruikte kolommen",
|
||||
"fleet.closeIdleColumnsCount": "Sluit {{count}} ongebruikte kolommen",
|
||||
"fleet.collapseReply": "Samenvouwen",
|
||||
"fleet.createTask": "Taak aanmaken",
|
||||
"fleet.dragHint": "Sleep om te herschikken",
|
||||
"fleet.empty": "Geen open taken",
|
||||
"fleet.emptyDesc": "Selecteer een lopende taak aan de linkerkant, of gebruik + om een kolom toe te voegen.",
|
||||
"fleet.noRunningTasks": "Geen lopende taken",
|
||||
"fleet.openInChat": "Openen in chat",
|
||||
"fleet.pin": "Kolom vastzetten",
|
||||
"fleet.reply": "Antwoorden",
|
||||
"fleet.runningTasks": "Lopende taken",
|
||||
"fleet.rows.one": "Enkele rij",
|
||||
"fleet.rows.two": "Twee rijen",
|
||||
"fleet.runningBoard": "Lopend Bord",
|
||||
"fleet.status.idle": "Inactief",
|
||||
"fleet.status.paused": "Gepauzeerd",
|
||||
"fleet.status.running": "Bezig",
|
||||
"fleet.status.scheduled": "Gepland",
|
||||
"fleet.tooltip": "Bekijk alle agenten naast elkaar",
|
||||
"fleet.unpin": "Kolom losmaken",
|
||||
"gateway.description": "Beschrijving",
|
||||
"gateway.descriptionPlaceholder": "Optioneel",
|
||||
"gateway.deviceName": "Apparaatnaam",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "MCP Ontdekken",
|
||||
"navigation.discoverModels": "Modellen Ontdekken",
|
||||
"navigation.discoverProviders": "Providers Ontdekken",
|
||||
"navigation.document": "Document",
|
||||
"navigation.group": "Groep",
|
||||
"navigation.groupChat": "Groepschat",
|
||||
"navigation.home": "Startpagina",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Verlopen",
|
||||
"credits.packages.tabs.expiredCount": "Verlopen ({{count}})",
|
||||
"credits.packages.title": "Mijn Tegoedpakketten",
|
||||
"credits.topUp.bestValue.cta": "Bekijk Ultimate jaarlijks",
|
||||
"credits.topUp.bestValue.savings": "Bespaar ${{savings}} op deze aankoop",
|
||||
"credits.topUp.bestValue.title": "{{plan}} jaarlijks biedt het laagste opwaardeertarief: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Annuleren",
|
||||
"credits.topUp.custom": "Aangepast",
|
||||
"credits.topUp.freeFeeHint": "Gratis plan-opwaarderingen omvatten een servicekosten van {{fee}} per 1M credits.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Beloningsverwerking: Credits worden binnen 1 uur verdeeld nadat de uitgenodigde een betaling heeft voltooid en verificatie heeft doorstaan.",
|
||||
"referral.rules.title": "Programmaregels",
|
||||
"referral.rules.validInvitation": "Geldige uitnodiging: De uitgenodigde registreert zich met jouw uitnodigingscode, voert een geldige actie uit en voltooit een betaling (abonnement of creditaanvulling).",
|
||||
"referral.rules.validOperation": "Geldige actie: één bericht verzenden of één afbeelding genereren",
|
||||
"referral.stats.availableBalance": "Beschikbaar Saldo",
|
||||
"referral.stats.description": "Bekijk je verwijzingsstatistieken",
|
||||
"referral.stats.title": "Verwijzingsoverzicht",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Gearchiveerd",
|
||||
"management.status.completed": "Voltooid",
|
||||
"management.status.failed": "Mislukt",
|
||||
"management.status.idle": "Inactief",
|
||||
"management.status.paused": "Gepauzeerd",
|
||||
"management.status.running": "Lopend",
|
||||
"management.status.waitingForHuman": "Wacht op invoer",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} mislukte onderwerpen",
|
||||
"projectStatus.loading_one": "{{count}} onderwerp wordt geladen",
|
||||
"projectStatus.loading_other": "{{count}} onderwerpen worden geladen",
|
||||
"projectStatus.unread_one": "{{count}} onderwerp met ongelezen reactie",
|
||||
"projectStatus.unread_other": "{{count}} onderwerpen met ongelezen reacties",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} onderwerp wacht op invoer",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} onderwerpen wachten op invoer",
|
||||
"renameModal.description": "Houd het kort en gemakkelijk herkenbaar.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Cześć, jestem **{{name}}**. Jedno zdanie wystarczy.\n\nChcesz, żebym lepiej dopasował się do Twojego stylu pracy? Przejdź do [Ustawień Agenta]({{url}}) i uzupełnij Profil Agenta (możesz go edytować w każdej chwili).",
|
||||
"agentDefaultMessageWithSystemRole": "Cześć, jestem **{{name}}**. Jedno zdanie wystarczy — to Ty masz kontrolę.",
|
||||
"agentDefaultMessageWithoutEdit": "Cześć, jestem **{{name}}**. Jedno zdanie wystarczy — to Ty masz kontrolę.",
|
||||
"agentDocument.backToChat": "Powrót do czatu",
|
||||
"agentDocument.linkCopied": "Link skopiowany",
|
||||
"agentDocument.openAsPage": "Otwórz jako pełną stronę",
|
||||
"agentProfile.files_one": "{{count}} plik",
|
||||
"agentProfile.files_other": "{{count}} pliki",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} baza wiedzy",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Wyodrębnij treść linku",
|
||||
"followUpPlaceholder": "Kontynuuj. Użyj @, aby przypisać zadania innym agentom.",
|
||||
"followUpPlaceholderHeterogeneous": "Kontynuuj.",
|
||||
"gatewayMode.title": "Tryb bramy",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Tryb bramy agenta",
|
||||
"gatewayMode.desc": "Uruchamiaj agentów w chmurze za pomocą bramy agenta LobeHub. Zadania będą kontynuowane nawet po zamknięciu strony.",
|
||||
"group.desc": "Pracuj nad zadaniem z wieloma Agentami w jednej wspólnej przestrzeni.",
|
||||
"group.memberTooltip": "Grupa zawiera {{count}} członków",
|
||||
"group.orchestratorThinking": "Orkiestrator myśli...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autoryzuj",
|
||||
"toolAuth.authorizing": "Autoryzowanie...",
|
||||
"toolAuth.hint": "Bez autoryzacji lub konfiguracji umiejętności mogą nie działać. Może to ograniczyć agenta lub powodować błędy.",
|
||||
"toolAuth.remove": "Usuń",
|
||||
"toolAuth.signIn": "Zaloguj się",
|
||||
"toolAuth.title": "Autoryzuj umiejętności dla tego agenta",
|
||||
"topic.checkOpenNewTopic": "Rozpocząć nowy temat?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Umiejętności",
|
||||
"workingPanel.space": "Przestrzeń",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Ty",
|
||||
"zenMode": "Tryb Zen"
|
||||
"you": "Ty"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Wszystkie uruchomione zadania są wyświetlane",
|
||||
"fleet.backToHome": "Powrót do strony głównej",
|
||||
"fleet.closeColumn": "Zamknij kolumnę",
|
||||
"fleet.closeIdleColumns": "Zamknij nieaktywne kolumny",
|
||||
"fleet.closeIdleColumnsCount": "Zamknij {{count}} nieaktywnych kolumn",
|
||||
"fleet.collapseReply": "Zwiń",
|
||||
"fleet.createTask": "Utwórz zadanie",
|
||||
"fleet.dragHint": "Przeciągnij, aby zmienić kolejność",
|
||||
"fleet.empty": "Brak otwartych zadań",
|
||||
"fleet.emptyDesc": "Wybierz uruchomione zadanie po lewej stronie lub użyj +, aby dodać kolumnę.",
|
||||
"fleet.noRunningTasks": "Brak uruchomionych zadań",
|
||||
"fleet.openInChat": "Otwórz w czacie",
|
||||
"fleet.pin": "Przypnij kolumnę",
|
||||
"fleet.reply": "Odpowiedz",
|
||||
"fleet.runningTasks": "Uruchomione zadania",
|
||||
"fleet.rows.one": "Pojedynczy wiersz",
|
||||
"fleet.rows.two": "Dwa wiersze",
|
||||
"fleet.runningBoard": "Tablica operacyjna",
|
||||
"fleet.status.idle": "Bezczynny",
|
||||
"fleet.status.paused": "Wstrzymany",
|
||||
"fleet.status.running": "Uruchomiony",
|
||||
"fleet.status.scheduled": "Zaplanowany",
|
||||
"fleet.tooltip": "Wyświetl wszystkich agentów obok siebie",
|
||||
"fleet.unpin": "Odepnij kolumnę",
|
||||
"gateway.description": "Opis",
|
||||
"gateway.descriptionPlaceholder": "Opcjonalne",
|
||||
"gateway.deviceName": "Nazwa urządzenia",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Odkryj MCP",
|
||||
"navigation.discoverModels": "Odkryj Modele",
|
||||
"navigation.discoverProviders": "Odkryj Dostawców",
|
||||
"navigation.document": "Dokument",
|
||||
"navigation.group": "Grupa",
|
||||
"navigation.groupChat": "Czat Grupowy",
|
||||
"navigation.home": "Strona główna",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Wygasłe",
|
||||
"credits.packages.tabs.expiredCount": "Wygasłe ({{count}})",
|
||||
"credits.packages.title": "Moje pakiety kredytowe",
|
||||
"credits.topUp.bestValue.cta": "Zobacz Ultimate roczny",
|
||||
"credits.topUp.bestValue.savings": "Zaoszczędź ${{savings}} na tym zakupie",
|
||||
"credits.topUp.bestValue.title": "{{plan}} roczny odblokowuje najniższą stawkę doładowania: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Anuluj",
|
||||
"credits.topUp.custom": "Niestandardowe",
|
||||
"credits.topUp.freeFeeHint": "Doładowania w darmowym planie obejmują opłatę serwisową w wysokości {{fee}} za każde 1M kredytów.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Przetwarzanie nagrody: Kredyty zostaną przyznane w ciągu 1 godziny po dokonaniu płatności przez zaproszonego i przejściu weryfikacji",
|
||||
"referral.rules.title": "Zasady Programu",
|
||||
"referral.rules.validInvitation": "Ważne zaproszenie: Zaproszony rejestruje się z Twoim kodem polecającym, wykonuje jedno ważne działanie i dokonuje płatności (subskrypcja lub doładowanie kredytów)",
|
||||
"referral.rules.validOperation": "Kryteria ważnej akcji: wysłanie jednej wiadomości na stronie czatu lub wygenerowanie jednego obrazu na stronie obrazów",
|
||||
"referral.stats.availableBalance": "Dostępne Środki",
|
||||
"referral.stats.description": "Zobacz swoje statystyki poleceń",
|
||||
"referral.stats.title": "Podsumowanie Poleceń",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Zarchiwizowane",
|
||||
"management.status.completed": "Zakończone",
|
||||
"management.status.failed": "Niepowodzenie",
|
||||
"management.status.idle": "Bezczynny",
|
||||
"management.status.paused": "Wstrzymane",
|
||||
"management.status.running": "W trakcie",
|
||||
"management.status.waitingForHuman": "Oczekiwanie na dane",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} nieudane tematy",
|
||||
"projectStatus.loading_one": "{{count}} ładowany temat",
|
||||
"projectStatus.loading_other": "{{count}} ładowane tematy",
|
||||
"projectStatus.unread_one": "{{count}} temat z nieprzeczytaną odpowiedzią",
|
||||
"projectStatus.unread_other": "{{count}} tematy z nieprzeczytanymi odpowiedziami",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} temat oczekujący na dane",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} tematy oczekujące na dane",
|
||||
"renameModal.description": "Utrzymaj nazwę krótką i łatwą do rozpoznania.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Olá, sou **{{name}}**. Uma frase basta.\n\nQuer que eu me adapte melhor ao seu fluxo de trabalho? Vá para [Configurações do Agente]({{url}}) e preencha o Perfil do Agente (você pode editá-lo a qualquer momento).",
|
||||
"agentDefaultMessageWithSystemRole": "Olá, sou **{{name}}**. Uma frase basta — você está no controle.",
|
||||
"agentDefaultMessageWithoutEdit": "Olá, sou **{{name}}**. Uma frase basta — você está no controle.",
|
||||
"agentDocument.backToChat": "Voltar para o chat",
|
||||
"agentDocument.linkCopied": "Link copiado",
|
||||
"agentDocument.openAsPage": "Abrir como página inteira",
|
||||
"agentProfile.files_one": "{{count}} arquivo",
|
||||
"agentProfile.files_other": "{{count}} arquivos",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} base de conhecimento",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Extrair Conteúdo de Links da Web",
|
||||
"followUpPlaceholder": "Acompanhar. Use @ para atribuir tarefas a outros agentes.",
|
||||
"followUpPlaceholderHeterogeneous": "Continuar.",
|
||||
"gatewayMode.title": "Modo Gateway",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Modo de Gateway do Agente",
|
||||
"gatewayMode.desc": "Execute agentes na nuvem através do Gateway do Agente do LobeHub. As tarefas continuam sendo executadas mesmo após você fechar a página.",
|
||||
"group.desc": "Avance em uma tarefa com vários Agentes em um espaço compartilhado.",
|
||||
"group.memberTooltip": "Há {{count}} membros no grupo",
|
||||
"group.orchestratorThinking": "Orquestrador está pensando...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Autorizar",
|
||||
"toolAuth.authorizing": "Autorizando...",
|
||||
"toolAuth.hint": "Sem autorização ou configuração, as Habilidades podem não funcionar. Isso pode limitar o Agente ou causar erros.",
|
||||
"toolAuth.remove": "Remover",
|
||||
"toolAuth.signIn": "Entrar",
|
||||
"toolAuth.title": "Autorizar Habilidades para este Agente",
|
||||
"topic.checkOpenNewTopic": "Iniciar um novo tópico?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Habilidades",
|
||||
"workingPanel.space": "Espaço",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Você",
|
||||
"zenMode": "Modo Zen"
|
||||
"you": "Você"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Todas as tarefas em execução estão exibidas",
|
||||
"fleet.backToHome": "Voltar para a página inicial",
|
||||
"fleet.closeColumn": "Fechar coluna",
|
||||
"fleet.closeIdleColumns": "Fechar colunas inativas",
|
||||
"fleet.closeIdleColumnsCount": "Fechar {{count}} colunas inativas",
|
||||
"fleet.collapseReply": "Recolher",
|
||||
"fleet.createTask": "Criar tarefa",
|
||||
"fleet.dragHint": "Arraste para reordenar",
|
||||
"fleet.empty": "Nenhuma tarefa aberta",
|
||||
"fleet.emptyDesc": "Selecione uma tarefa em execução à esquerda ou use + para adicionar uma coluna.",
|
||||
"fleet.noRunningTasks": "Nenhuma tarefa em execução",
|
||||
"fleet.openInChat": "Abrir no chat",
|
||||
"fleet.pin": "Fixar coluna",
|
||||
"fleet.reply": "Responder",
|
||||
"fleet.runningTasks": "Tarefas em execução",
|
||||
"fleet.rows.one": "Uma linha",
|
||||
"fleet.rows.two": "Duas linhas",
|
||||
"fleet.runningBoard": "Painel em execução",
|
||||
"fleet.status.idle": "Inativo",
|
||||
"fleet.status.paused": "Pausado",
|
||||
"fleet.status.running": "Em execução",
|
||||
"fleet.status.scheduled": "Agendado",
|
||||
"fleet.tooltip": "Visualizar todos os agentes lado a lado",
|
||||
"fleet.unpin": "Desafixar coluna",
|
||||
"gateway.description": "Descrição",
|
||||
"gateway.descriptionPlaceholder": "Opcional",
|
||||
"gateway.deviceName": "Nome do Dispositivo",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Descobrir MCP",
|
||||
"navigation.discoverModels": "Descobrir Modelos",
|
||||
"navigation.discoverProviders": "Descobrir Provedores",
|
||||
"navigation.document": "Documento",
|
||||
"navigation.group": "Grupo",
|
||||
"navigation.groupChat": "Chat em Grupo",
|
||||
"navigation.home": "Início",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Expirados",
|
||||
"credits.packages.tabs.expiredCount": "Expirados ({{count}})",
|
||||
"credits.packages.title": "Meus Pacotes de Créditos",
|
||||
"credits.topUp.bestValue.cta": "Ver Ultimate anual",
|
||||
"credits.topUp.bestValue.savings": "Economize ${{savings}} nesta compra",
|
||||
"credits.topUp.bestValue.title": "{{plan}} anual desbloqueia a menor taxa de recarga: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Cancelar",
|
||||
"credits.topUp.custom": "Personalizado",
|
||||
"credits.topUp.freeFeeHint": "Recargas do plano gratuito incluem uma taxa de serviço de {{fee}} por 1M de créditos.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Processamento de recompensas: Créditos serão distribuídos dentro de 1 hora após o convidado completar um pagamento e passar pela verificação",
|
||||
"referral.rules.title": "Regras do Programa",
|
||||
"referral.rules.validInvitation": "Convite válido: O convidado se registra com seu código de indicação, realiza uma ação válida e completa um pagamento (assinatura ou recarga de créditos)",
|
||||
"referral.rules.validOperation": "Critérios de ação válida: Enviar uma mensagem na página de chat ou gerar uma imagem",
|
||||
"referral.stats.availableBalance": "Saldo Disponível",
|
||||
"referral.stats.description": "Veja suas estatísticas de indicação",
|
||||
"referral.stats.title": "Visão Geral das Indicações",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Arquivado",
|
||||
"management.status.completed": "Concluído",
|
||||
"management.status.failed": "Falhou",
|
||||
"management.status.idle": "Inativo",
|
||||
"management.status.paused": "Pausado",
|
||||
"management.status.running": "Em execução",
|
||||
"management.status.waitingForHuman": "Aguardando entrada",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} tópicos falharam",
|
||||
"projectStatus.loading_one": "{{count}} tópico carregando",
|
||||
"projectStatus.loading_other": "{{count}} tópicos carregando",
|
||||
"projectStatus.unread_one": "{{count}} tópico com resposta não lida",
|
||||
"projectStatus.unread_other": "{{count}} tópicos com respostas não lidas",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} tópico aguardando entrada",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} tópicos aguardando entrada",
|
||||
"renameModal.description": "Mantenha curto e fácil de reconhecer.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Привет, я **{{name}}**. Одного предложения достаточно.\n\nХотите, чтобы я лучше соответствовал вашему рабочему процессу? Перейдите в [Настройки агента]({{url}}) и заполните профиль агента (вы можете изменить его в любое время).",
|
||||
"agentDefaultMessageWithSystemRole": "Привет, я **{{name}}**. Одного предложения достаточно — вы управляете процессом.",
|
||||
"agentDefaultMessageWithoutEdit": "Привет, я **{{name}}**. Одного предложения достаточно — вы управляете процессом.",
|
||||
"agentDocument.backToChat": "Вернуться в чат",
|
||||
"agentDocument.linkCopied": "Ссылка скопирована",
|
||||
"agentDocument.openAsPage": "Открыть на всю страницу",
|
||||
"agentProfile.files_one": "{{count}} файл",
|
||||
"agentProfile.files_other": "{{count}} файлов",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} база знаний",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Извлекать содержимое веб-ссылок",
|
||||
"followUpPlaceholder": "Продолжение. Используйте @, чтобы назначать задачи другим агентам.",
|
||||
"followUpPlaceholderHeterogeneous": "Продолжить.",
|
||||
"gatewayMode.title": "Режим шлюза",
|
||||
"gatewayMode.beta": "Бета",
|
||||
"gatewayMode.cardTitle": "Режим шлюза агента",
|
||||
"gatewayMode.desc": "Запускайте агентов в облаке через шлюз агентов LobeHub. Задачи продолжают выполняться даже после закрытия страницы.",
|
||||
"group.desc": "Продвигайте задачу с помощью нескольких агентов в общем пространстве.",
|
||||
"group.memberTooltip": "В группе {{count}} участников",
|
||||
"group.orchestratorThinking": "Оркестратор обдумывает...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Авторизовать",
|
||||
"toolAuth.authorizing": "Авторизация...",
|
||||
"toolAuth.hint": "Без авторизации или настройки навыки могут не работать. Это может ограничить агента или вызвать ошибки.",
|
||||
"toolAuth.remove": "Удалить",
|
||||
"toolAuth.signIn": "Войти",
|
||||
"toolAuth.title": "Авторизация навыков для этого агента",
|
||||
"topic.checkOpenNewTopic": "Начать новую тему?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Навыки",
|
||||
"workingPanel.space": "Пространство",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Вы",
|
||||
"zenMode": "Режим дзен"
|
||||
"you": "Вы"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Показаны все выполняемые задачи",
|
||||
"fleet.backToHome": "Вернуться на главную",
|
||||
"fleet.closeColumn": "Закрыть колонку",
|
||||
"fleet.closeIdleColumns": "Закрыть неактивные колонки",
|
||||
"fleet.closeIdleColumnsCount": "Закрыть {{count}} неактивных колонок",
|
||||
"fleet.collapseReply": "Свернуть",
|
||||
"fleet.createTask": "Создать задачу",
|
||||
"fleet.dragHint": "Перетащите для изменения порядка",
|
||||
"fleet.empty": "Нет открытых задач",
|
||||
"fleet.emptyDesc": "Выберите выполняемую задачу слева или используйте +, чтобы добавить колонку.",
|
||||
"fleet.noRunningTasks": "Нет выполняемых задач",
|
||||
"fleet.openInChat": "Открыть в чате",
|
||||
"fleet.pin": "Закрепить колонку",
|
||||
"fleet.reply": "Ответить",
|
||||
"fleet.runningTasks": "Выполняемые задачи",
|
||||
"fleet.rows.one": "Один ряд",
|
||||
"fleet.rows.two": "Два ряда",
|
||||
"fleet.runningBoard": "Рабочая панель",
|
||||
"fleet.status.idle": "Ожидание",
|
||||
"fleet.status.paused": "Приостановлено",
|
||||
"fleet.status.running": "Выполняется",
|
||||
"fleet.status.scheduled": "Запланировано",
|
||||
"fleet.tooltip": "Просмотреть всех агентов рядом друг с другом",
|
||||
"fleet.unpin": "Открепить колонку",
|
||||
"gateway.description": "Описание",
|
||||
"gateway.descriptionPlaceholder": "Необязательно",
|
||||
"gateway.deviceName": "Имя устройства",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Открыть MCP",
|
||||
"navigation.discoverModels": "Открыть Модели",
|
||||
"navigation.discoverProviders": "Открыть Провайдеров",
|
||||
"navigation.document": "Документ",
|
||||
"navigation.group": "Группа",
|
||||
"navigation.groupChat": "Групповой Чат",
|
||||
"navigation.home": "Главная",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Истёкшие",
|
||||
"credits.packages.tabs.expiredCount": "Истёкшие ({{count}})",
|
||||
"credits.packages.title": "Мои пакеты кредитов",
|
||||
"credits.topUp.bestValue.cta": "Посмотреть Ultimate годовой",
|
||||
"credits.topUp.bestValue.savings": "Сэкономьте ${{savings}} на этой покупке",
|
||||
"credits.topUp.bestValue.title": "{{plan}} годовой предоставляет самую низкую ставку пополнения: ${{price}} / 1М {{creditLabel}}",
|
||||
"credits.topUp.cancel": "Отмена",
|
||||
"credits.topUp.custom": "Свой вариант",
|
||||
"credits.topUp.freeFeeHint": "Пополнения для бесплатного плана включают комиссию за обслуживание в размере {{fee}} за 1M кредитов.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Обработка награды: Кредиты будут распределены в течение 1 часа после того, как приглашенный завершит платеж и пройдет проверку.",
|
||||
"referral.rules.title": "Правила программы",
|
||||
"referral.rules.validInvitation": "Действительное приглашение: Приглашенный регистрируется с вашим реферальным кодом, выполняет одно действительное действие и завершает платеж (подписка или пополнение кредитов).",
|
||||
"referral.rules.validOperation": "Критерии действия: отправка сообщения или генерация изображения",
|
||||
"referral.stats.availableBalance": "Доступный баланс",
|
||||
"referral.stats.description": "Просмотр статистики приглашений",
|
||||
"referral.stats.title": "Обзор приглашений",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Архивированные",
|
||||
"management.status.completed": "Завершенные",
|
||||
"management.status.failed": "Неудачные",
|
||||
"management.status.idle": "Бездействие",
|
||||
"management.status.paused": "Приостановленные",
|
||||
"management.status.running": "Выполняются",
|
||||
"management.status.waitingForHuman": "Ожидание ввода",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} неудачных тем",
|
||||
"projectStatus.loading_one": "{{count}} загружаемая тема",
|
||||
"projectStatus.loading_other": "{{count}} загружаемых тем",
|
||||
"projectStatus.unread_one": "{{count}} тема с непрочитанным ответом",
|
||||
"projectStatus.unread_other": "{{count}} тем с непрочитанными ответами",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} тема ожидает ввода",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} тем ожидают ввода",
|
||||
"renameModal.description": "Сделайте название коротким и легко узнаваемым.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Merhaba, ben **{{name}}**. Bir cümle yeterli.\n\nİş akışına daha iyi uyum sağlamamı ister misin? [Ajan Ayarları]({{url}}) bölümüne gidip Ajan Profilini doldur (istediğin zaman düzenleyebilirsin).",
|
||||
"agentDefaultMessageWithSystemRole": "Merhaba, ben **{{name}}**. Bir cümle yeterli—kontrol sende.",
|
||||
"agentDefaultMessageWithoutEdit": "Merhaba, ben **{{name}}**. Bir cümle yeterli—kontrol sende.",
|
||||
"agentDocument.backToChat": "Sohbete geri dön",
|
||||
"agentDocument.linkCopied": "Bağlantı kopyalandı",
|
||||
"agentDocument.openAsPage": "Tam sayfa olarak aç",
|
||||
"agentProfile.files_one": "{{count}} dosya",
|
||||
"agentProfile.files_other": "{{count}} dosya",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} bilgi tabanı",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Web Sayfası Bağlantı İçeriğini Çıkar",
|
||||
"followUpPlaceholder": "Takip edin. Diğer ajanlara görev atamak için @ kullanın.",
|
||||
"followUpPlaceholderHeterogeneous": "Devam edin.",
|
||||
"gatewayMode.title": "Ağ Geçidi Modu",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Aracı Geçit Modu",
|
||||
"gatewayMode.desc": "LobeHub'un Aracı Geçidi üzerinden bulutta aracılar çalıştırın. Sayfayı kapattıktan sonra bile görevler çalışmaya devam eder.",
|
||||
"group.desc": "Birden fazla Ajanla ortak bir alanda görevleri ilerletin.",
|
||||
"group.memberTooltip": "Grupta {{count}} üye var",
|
||||
"group.orchestratorThinking": "Yönlendirici düşünüyor...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Yetkilendir",
|
||||
"toolAuth.authorizing": "Yetkilendiriliyor...",
|
||||
"toolAuth.hint": "Yetkilendirme veya yapılandırma olmadan, Yetenekler çalışmayabilir. Bu, Ajan'ı sınırlayabilir veya hatalara neden olabilir.",
|
||||
"toolAuth.remove": "Kaldır",
|
||||
"toolAuth.signIn": "Giriş Yap",
|
||||
"toolAuth.title": "Bu Ajan için Yetenekleri Yetkilendir",
|
||||
"topic.checkOpenNewTopic": "Yeni bir konu başlatılsın mı?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Beceriler",
|
||||
"workingPanel.space": "Boşluk",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Sen",
|
||||
"zenMode": "Zen Modu"
|
||||
"you": "Sen"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Tüm çalışan görevler gösteriliyor",
|
||||
"fleet.backToHome": "Ana sayfaya dön",
|
||||
"fleet.closeColumn": "Sütunu kapat",
|
||||
"fleet.closeIdleColumns": "Boşta olan sütunları kapat",
|
||||
"fleet.closeIdleColumnsCount": "{{count}} boşta olan sütunu kapat",
|
||||
"fleet.collapseReply": "Daralt",
|
||||
"fleet.createTask": "Görev oluştur",
|
||||
"fleet.dragHint": "Yeniden sıralamak için sürükleyin",
|
||||
"fleet.empty": "Açık görev yok",
|
||||
"fleet.emptyDesc": "Soldaki çalışan bir görevi seçin veya sütun eklemek için + kullanın.",
|
||||
"fleet.noRunningTasks": "Çalışan görev yok",
|
||||
"fleet.openInChat": "Sohbette aç",
|
||||
"fleet.pin": "Sütunu sabitle",
|
||||
"fleet.reply": "Yanıtla",
|
||||
"fleet.runningTasks": "Çalışan Görevler",
|
||||
"fleet.rows.one": "Tek satır",
|
||||
"fleet.rows.two": "İki satır",
|
||||
"fleet.runningBoard": "Çalışma Panosu",
|
||||
"fleet.status.idle": "Boşta",
|
||||
"fleet.status.paused": "Duraklatıldı",
|
||||
"fleet.status.running": "Çalışıyor",
|
||||
"fleet.status.scheduled": "Planlandı",
|
||||
"fleet.tooltip": "Tüm ajanları yan yana görüntüle",
|
||||
"fleet.unpin": "Sütunun sabitlemesini kaldır",
|
||||
"gateway.description": "Açıklama",
|
||||
"gateway.descriptionPlaceholder": "Opsiyonel",
|
||||
"gateway.deviceName": "Cihaz Adı",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "MCP'yi Keşfet",
|
||||
"navigation.discoverModels": "Modelleri Keşfet",
|
||||
"navigation.discoverProviders": "Sağlayıcıları Keşfet",
|
||||
"navigation.document": "Belge",
|
||||
"navigation.group": "Grup",
|
||||
"navigation.groupChat": "Grup Sohbeti",
|
||||
"navigation.home": "Ana Sayfa",
|
||||
|
||||
@@ -89,6 +89,9 @@
|
||||
"credits.packages.tabs.expired": "Süresi Dolmuş",
|
||||
"credits.packages.tabs.expiredCount": "Süresi Dolmuş ({{count}})",
|
||||
"credits.packages.title": "Kredi Paketlerim",
|
||||
"credits.topUp.bestValue.cta": "En İyi Yıllık Görüntüle",
|
||||
"credits.topUp.bestValue.savings": "Bu satın alımda ${{savings}} tasarruf edin",
|
||||
"credits.topUp.bestValue.title": "{{plan}} yıllık en düşük doldurma oranını açar: ${{price}} / 1M {{creditLabel}}",
|
||||
"credits.topUp.cancel": "İptal Et",
|
||||
"credits.topUp.custom": "Özel",
|
||||
"credits.topUp.freeFeeHint": "Ücretsiz plan yüklemelerinde her 1M kredi için {{fee}} hizmet ücreti uygulanır.",
|
||||
@@ -417,7 +420,6 @@
|
||||
"referral.rules.rewardDelay": "Ödül işleme: Davet edilen kişi bir ödeme yaptıktan ve doğrulamadan geçtikten sonra krediler 1 saat içinde dağıtılacaktır.",
|
||||
"referral.rules.title": "Program Kuralları",
|
||||
"referral.rules.validInvitation": "Geçerli davet: Davet edilen kişi, davet kodunuzla kaydolur, bir geçerli işlem yapar ve bir ödeme (abonelik veya kredi doldurma) tamamlar.",
|
||||
"referral.rules.validOperation": "Geçerli işlem kriteri: Sohbet sayfasında bir mesaj gönderme veya görsel sayfasında bir görsel oluşturma",
|
||||
"referral.stats.availableBalance": "Kullanılabilir Bakiye",
|
||||
"referral.stats.description": "Referans istatistiklerinizi görüntüleyin",
|
||||
"referral.stats.title": "Referans Genel Bakış",
|
||||
|
||||
@@ -143,6 +143,7 @@
|
||||
"management.status.archived": "Arşivlenmiş",
|
||||
"management.status.completed": "Tamamlanmış",
|
||||
"management.status.failed": "Başarısız",
|
||||
"management.status.idle": "Boşta",
|
||||
"management.status.paused": "Durduruldu",
|
||||
"management.status.running": "Çalışıyor",
|
||||
"management.status.waitingForHuman": "Girdi bekleniyor",
|
||||
@@ -154,6 +155,8 @@
|
||||
"projectStatus.failed_other": "{{count}} başarısız konu",
|
||||
"projectStatus.loading_one": "{{count}} yükleniyor konu",
|
||||
"projectStatus.loading_other": "{{count}} yükleniyor konu",
|
||||
"projectStatus.unread_one": "{{count}} konu okunmamış yanıt içeriyor",
|
||||
"projectStatus.unread_other": "{{count}} konu okunmamış yanıtlar içeriyor",
|
||||
"projectStatus.waitingForHuman_one": "{{count}} konu yanıt bekliyor",
|
||||
"projectStatus.waitingForHuman_other": "{{count}} konu yanıt bekliyor",
|
||||
"renameModal.description": "Kısa ve kolay tanınabilir olsun.",
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
"agentDefaultMessage": "Chào bạn, tôi là **{{name}}**. Một câu là đủ.\n\nMuốn tôi phù hợp hơn với quy trình làm việc của bạn? Truy cập [Cài đặt Tác nhân]({{url}}) và điền Hồ sơ Tác nhân (bạn có thể chỉnh sửa bất cứ lúc nào).",
|
||||
"agentDefaultMessageWithSystemRole": "Chào bạn, tôi là **{{name}}**. Một câu là đủ—bạn là người kiểm soát.",
|
||||
"agentDefaultMessageWithoutEdit": "Chào bạn, tôi là **{{name}}**. Một câu là đủ—bạn là người kiểm soát.",
|
||||
"agentDocument.backToChat": "Quay lại trò chuyện",
|
||||
"agentDocument.linkCopied": "Liên kết đã được sao chép",
|
||||
"agentDocument.openAsPage": "Mở dưới dạng trang đầy đủ",
|
||||
"agentProfile.files_one": "{{count}} tệp",
|
||||
"agentProfile.files_other": "{{count}} tệp",
|
||||
"agentProfile.knowledgeBases_one": "{{count}} cơ sở tri thức",
|
||||
@@ -165,7 +168,9 @@
|
||||
"extendParams.urlContext.title": "Trích xuất nội dung liên kết web",
|
||||
"followUpPlaceholder": "Theo dõi. Dùng @ để giao nhiệm vụ cho tác vụ khác.",
|
||||
"followUpPlaceholderHeterogeneous": "Tiếp tục.",
|
||||
"gatewayMode.title": "Chế độ Gateway",
|
||||
"gatewayMode.beta": "Beta",
|
||||
"gatewayMode.cardTitle": "Chế độ Cổng Đại lý",
|
||||
"gatewayMode.desc": "Chạy các đại lý trên đám mây thông qua Cổng Đại lý của LobeHub. Nhiệm vụ vẫn tiếp tục chạy ngay cả khi bạn đóng trang.",
|
||||
"group.desc": "Thực hiện nhiệm vụ với nhiều Tác nhân trong một không gian chung.",
|
||||
"group.memberTooltip": "Có {{count}} thành viên trong nhóm",
|
||||
"group.orchestratorThinking": "Điều phối viên đang suy nghĩ...",
|
||||
@@ -877,6 +882,7 @@
|
||||
"toolAuth.authorize": "Ủy quyền",
|
||||
"toolAuth.authorizing": "Đang ủy quyền...",
|
||||
"toolAuth.hint": "Nếu không có ủy quyền hoặc cấu hình, kỹ năng có thể không hoạt động. Điều này có thể giới hạn tác nhân hoặc gây lỗi.",
|
||||
"toolAuth.remove": "Xóa",
|
||||
"toolAuth.signIn": "Đăng nhập",
|
||||
"toolAuth.title": "Ủy quyền kỹ năng cho tác nhân này",
|
||||
"topic.checkOpenNewTopic": "Bắt đầu chủ đề mới?",
|
||||
@@ -1118,6 +1124,5 @@
|
||||
"workingPanel.skills.title": "Kỹ năng",
|
||||
"workingPanel.space": "Khoảng trống",
|
||||
"workingPanel.title": "Working Panel",
|
||||
"you": "Bạn",
|
||||
"zenMode": "Chế độ tập trung"
|
||||
"you": "Bạn"
|
||||
}
|
||||
|
||||
@@ -3,18 +3,26 @@
|
||||
"fleet.allShown": "Tất cả các nhiệm vụ đang chạy đều được hiển thị",
|
||||
"fleet.backToHome": "Quay về trang chủ",
|
||||
"fleet.closeColumn": "Đóng cột",
|
||||
"fleet.closeIdleColumns": "Đóng các cột không hoạt động",
|
||||
"fleet.closeIdleColumnsCount": "Đóng {{count}} cột không hoạt động",
|
||||
"fleet.collapseReply": "Thu gọn",
|
||||
"fleet.createTask": "Tạo nhiệm vụ",
|
||||
"fleet.dragHint": "Kéo để sắp xếp lại",
|
||||
"fleet.empty": "Không có nhiệm vụ nào đang mở",
|
||||
"fleet.emptyDesc": "Chọn một nhiệm vụ đang chạy ở bên trái, hoặc sử dụng + để thêm cột.",
|
||||
"fleet.noRunningTasks": "Không có nhiệm vụ nào đang chạy",
|
||||
"fleet.openInChat": "Mở trong trò chuyện",
|
||||
"fleet.pin": "Ghim cột",
|
||||
"fleet.reply": "Trả lời",
|
||||
"fleet.runningTasks": "Nhiệm vụ đang chạy",
|
||||
"fleet.rows.one": "Một hàng",
|
||||
"fleet.rows.two": "Hai hàng",
|
||||
"fleet.runningBoard": "Bảng chạy",
|
||||
"fleet.status.idle": "Nhàn rỗi",
|
||||
"fleet.status.paused": "Tạm dừng",
|
||||
"fleet.status.running": "Đang chạy",
|
||||
"fleet.status.scheduled": "Đã lên lịch",
|
||||
"fleet.tooltip": "Xem tất cả các tác nhân cạnh nhau",
|
||||
"fleet.unpin": "Bỏ ghim cột",
|
||||
"gateway.description": "Mô tả",
|
||||
"gateway.descriptionPlaceholder": "Không bắt buộc",
|
||||
"gateway.deviceName": "Tên thiết bị",
|
||||
@@ -30,6 +38,7 @@
|
||||
"navigation.discoverMcp": "Khám phá MCP",
|
||||
"navigation.discoverModels": "Khám phá Mô hình",
|
||||
"navigation.discoverProviders": "Khám phá Nhà cung cấp",
|
||||
"navigation.document": "Tài liệu",
|
||||
"navigation.group": "Nhóm",
|
||||
"navigation.groupChat": "Trò chuyện Nhóm",
|
||||
"navigation.home": "Trang chủ",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user