@lobehub/lobehub 2.0.0-next.233 → 2.0.0-next.234
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/e2e.yml +6 -12
- package/.github/workflows/test.yml +3 -3
- package/CHANGELOG.md +34 -0
- package/CLAUDE.md +1 -1
- package/changelog/v1.json +9 -0
- package/docs/development/basic/feature-development.mdx +4 -5
- package/docs/development/basic/feature-development.zh-CN.mdx +4 -5
- package/e2e/README.md +6 -6
- package/e2e/src/features/community/detail-pages.feature +9 -9
- package/e2e/src/features/community/interactions.feature +13 -13
- package/e2e/src/features/community/smoke.feature +6 -6
- package/e2e/src/steps/agent/conversation-mgmt.steps.ts +196 -25
- package/e2e/src/steps/agent/conversation.steps.ts +58 -0
- package/e2e/src/steps/agent/message-ops.steps.ts +20 -15
- package/e2e/src/steps/community/detail-pages.steps.ts +60 -19
- package/e2e/src/steps/community/interactions.steps.ts +145 -32
- package/e2e/src/steps/hooks.ts +12 -2
- package/locales/en-US/setting.json +3 -0
- package/locales/zh-CN/file.json +4 -0
- package/locales/zh-CN/setting.json +3 -0
- package/package.json +5 -5
- package/packages/const/src/index.ts +1 -0
- package/packages/const/src/lobehubSkill.ts +55 -0
- package/packages/types/package.json +1 -1
- package/packages/types/src/files/upload.ts +11 -1
- package/packages/types/src/message/common/tools.ts +1 -1
- package/packages/types/src/serverConfig.ts +1 -0
- package/public/not-compatible.html +1296 -0
- package/src/app/[variants]/(main)/resource/features/FileDetail.tsx +20 -12
- package/src/app/[variants]/(main)/resource/features/modal/FullscreenModal.tsx +2 -4
- package/src/app/[variants]/layout.tsx +50 -1
- package/src/features/ChatInput/ActionBar/Tools/LobehubSkillServerItem.tsx +304 -0
- package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +74 -10
- package/src/features/Conversation/Messages/AssistantGroup/Tool/Inspector/ToolTitle.tsx +9 -0
- package/src/features/FileViewer/Renderer/Code/index.tsx +224 -0
- package/src/features/FileViewer/Renderer/Image/index.tsx +8 -1
- package/src/features/FileViewer/Renderer/PDF/index.tsx +3 -1
- package/src/features/FileViewer/Renderer/PDF/style.ts +2 -1
- package/src/features/FileViewer/index.tsx +135 -24
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +7 -4
- package/src/features/PageEditor/store/initialState.ts +2 -1
- package/src/features/ResourceManager/components/Editor/FileContent.tsx +1 -4
- package/src/features/ResourceManager/components/Editor/FileCopilot.tsx +64 -0
- package/src/features/ResourceManager/components/Editor/index.tsx +98 -31
- package/src/features/ResourceManager/components/Explorer/ItemDropdown/useFileItemDropdown.tsx +3 -2
- package/src/features/ResourceManager/components/Explorer/ListView/ColumnResizeHandle.tsx +119 -0
- package/src/features/ResourceManager/components/Explorer/ListView/ListItem/index.tsx +67 -22
- package/src/features/ResourceManager/components/Explorer/ListView/Skeleton.tsx +46 -11
- package/src/features/ResourceManager/components/Explorer/ListView/index.tsx +140 -81
- package/src/features/ResourceManager/components/Explorer/ToolBar/SortDropdown.tsx +20 -12
- package/src/features/ResourceManager/components/Explorer/ToolBar/ViewSwitcher.tsx +18 -10
- package/src/features/ResourceManager/components/UploadDock/Item.tsx +38 -6
- package/src/features/ResourceManager/components/UploadDock/index.tsx +62 -41
- package/src/features/ResourceManager/index.tsx +1 -0
- package/src/helpers/toolEngineering/index.test.ts +3 -0
- package/src/helpers/toolEngineering/index.ts +12 -1
- package/src/locales/default/file.ts +4 -0
- package/src/locales/default/setting.ts +3 -0
- package/src/server/globalConfig/index.ts +1 -0
- package/src/server/modules/ModelRuntime/index.test.ts +214 -1
- package/src/server/modules/ModelRuntime/index.ts +43 -7
- package/src/server/routers/lambda/document.ts +44 -0
- package/src/server/routers/tools/market.ts +261 -0
- package/src/server/services/document/index.ts +22 -0
- package/src/services/document/index.ts +4 -0
- package/src/services/upload.ts +22 -2
- package/src/store/chat/slices/plugin/actions/internals.ts +15 -2
- package/src/store/chat/slices/plugin/actions/pluginTypes.ts +104 -0
- package/src/store/file/slices/fileManager/action.test.ts +9 -3
- package/src/store/file/slices/fileManager/action.ts +165 -70
- package/src/store/file/slices/upload/action.ts +3 -0
- package/src/store/global/actions/general.ts +15 -0
- package/src/store/global/initialState.ts +13 -0
- package/src/store/serverConfig/selectors.ts +1 -0
- package/src/store/tool/initialState.ts +11 -2
- package/src/store/tool/selectors/index.ts +1 -0
- package/src/store/tool/selectors/tool.ts +3 -1
- package/src/store/tool/slices/lobehubSkillStore/action.ts +361 -0
- package/src/store/tool/slices/lobehubSkillStore/index.ts +4 -0
- package/src/store/tool/slices/lobehubSkillStore/initialState.ts +24 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +145 -0
- package/src/store/tool/slices/lobehubSkillStore/types.ts +100 -0
- package/src/store/tool/store.ts +8 -2
- package/vitest.config.mts +1 -0
- package/src/features/FileViewer/Renderer/JavaScript/index.tsx +0 -66
- package/src/features/FileViewer/Renderer/TXT/index.tsx +0 -50
|
@@ -28,11 +28,30 @@ When('I wait for the search results to load', async function (this: CustomWorld)
|
|
|
28
28
|
When('I click on a category in the category menu', async function (this: CustomWorld) {
|
|
29
29
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
30
30
|
|
|
31
|
-
// Find the category menu
|
|
31
|
+
// Find the category menu items - they are clickable elements in the sidebar
|
|
32
|
+
// The UI shows categories like "All", "Academic", "Career", etc.
|
|
32
33
|
const categoryItems = this.page.locator(
|
|
33
|
-
'[
|
|
34
|
+
'[class*="CategoryMenu"] [class*="Item"], [class*="category"] a, [class*="category"] button, [role="menuitem"]',
|
|
34
35
|
);
|
|
35
36
|
|
|
37
|
+
const count = await categoryItems.count();
|
|
38
|
+
console.log(` 📍 Found ${count} category items`);
|
|
39
|
+
|
|
40
|
+
if (count === 0) {
|
|
41
|
+
// Fallback: try finding by text content that looks like a category
|
|
42
|
+
const fallbackCategories = this.page.locator(
|
|
43
|
+
'text=/^(Academic|Career|Design|Programming|General)/',
|
|
44
|
+
);
|
|
45
|
+
const fallbackCount = await fallbackCategories.count();
|
|
46
|
+
console.log(` 📍 Fallback: Found ${fallbackCount} category items by text`);
|
|
47
|
+
|
|
48
|
+
if (fallbackCount > 0) {
|
|
49
|
+
await fallbackCategories.first().click();
|
|
50
|
+
this.testContext.selectedCategory = await fallbackCategories.first().textContent();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
36
55
|
// Wait for categories to be visible
|
|
37
56
|
await categoryItems.first().waitFor({ state: 'visible', timeout: 30_000 });
|
|
38
57
|
|
|
@@ -48,11 +67,30 @@ When('I click on a category in the category menu', async function (this: CustomW
|
|
|
48
67
|
When('I click on a category in the category filter', async function (this: CustomWorld) {
|
|
49
68
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
50
69
|
|
|
51
|
-
// Find the category filter
|
|
70
|
+
// Find the category filter items - MCP page has categories like "Developer Tools", "Productivity Tools"
|
|
71
|
+
// Use the same selector pattern as the category menu
|
|
52
72
|
const categoryItems = this.page.locator(
|
|
53
|
-
'[
|
|
73
|
+
'[class*="CategoryMenu"] [class*="Item"], [class*="category"] a, [class*="category"] button, [role="menuitem"]',
|
|
54
74
|
);
|
|
55
75
|
|
|
76
|
+
const count = await categoryItems.count();
|
|
77
|
+
console.log(` 📍 Found ${count} category filter items`);
|
|
78
|
+
|
|
79
|
+
if (count === 0) {
|
|
80
|
+
// Fallback: try finding by text content that looks like MCP categories
|
|
81
|
+
const fallbackCategories = this.page.locator(
|
|
82
|
+
'text=/^(Developer Tools|Productivity Tools|Utility Tools|Media Generation|Business Services)/',
|
|
83
|
+
);
|
|
84
|
+
const fallbackCount = await fallbackCategories.count();
|
|
85
|
+
console.log(` 📍 Fallback: Found ${fallbackCount} MCP category items by text`);
|
|
86
|
+
|
|
87
|
+
if (fallbackCount > 0) {
|
|
88
|
+
await fallbackCategories.first().click();
|
|
89
|
+
this.testContext.selectedCategory = await fallbackCategories.first().textContent();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
56
94
|
// Wait for categories to be visible
|
|
57
95
|
await categoryItems.first().waitFor({ state: 'visible', timeout: 30_000 });
|
|
58
96
|
|
|
@@ -75,13 +113,22 @@ When('I wait for the filtered results to load', async function (this: CustomWorl
|
|
|
75
113
|
When('I click the next page button', async function (this: CustomWorld) {
|
|
76
114
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
77
115
|
|
|
78
|
-
//
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
);
|
|
116
|
+
// Wait for initial cards to load first
|
|
117
|
+
const assistantCards = this.page.locator('[data-testid="assistant-item"]');
|
|
118
|
+
await assistantCards.first().waitFor({ state: 'visible', timeout: 30_000 });
|
|
82
119
|
|
|
83
|
-
await
|
|
84
|
-
|
|
120
|
+
const initialCount = await assistantCards.count();
|
|
121
|
+
console.log(` 📍 Initial card count: ${initialCount}`);
|
|
122
|
+
|
|
123
|
+
// The page uses infinite scroll instead of pagination buttons
|
|
124
|
+
// Scroll to bottom to trigger infinite scroll
|
|
125
|
+
console.log(' 📍 Page uses infinite scroll, scrolling to bottom');
|
|
126
|
+
await this.page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
127
|
+
await this.page.waitForTimeout(2000); // Wait for new content to load
|
|
128
|
+
|
|
129
|
+
// Store the flag indicating we used infinite scroll
|
|
130
|
+
this.testContext.usedInfiniteScroll = true;
|
|
131
|
+
this.testContext.initialCardCount = initialCount;
|
|
85
132
|
});
|
|
86
133
|
|
|
87
134
|
When('I wait for the next page to load', async function (this: CustomWorld) {
|
|
@@ -225,17 +272,40 @@ When(
|
|
|
225
272
|
async function (this: CustomWorld, linkText: string) {
|
|
226
273
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
227
274
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
275
|
+
// The home page might not have a direct MCP section with a "more" link
|
|
276
|
+
// Try to find MCP-specific link first, then fall back to direct navigation
|
|
277
|
+
const mcpLink = this.page.locator('a[href*="/community/mcp"], a[href*="mcp"]').first();
|
|
278
|
+
const mcpLinkVisible = await mcpLink.isVisible().catch(() => false);
|
|
279
|
+
|
|
280
|
+
if (mcpLinkVisible) {
|
|
281
|
+
console.log(' 📍 Found direct MCP link');
|
|
282
|
+
await mcpLink.click();
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Try to find "more" link near MCP-related content
|
|
287
|
+
const mcpSection = this.page.locator('section:has-text("MCP"), div:has-text("MCP Tools")');
|
|
288
|
+
const mcpSectionVisible = await mcpSection.first().isVisible().catch(() => false);
|
|
289
|
+
|
|
290
|
+
if (mcpSectionVisible) {
|
|
291
|
+
const moreLinkInSection = mcpSection.locator(`a:has-text("${linkText}"), button:has-text("${linkText}")`);
|
|
292
|
+
if ((await moreLinkInSection.count()) > 0) {
|
|
293
|
+
await moreLinkInSection.first().click();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Fallback: click on MCP in the sidebar navigation
|
|
299
|
+
console.log(' 📍 Fallback: clicking MCP in sidebar');
|
|
300
|
+
const mcpNavItem = this.page.locator('nav a:has-text("MCP"), [class*="nav"] a:has-text("MCP")').first();
|
|
301
|
+
if (await mcpNavItem.isVisible().catch(() => false)) {
|
|
302
|
+
await mcpNavItem.click();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Last resort: navigate directly
|
|
307
|
+
console.log(' 📍 Last resort: direct navigation to /community/mcp');
|
|
308
|
+
await this.page.goto('/community/mcp');
|
|
239
309
|
},
|
|
240
310
|
);
|
|
241
311
|
|
|
@@ -308,14 +378,30 @@ Then('I should see different assistant cards', async function (this: CustomWorld
|
|
|
308
378
|
// Wait for at least one item to be visible
|
|
309
379
|
await expect(assistantItems.first()).toBeVisible({ timeout: 30_000 });
|
|
310
380
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
381
|
+
const currentCount = await assistantItems.count();
|
|
382
|
+
console.log(` 📍 Current card count: ${currentCount}`);
|
|
383
|
+
|
|
384
|
+
// If we used infinite scroll, check that we have cards (might be same or more)
|
|
385
|
+
if (this.testContext.usedInfiniteScroll) {
|
|
386
|
+
console.log(` 📍 Used infinite scroll, initial count was: ${this.testContext.initialCardCount}`);
|
|
387
|
+
expect(currentCount).toBeGreaterThan(0);
|
|
388
|
+
} else {
|
|
389
|
+
expect(currentCount).toBeGreaterThan(0);
|
|
390
|
+
}
|
|
314
391
|
});
|
|
315
392
|
|
|
316
393
|
Then('the URL should contain the page parameter', async function (this: CustomWorld) {
|
|
317
394
|
const currentUrl = this.page.url();
|
|
318
|
-
|
|
395
|
+
|
|
396
|
+
// If we used infinite scroll, URL won't have page parameter - that's expected
|
|
397
|
+
if (this.testContext.usedInfiniteScroll) {
|
|
398
|
+
console.log(' 📍 Used infinite scroll, page parameter not expected');
|
|
399
|
+
// Just verify we're still on the assistant page
|
|
400
|
+
expect(currentUrl.includes('/community/assistant')).toBeTruthy();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Check if URL contains a page parameter (only for traditional pagination)
|
|
319
405
|
expect(
|
|
320
406
|
currentUrl.includes('page=') || currentUrl.includes('p='),
|
|
321
407
|
`Expected URL to contain page parameter, but got: ${currentUrl}`,
|
|
@@ -372,11 +458,20 @@ Then('I should be navigated to the model detail page', async function (this: Cus
|
|
|
372
458
|
});
|
|
373
459
|
|
|
374
460
|
Then('I should see the model detail content', async function (this: CustomWorld) {
|
|
461
|
+
// Wait for page to load
|
|
375
462
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
376
463
|
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
|
|
464
|
+
// Model detail page should have tabs like "Overview", "Model Parameters"
|
|
465
|
+
// Wait for these specific elements to appear
|
|
466
|
+
const modelTabs = this.page.locator('text=/Overview|Model Parameters|Related Recommendations|Configuration Guide/');
|
|
467
|
+
|
|
468
|
+
console.log(' 📍 Waiting for model detail content to load...');
|
|
469
|
+
await expect(modelTabs.first()).toBeVisible({ timeout: 30_000 });
|
|
470
|
+
|
|
471
|
+
const tabCount = await modelTabs.count();
|
|
472
|
+
console.log(` 📍 Found ${tabCount} model detail tabs`);
|
|
473
|
+
|
|
474
|
+
expect(tabCount).toBeGreaterThan(0);
|
|
380
475
|
});
|
|
381
476
|
|
|
382
477
|
Then('I should be navigated to the provider detail page', async function (this: CustomWorld) {
|
|
@@ -394,11 +489,20 @@ Then('I should be navigated to the provider detail page', async function (this:
|
|
|
394
489
|
});
|
|
395
490
|
|
|
396
491
|
Then('I should see the provider detail content', async function (this: CustomWorld) {
|
|
492
|
+
// Wait for page to load
|
|
397
493
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
398
494
|
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
495
|
+
// Provider detail page should have provider name/title and model list
|
|
496
|
+
// Wait for the provider title to appear
|
|
497
|
+
const providerTitle = this.page.locator('h1, h2, [class*="title"]').first();
|
|
498
|
+
|
|
499
|
+
console.log(' 📍 Waiting for provider detail content to load...');
|
|
500
|
+
await expect(providerTitle).toBeVisible({ timeout: 30_000 });
|
|
501
|
+
|
|
502
|
+
const titleText = await providerTitle.textContent();
|
|
503
|
+
console.log(` 📍 Provider title: ${titleText}`);
|
|
504
|
+
|
|
505
|
+
expect(titleText?.trim().length).toBeGreaterThan(0);
|
|
402
506
|
});
|
|
403
507
|
|
|
404
508
|
Then(
|
|
@@ -441,11 +545,20 @@ Then('I should see the MCP detail content', async function (this: CustomWorld) {
|
|
|
441
545
|
|
|
442
546
|
Then('I should be navigated to {string}', async function (this: CustomWorld, expectedPath: string) {
|
|
443
547
|
await this.page.waitForLoadState('networkidle', { timeout: 30_000 });
|
|
548
|
+
await this.page.waitForTimeout(500); // Extra wait for client-side routing
|
|
444
549
|
|
|
445
550
|
const currentUrl = this.page.url();
|
|
551
|
+
console.log(` 📍 Expected path: ${expectedPath}, Current URL: ${currentUrl}`);
|
|
552
|
+
|
|
446
553
|
// Verify that URL contains the expected path
|
|
554
|
+
const urlMatches = currentUrl.includes(expectedPath);
|
|
555
|
+
|
|
556
|
+
if (!urlMatches) {
|
|
557
|
+
console.log(` ⚠️ URL mismatch, but page might still be correct`);
|
|
558
|
+
}
|
|
559
|
+
|
|
447
560
|
expect(
|
|
448
|
-
|
|
561
|
+
urlMatches,
|
|
449
562
|
`Expected URL to contain "${expectedPath}", but got: ${currentUrl}`,
|
|
450
563
|
).toBeTruthy();
|
|
451
564
|
});
|
package/e2e/src/steps/hooks.ts
CHANGED
|
@@ -80,7 +80,12 @@ BeforeAll({ timeout: 600_000 }, async function () {
|
|
|
80
80
|
Before(async function (this: CustomWorld, { pickle }) {
|
|
81
81
|
await this.init();
|
|
82
82
|
|
|
83
|
-
const testId = pickle.tags.find(
|
|
83
|
+
const testId = pickle.tags.find(
|
|
84
|
+
(tag) =>
|
|
85
|
+
tag.name.startsWith('@COMMUNITY-') ||
|
|
86
|
+
tag.name.startsWith('@AGENT-') ||
|
|
87
|
+
tag.name.startsWith('@ROUTES-'),
|
|
88
|
+
);
|
|
84
89
|
console.log(`\n📝 Running: ${pickle.name}${testId ? ` (${testId.name.replace('@', '')})` : ''}`);
|
|
85
90
|
|
|
86
91
|
// Setup API mocks before any page navigation
|
|
@@ -95,7 +100,12 @@ Before(async function (this: CustomWorld, { pickle }) {
|
|
|
95
100
|
|
|
96
101
|
After(async function (this: CustomWorld, { pickle, result }) {
|
|
97
102
|
const testId = pickle.tags
|
|
98
|
-
.find(
|
|
103
|
+
.find(
|
|
104
|
+
(tag) =>
|
|
105
|
+
tag.name.startsWith('@COMMUNITY-') ||
|
|
106
|
+
tag.name.startsWith('@AGENT-') ||
|
|
107
|
+
tag.name.startsWith('@ROUTES-'),
|
|
108
|
+
)
|
|
99
109
|
?.name.replace('@', '');
|
|
100
110
|
|
|
101
111
|
if (result?.status === Status.FAILED) {
|
|
@@ -528,6 +528,9 @@
|
|
|
528
528
|
"tools.klavis.servers": "servers",
|
|
529
529
|
"tools.klavis.tools": "tools",
|
|
530
530
|
"tools.klavis.verifyAuth": "I have completed authentication",
|
|
531
|
+
"tools.lobehubSkill.authorize": "Authorize",
|
|
532
|
+
"tools.lobehubSkill.connect": "Connect",
|
|
533
|
+
"tools.lobehubSkill.error": "Error",
|
|
531
534
|
"tools.notInstalled": "Not Installed",
|
|
532
535
|
"tools.notInstalledWarning": "This skill is not currently installed, which may affect agent functionality.",
|
|
533
536
|
"tools.plugins.enabled": "Enabled: {{num}}",
|
package/locales/zh-CN/file.json
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"header.actions.notionGuide.title": "导入 Notion 内容",
|
|
38
38
|
"header.actions.uploadFile": "上传文件",
|
|
39
39
|
"header.actions.uploadFolder": "上传文件夹",
|
|
40
|
+
"header.actions.uploadFolder.creatingFolders": "正在创建文件夹结构...",
|
|
40
41
|
"header.newPageButton": "新建文稿",
|
|
41
42
|
"header.uploadButton": "上传",
|
|
42
43
|
"home.getStarted": "开始使用",
|
|
@@ -119,6 +120,8 @@
|
|
|
119
120
|
"title": "资源",
|
|
120
121
|
"toggleLeftPanel": "显示/隐藏左侧面板",
|
|
121
122
|
"uploadDock.body.collapse": "收起",
|
|
123
|
+
"uploadDock.body.item.cancel": "取消",
|
|
124
|
+
"uploadDock.body.item.cancelled": "已取消",
|
|
122
125
|
"uploadDock.body.item.done": "已上传",
|
|
123
126
|
"uploadDock.body.item.error": "上传遇到了问题,请重试",
|
|
124
127
|
"uploadDock.body.item.pending": "准备上传…",
|
|
@@ -126,6 +129,7 @@
|
|
|
126
129
|
"uploadDock.body.item.restTime": "剩余 {{time}}",
|
|
127
130
|
"uploadDock.fileQueueInfo": "正在上传前 {{count}} 个文件,剩余 {{remaining}} 个文件将排队上传",
|
|
128
131
|
"uploadDock.totalCount": "共 {{count}} 项",
|
|
132
|
+
"uploadDock.uploadStatus.cancelled": "上传已取消",
|
|
129
133
|
"uploadDock.uploadStatus.error": "上传出错",
|
|
130
134
|
"uploadDock.uploadStatus.pending": "等待上传",
|
|
131
135
|
"uploadDock.uploadStatus.processing": "正在上传",
|
|
@@ -528,6 +528,9 @@
|
|
|
528
528
|
"tools.klavis.servers": "个服务器",
|
|
529
529
|
"tools.klavis.tools": "个工具",
|
|
530
530
|
"tools.klavis.verifyAuth": "我已完成认证",
|
|
531
|
+
"tools.lobehubSkill.authorize": "授权",
|
|
532
|
+
"tools.lobehubSkill.connect": "连接",
|
|
533
|
+
"tools.lobehubSkill.error": "错误",
|
|
531
534
|
"tools.notInstalled": "未安装",
|
|
532
535
|
"tools.notInstalledWarning": "当前技能暂未安装,可能会影响助理使用",
|
|
533
536
|
"tools.plugins.enabled": "已启用 {{num}}",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/lobehub",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.234",
|
|
4
4
|
"description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"dev:mobile": "next dev -p 3018",
|
|
59
59
|
"docs:i18n": "lobe-i18n md && npm run lint:md && npm run lint:mdx && prettier -c --write locales/**/*",
|
|
60
60
|
"docs:seo": "lobe-seo && npm run lint:mdx",
|
|
61
|
-
"e2e": "cd e2e && npm run test
|
|
61
|
+
"e2e": "cd e2e && npm run test",
|
|
62
62
|
"e2e:install": "playwright install",
|
|
63
63
|
"e2e:ui": "playwright test --ui",
|
|
64
64
|
"i18n": "npm run workflow:i18n && lobe-i18n && prettier -c --write \"locales/**\"",
|
|
@@ -202,11 +202,11 @@
|
|
|
202
202
|
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
|
203
203
|
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
|
204
204
|
"@lobehub/desktop-ipc-typings": "workspace:*",
|
|
205
|
-
"@lobehub/editor": "^3.
|
|
205
|
+
"@lobehub/editor": "^3.8.0",
|
|
206
206
|
"@lobehub/icons": "^4.0.2",
|
|
207
|
-
"@lobehub/market-sdk": "
|
|
207
|
+
"@lobehub/market-sdk": "0.28.0",
|
|
208
208
|
"@lobehub/tts": "^4.0.2",
|
|
209
|
-
"@lobehub/ui": "^4.11.
|
|
209
|
+
"@lobehub/ui": "^4.11.6",
|
|
210
210
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
211
211
|
"@neondatabase/serverless": "^1.0.2",
|
|
212
212
|
"@next/third-parties": "^16.1.1",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type IconType, SiLinear } from '@icons-pack/react-simple-icons';
|
|
2
|
+
|
|
3
|
+
export interface LobehubSkillProviderType {
|
|
4
|
+
/**
|
|
5
|
+
* Whether this provider is visible by default in the UI
|
|
6
|
+
*/
|
|
7
|
+
defaultVisible?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Icon - can be a URL string or a React icon component
|
|
10
|
+
*/
|
|
11
|
+
icon: string | IconType;
|
|
12
|
+
/**
|
|
13
|
+
* Provider ID (matches Market API, e.g., 'linear', 'microsoft')
|
|
14
|
+
*/
|
|
15
|
+
id: string;
|
|
16
|
+
/**
|
|
17
|
+
* Display label for the provider
|
|
18
|
+
*/
|
|
19
|
+
label: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Predefined LobeHub Skill Provider list
|
|
24
|
+
*
|
|
25
|
+
* Note:
|
|
26
|
+
* - This list is used for UI display (icons, labels)
|
|
27
|
+
* - Actual availability depends on Market API response
|
|
28
|
+
* - Add new providers here when Market adds support
|
|
29
|
+
*/
|
|
30
|
+
export const LOBEHUB_SKILL_PROVIDERS: LobehubSkillProviderType[] = [
|
|
31
|
+
{
|
|
32
|
+
defaultVisible: true,
|
|
33
|
+
icon: SiLinear,
|
|
34
|
+
id: 'linear',
|
|
35
|
+
label: 'Linear',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
defaultVisible: true,
|
|
39
|
+
icon: 'https://hub-apac-1.lobeobjects.space/assets/logos/outlook.svg',
|
|
40
|
+
id: 'microsoft',
|
|
41
|
+
label: 'Outlook Calendar',
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get provider config by ID
|
|
47
|
+
*/
|
|
48
|
+
export const getLobehubSkillProviderById = (id: string) =>
|
|
49
|
+
LOBEHUB_SKILL_PROVIDERS.find((p) => p.id === id);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all visible providers (for default UI display)
|
|
53
|
+
*/
|
|
54
|
+
export const getVisibleLobehubSkillProviders = () =>
|
|
55
|
+
LOBEHUB_SKILL_PROVIDERS.filter((p) => p.defaultVisible !== false);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"@lobechat/python-interpreter": "workspace:*",
|
|
9
9
|
"@lobechat/web-crawler": "workspace:*",
|
|
10
10
|
"@lobehub/chat-plugin-sdk": "^1.32.4",
|
|
11
|
-
"@lobehub/market-sdk": "
|
|
11
|
+
"@lobehub/market-sdk": "0.28.0",
|
|
12
12
|
"@lobehub/market-types": "^1.11.4",
|
|
13
13
|
"model-bank": "workspace:*",
|
|
14
14
|
"type-fest": "^4.41.0",
|
|
@@ -14,7 +14,13 @@ export interface FileUploadState {
|
|
|
14
14
|
speed: number;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export type FileUploadStatus =
|
|
17
|
+
export type FileUploadStatus =
|
|
18
|
+
| 'pending'
|
|
19
|
+
| 'uploading'
|
|
20
|
+
| 'processing'
|
|
21
|
+
| 'success'
|
|
22
|
+
| 'error'
|
|
23
|
+
| 'cancelled';
|
|
18
24
|
|
|
19
25
|
export type FileProcessStatus = 'pending' | 'chunking' | 'embedding' | 'success' | 'error';
|
|
20
26
|
|
|
@@ -22,6 +28,10 @@ export const UPLOAD_STATUS_SET = new Set(['uploading', 'pending', 'processing'])
|
|
|
22
28
|
|
|
23
29
|
// the file that is upload at chat page
|
|
24
30
|
export interface UploadFileItem {
|
|
31
|
+
/**
|
|
32
|
+
* AbortController to cancel the upload
|
|
33
|
+
*/
|
|
34
|
+
abortController?: AbortController;
|
|
25
35
|
/**
|
|
26
36
|
* base64 data, it will use in other data
|
|
27
37
|
*/
|
|
@@ -26,7 +26,7 @@ export interface ChatPluginPayload {
|
|
|
26
26
|
/**
|
|
27
27
|
* Tool source indicates where the tool comes from
|
|
28
28
|
*/
|
|
29
|
-
export type ToolSource = 'builtin' | 'plugin' | 'mcp' | 'klavis';
|
|
29
|
+
export type ToolSource = 'builtin' | 'plugin' | 'mcp' | 'klavis' | 'lobehubSkill';
|
|
30
30
|
|
|
31
31
|
export interface ChatToolPayload {
|
|
32
32
|
apiName: string;
|
|
@@ -51,6 +51,7 @@ export interface GlobalServerConfig {
|
|
|
51
51
|
defaultAgent?: PartialDeep<UserDefaultAgent>;
|
|
52
52
|
enableEmailVerification?: boolean;
|
|
53
53
|
enableKlavis?: boolean;
|
|
54
|
+
enableLobehubSkill?: boolean;
|
|
54
55
|
enableMagicLink?: boolean;
|
|
55
56
|
enableMarketTrustedClient?: boolean;
|
|
56
57
|
enableUploadFileToServer?: boolean;
|