🐛 fix: bump next to 16.1.5 to fix CVE-2026-23864 (#11886)

* fix tests

* bump next to 16.1.5
This commit is contained in:
Arvin Xu
2026-01-27 15:11:48 +08:00
committed by GitHub
parent e63ad2d547
commit 7d9e690646
4 changed files with 106 additions and 116 deletions
+4 -4
View File
@@ -211,7 +211,7 @@
"@modelcontextprotocol/sdk": "^1.25.3",
"@napi-rs/canvas": "^0.1.88",
"@neondatabase/serverless": "^1.0.2",
"@next/third-parties": "^16.1.4",
"@next/third-parties": "^16.1.5",
"@opentelemetry/exporter-jaeger": "^2.5.0",
"@opentelemetry/winston-transport": "^0.19.0",
"@react-pdf/renderer": "^4.3.2",
@@ -280,7 +280,7 @@
"model-bank": "workspace:*",
"motion": "^12.29.0",
"nanoid": "^5.1.6",
"next": "^16.1.4",
"next": "^16.1.5",
"next-mdx-remote": "^5.0.0",
"next-themes": "^0.4.6",
"nextjs-toploader": "^3.9.17",
@@ -369,8 +369,8 @@
"@lobehub/lint": "^1.26.3",
"@lobehub/market-types": "^1.12.3",
"@lobehub/seo-cli": "^1.7.0",
"@next/bundle-analyzer": "^16.1.4",
"@next/eslint-plugin-next": "^16.1.4",
"@next/bundle-analyzer": "^16.1.5",
"@next/eslint-plugin-next": "^16.1.5",
"@peculiar/webcrypto": "^1.5.0",
"@playwright/test": "^1.58.0",
"@prettier/sync": "^0.6.1",
+5
View File
@@ -356,6 +356,11 @@ export interface TaskStatusResult {
currentActivity?: TaskCurrentActivity;
/** Error message if task failed */
error?: string;
/**
* Parsed UI messages from conversation-flow
* Used for displaying intermediate steps in server task
*/
messages?: UIChatMessage[];
/** Task result content (last assistant message) */
result?: string;
/** Current task status */
+93 -88
View File
@@ -1,4 +1,5 @@
import { type AgentRuntimeContext } from '@lobechat/agent-runtime';
import { parse } from '@lobechat/conversation-flow';
import {
type TaskCurrentActivity,
type TaskStatusResult,
@@ -251,94 +252,6 @@ const aiAgentProcedure = authedProcedure.use(serverDatabase).use(async (opts) =>
});
export const aiAgentRouter = router({
/**
* Create Thread for client-side task execution
*
* This endpoint is called by desktop client when runInClient=true.
* It creates the Thread but does NOT execute the task - execution happens on client side.
*/
createClientTaskThread: aiAgentProcedure
.input(CreateClientTaskThreadSchema)
.mutation(async ({ input, ctx }) => {
const { agentId, groupId, instruction, parentMessageId, title, topicId } = input;
log('createClientTaskThread: agentId=%s, groupId=%s', agentId, groupId);
try {
// 1. Create Thread for isolated task execution
const startedAt = new Date().toISOString();
const thread = await ctx.threadModel.create({
agentId,
groupId,
metadata: { clientMode: true, startedAt },
sourceMessageId: parentMessageId,
status: ThreadStatus.Processing,
title,
topicId,
type: ThreadType.Isolation,
});
if (!thread) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to create thread for task execution',
});
}
log('createClientTaskThread: created thread %s', thread.id);
// 2. Create initial user message (persisted to database)
const userMessage = await ctx.messageModel.create({
agentId,
content: instruction,
groupId,
parentId: parentMessageId,
role: 'user',
threadId: thread.id,
topicId,
});
log('createClientTaskThread: created user message %s', userMessage.id);
// 3. Query thread messages and main chat messages in parallel
const [threadMessages, messages] = await Promise.all([
// Thread messages (messages within this thread)
ctx.messageModel.query({ agentId, threadId: thread.id, topicId }),
// Main chat messages (messages without threadId, includes updated taskDetail)
// Pass both agentId and groupId - query() prioritizes groupId when present
ctx.messageModel.query({ agentId, groupId, topicId }),
]);
log(
'createClientTaskThread: queried %d thread messages, %d main messages',
threadMessages.length,
messages.length,
);
// 4. Return Thread, userMessageId, threadMessages and messages
return {
messages,
startedAt,
success: true,
threadId: thread.id,
threadMessages,
userMessageId: userMessage.id,
};
} catch (error: any) {
log('createClientTaskThread failed: %O', error);
if (error instanceof TRPCError) {
throw error;
}
throw new TRPCError({
cause: error,
code: 'INTERNAL_SERVER_ERROR',
message: `Failed to create client task thread: ${error.message}`,
});
}
}),
/**
* Create Thread for client-side task execution in Group mode
*
@@ -433,6 +346,94 @@ export const aiAgentRouter = router({
}
}),
/**
* Create Thread for client-side task execution
*
* This endpoint is called by desktop client when runInClient=true.
* It creates the Thread but does NOT execute the task - execution happens on client side.
*/
createClientTaskThread: aiAgentProcedure
.input(CreateClientTaskThreadSchema)
.mutation(async ({ input, ctx }) => {
const { agentId, groupId, instruction, parentMessageId, title, topicId } = input;
log('createClientTaskThread: agentId=%s, groupId=%s', agentId, groupId);
try {
// 1. Create Thread for isolated task execution
const startedAt = new Date().toISOString();
const thread = await ctx.threadModel.create({
agentId,
groupId,
metadata: { clientMode: true, startedAt },
sourceMessageId: parentMessageId,
status: ThreadStatus.Processing,
title,
topicId,
type: ThreadType.Isolation,
});
if (!thread) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to create thread for task execution',
});
}
log('createClientTaskThread: created thread %s', thread.id);
// 2. Create initial user message (persisted to database)
const userMessage = await ctx.messageModel.create({
agentId,
content: instruction,
groupId,
parentId: parentMessageId,
role: 'user',
threadId: thread.id,
topicId,
});
log('createClientTaskThread: created user message %s', userMessage.id);
// 3. Query thread messages and main chat messages in parallel
const [threadMessages, messages] = await Promise.all([
// Thread messages (messages within this thread)
ctx.messageModel.query({ agentId, threadId: thread.id, topicId }),
// Main chat messages (messages without threadId, includes updated taskDetail)
// Pass both agentId and groupId - query() prioritizes groupId when present
ctx.messageModel.query({ agentId, groupId, topicId }),
]);
log(
'createClientTaskThread: queried %d thread messages, %d main messages',
threadMessages.length,
messages.length,
);
// 4. Return Thread, userMessageId, threadMessages and messages
return {
messages,
startedAt,
success: true,
threadId: thread.id,
threadMessages,
userMessageId: userMessage.id,
};
} catch (error: any) {
log('createClientTaskThread failed: %O', error);
if (error instanceof TRPCError) {
throw error;
}
throw new TRPCError({
cause: error,
code: 'INTERNAL_SERVER_ERROR',
message: `Failed to create client task thread: ${error.message}`,
});
}
}),
createOperation: aiAgentProcedure
.input(CreateAgentOperationSchema)
.mutation(async ({ input, ctx }) => {
@@ -907,6 +908,9 @@ export const aiAgentRouter = router({
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
);
// 6.1 Parse messages using conversation-flow for UI display
const { flatList: parsedMessages } = parse(threadMessages);
// 7. Get result content when task is completed or failed
let resultContent: string | undefined;
if (updatedTaskStatus === 'completed' || updatedTaskStatus === 'failed') {
@@ -974,6 +978,7 @@ export const aiAgentRouter = router({
(updatedMetadata?.totalCost ? { total: updatedMetadata.totalCost } : undefined),
currentActivity,
error: updatedMetadata?.error ?? realtimeStatus?.currentState?.error,
messages: parsedMessages,
result: resultContent,
status: updatedTaskStatus,
stepCount: realtimeStatus?.currentState?.stepCount,
+4 -24
View File
@@ -35,10 +35,6 @@ vi.mock('semver', async (importOriginal) => {
};
});
vi.mock('url-join', () => ({
default: vi.fn((...args) => args.join('/')),
}));
// 模拟 process.env
const originalEnv = process.env;
@@ -282,33 +278,17 @@ describe('ChangelogService', () => {
});
describe('replaceCdnUrl', () => {
it('should replace URL with CDN URL if available', async () => {
// 设置环境变量
process.env.DOC_S3_PUBLIC_DOMAIN = 'https://cdn.example.com';
// 重新导入模块以确保环境变量生效
const { ChangelogService } = await import('./index');
const service = new ChangelogService();
service.cdnUrls = { 'https://example.com/image.jpg': 'image-hash.jpg' };
it('should replace /blog URL with CDN URL', () => {
// @ts-ignore - accessing private method for testing
const result = service.replaceCdnUrl('https://example.com/image.jpg');
const result = service.replaceCdnUrl('/blog/image.jpg');
expect(result).toBe('https://cdn.example.com/image-hash.jpg');
expect(result).toBe('https://hub-apac-1.lobeobjects.space/blog/image.jpg');
});
it('should return original URL if CDN URL is not available', () => {
const originalDocCdnPrefix = process.env.DOC_S3_PUBLIC_DOMAIN;
process.env.DOC_S3_PUBLIC_DOMAIN = 'https://cdn.example.com';
service.cdnUrls = {};
it('should return original URL if not starting with /blog', () => {
// @ts-ignore - accessing private method for testing
const result = service.replaceCdnUrl('https://example.com/image.jpg');
expect(result).toBe('https://example.com/image.jpg');
// Restore original value
process.env.DOC_S3_PUBLIC_DOMAIN = originalDocCdnPrefix;
});
});
});