122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import type {
|
||
CustomWorldAgentMessage,
|
||
CustomWorldAgentOperationRecord,
|
||
CustomWorldWorkSummary,
|
||
} from '../../../packages/shared/src/contracts/customWorldAgent';
|
||
import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime';
|
||
import { ApiClientError, isTimeoutError } from '../../services/apiClient';
|
||
import type { CustomWorldProfile } from '../../types';
|
||
|
||
export function resolveRpgEntryErrorMessage(error: unknown, fallback: string) {
|
||
if (isTimeoutError(error)) {
|
||
if (/智能创作/u.test(fallback)) {
|
||
return '开启智能创作工作区超时,请确认运行时后端已启动后重试。';
|
||
}
|
||
if (/拼图/u.test(fallback)) {
|
||
return '开启拼图创作工作台超时,请确认运行时后端已启动后重试。';
|
||
}
|
||
if (/大鱼吃小鱼/u.test(fallback)) {
|
||
return '开启大鱼吃小鱼创作工作台超时,请确认运行时后端已启动后重试。';
|
||
}
|
||
if (/共创工作台/u.test(fallback)) {
|
||
return '开启创作工作台超时,请确认运行时后端已启动后重试。';
|
||
}
|
||
return '请求超时,请稍后重试。';
|
||
}
|
||
|
||
if (
|
||
error instanceof ApiClientError &&
|
||
error.status === 401 &&
|
||
(error.code === 'UNAUTHORIZED' ||
|
||
error.message.includes('Authorization Bearer Token'))
|
||
) {
|
||
return '当前登录状态已失效,请重新登录后继续。';
|
||
}
|
||
|
||
return error instanceof Error ? error.message : fallback;
|
||
}
|
||
|
||
export function createFailedRpgEntryAgentOperation(params: {
|
||
type: CustomWorldAgentOperationRecord['type'];
|
||
phaseLabel: string;
|
||
error: string;
|
||
}): CustomWorldAgentOperationRecord {
|
||
return {
|
||
operationId: `local-failed-${Date.now()}`,
|
||
type: params.type,
|
||
status: 'failed',
|
||
phaseLabel: params.phaseLabel,
|
||
phaseDetail: params.error,
|
||
progress: 0,
|
||
error: params.error,
|
||
startedAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
};
|
||
}
|
||
|
||
export function buildOptimisticRpgEntryAgentMessage(
|
||
payload: Pick<CustomWorldAgentMessage, 'id' | 'role' | 'kind' | 'text'>,
|
||
): CustomWorldAgentMessage {
|
||
return {
|
||
...payload,
|
||
createdAt: new Date().toISOString(),
|
||
relatedOperationId: null,
|
||
};
|
||
}
|
||
|
||
export function normalizeRpgEntryAgentBackedProfile(
|
||
profile: CustomWorldProfile,
|
||
) {
|
||
// 中文注释:保存前 canonicalize 已迁到 server-rs;
|
||
// 这里保留透传函数只为了兼容旧导入,不再改写正式 profile 字段。
|
||
return profile;
|
||
}
|
||
|
||
export function stringifyRpgEntryAgentBackedProfile(
|
||
profile: CustomWorldProfile,
|
||
) {
|
||
return JSON.stringify(profile);
|
||
}
|
||
|
||
export function buildRpgEntryCreationHubFallbackItems(
|
||
entries: CustomWorldLibraryEntry<CustomWorldProfile>[],
|
||
): CustomWorldWorkSummary[] {
|
||
return entries
|
||
.filter((entry) => entry.visibility === 'published')
|
||
.map((entry) => ({
|
||
workId: `fallback:${entry.profileId}`,
|
||
sourceType: 'published_profile',
|
||
status: 'published',
|
||
title: entry.worldName,
|
||
subtitle: entry.subtitle || '已发布作品',
|
||
summary: entry.summaryText || '继续补完这个世界的设定与游玩入口。',
|
||
coverImageSrc: entry.coverImageSrc,
|
||
coverRenderMode: 'image',
|
||
coverCharacterImageSrcs: [],
|
||
updatedAt: entry.updatedAt,
|
||
publishedAt: entry.publishedAt,
|
||
stage: null,
|
||
stageLabel: '已发布',
|
||
playableNpcCount: entry.playableNpcCount,
|
||
landmarkCount: entry.landmarkCount,
|
||
roleVisualReadyCount: 0,
|
||
roleAnimationReadyCount: 0,
|
||
roleAssetSummaryLabel: null,
|
||
sessionId: null,
|
||
profileId: entry.profileId,
|
||
canResume: false,
|
||
canEnterWorld: true,
|
||
}));
|
||
}
|
||
|
||
/**
|
||
* 兼容创作链工作包已经接入的旧 helper 命名,避免本轮迁移波及其他并行改动。
|
||
*/
|
||
export const resolveRpgCreationErrorMessage = resolveRpgEntryErrorMessage;
|
||
export const createFailedAgentOperation = createFailedRpgEntryAgentOperation;
|
||
export const buildOptimisticAgentMessage = buildOptimisticRpgEntryAgentMessage;
|
||
export const normalizeAgentBackedProfile = normalizeRpgEntryAgentBackedProfile;
|
||
export const stringifyAgentBackedProfile = stringifyRpgEntryAgentBackedProfile;
|
||
export const buildCreationHubFallbackItems =
|
||
buildRpgEntryCreationHubFallbackItems;
|