import path from 'node:path'; import { DEFAULT_MAX_FILE_BYTES } from './mindspace.mjs'; import { createMindSpaceServiceFacade } from './mindspace-service.mjs'; import { createLocalMindSpaceStorageAdapter } from './mindspace-storage-adapter.mjs'; import { normalizeWorkspaceRelativePath } from './mindspace-workspace-relative-path.mjs'; import { buildPublicUrl, PUBLISH_ROOT_DIR, resolveGoosedSandboxRoot, resolvePublicBaseUrl, resolvePublishDir, } from './user-publish.mjs'; export function resolvePageWorkspaceRelativePath(row) { const fromColumn = normalizeWorkspaceRelativePath(row?.workspace_relative_path); if (fromColumn) return fromColumn; let snapshot = row?.source_snapshot_json; if (typeof snapshot === 'string') { try { snapshot = JSON.parse(snapshot); } catch { snapshot = null; } } return normalizeWorkspaceRelativePath(snapshot?.relative_path); } export function isPublicWorkspaceHtmlRelativePath(relativePath) { const normalized = normalizeWorkspaceRelativePath(relativePath); return Boolean(normalized?.startsWith('public/') && normalized.toLowerCase().endsWith('.html')); } /** MindSpace 工作区静态页 canonical 公网 URL(/MindSpace/<用户ID>/public/...) */ export function resolveWorkspaceMindSpacePublicUrl({ h5Root, env = process.env, userId, relativePath, } = {}) { const normalized = normalizeWorkspaceRelativePath(relativePath); if (!userId || !normalized || !isPublicWorkspaceHtmlRelativePath(normalized)) return null; return buildMindSpacePublicUrlForUser({ h5Root: h5Root ?? process.cwd(), env, user: userId, relativePath: normalized, }); } export function resolveMindSpaceStorageRoot(h5Root, env = process.env) { return path.resolve(env.MINDSPACE_STORAGE_ROOT ?? path.join(h5Root, 'data', 'mindspace')); } export function resolveMindSpaceRuntimeConfig(h5Root, env = process.env) { return { storageRoot: resolveMindSpaceStorageRoot(h5Root, env), publicBaseUrl: resolvePublicBaseUrl(env), }; } export function resolveMindSpaceServerRuntimeOptions(h5Root, env = process.env) { const runtime = resolveMindSpaceRuntimeConfig(h5Root, env); return { ...runtime, adapterKind: String(env.MINDSPACE_SERVER_ADAPTER ?? 'local').trim().toLowerCase() || 'local', publishRoot: resolveMindSpacePublishRoot(h5Root), maxFileBytes: Number(env.MINDSPACE_MAX_FILE_BYTES ?? DEFAULT_MAX_FILE_BYTES), aiDailyLimit: Number(env.MINDSPACE_FREE_AI_DAILY_LIMIT ?? 10), publicPageLimit: Number(env.MINDSPACE_FREE_PUBLIC_PAGE_LIMIT ?? 5), monthlyViewLimit: Number(env.MINDSPACE_FREE_MONTHLY_VIEW_LIMIT ?? 1000), experienceEnabled: env.MINDSPACE_EXPERIENCE_ENABLED !== 'false', agentWorker: { enabled: env.MINDSPACE_AGENT_WORKER_ENABLED === 'true', concurrency: Math.max(1, Number(env.MINDSPACE_AGENT_WORKER_CONCURRENCY ?? 2)), pollMs: Math.max(200, Number(env.MINDSPACE_AGENT_WORKER_POLL_MS ?? 1000)), staleMs: Math.max(10_000, Number(env.MINDSPACE_AGENT_WORKER_STALE_MS ?? 5 * 60 * 1000)), ssePollMs: Math.max(500, Number(env.MINDSPACE_AGENT_SSE_POLL_MS ?? 1000)), }, remote: { baseUrl: String(env.MINDSPACE_REMOTE_BASE_URL ?? '').trim(), authToken: String(env.MINDSPACE_REMOTE_AUTH_TOKEN ?? '').trim(), operationBasePath: String(env.MINDSPACE_REMOTE_OPERATION_BASE_PATH ?? '/mindspace/v1/adapter') .trim(), timeoutMs: Math.max(1000, Number(env.MINDSPACE_REMOTE_TIMEOUT_MS ?? 15_000)), }, }; } export function resolveMindSpacePublishRoot(h5Root) { return path.resolve(h5Root, PUBLISH_ROOT_DIR); } export function resolveMindSpaceUserPublishDir(h5Root, user) { return path.resolve(resolvePublishDir(h5Root, user)); } export function buildMindSpacePublicRoutePath(ownerKey, segments = []) { const userId = String(ownerKey ?? '').trim(); if (!userId) { throw new Error('MindSpace public route requires an owner key'); } const cleanSegments = Array.isArray(segments) ? segments.map((segment) => encodeURIComponent(String(segment ?? ''))) : []; return `/${PUBLISH_ROOT_DIR}/${encodeURIComponent(userId)}${cleanSegments.length ? `/${cleanSegments.join('/')}` : '/'}`; } export function buildMindSpacePublicUrlForUser({ h5Root, env = process.env, user, relativePath = '' } = {}) { const ownerKey = typeof user === 'string' || typeof user === 'number' ? String(user) : String(user?.id ?? '').trim(); if (!ownerKey) { throw new Error('MindSpace public URL requires a user id'); } const { publicBaseUrl } = resolveMindSpaceRuntimeConfig(h5Root, env); return buildPublicUrl(publicBaseUrl, ownerKey, relativePath); } export function buildMindSpaceWorkspaceRef(user) { const ownerKey = typeof user === 'string' || typeof user === 'number' ? String(user) : String(user?.id ?? '').trim(); if (!ownerKey) { throw new Error('MindSpace workspace ref requires a user id'); } return `mindspace://users/${encodeURIComponent(ownerKey)}/workspace`; } export function resolveMindSpaceAgentWorkspaceCapability({ h5Root, env = process.env, user } = {}) { return { workspaceRoot: resolveMindSpaceUserPublishDir(h5Root, user), sandboxRoot: resolveGoosedSandboxRoot(h5Root, user, env), workspaceRef: buildMindSpaceWorkspaceRef(user), }; } export function createMindSpaceLocalRuntime({ h5Root, env = process.env, conversationPackageBackfill, conversationPackagePublicHtmlHydrator, logger = console, } = {}) { const config = resolveMindSpaceRuntimeConfig(h5Root, env); return { ...config, storageAdapter: createLocalMindSpaceStorageAdapter(config.storageRoot), serviceFacade: createMindSpaceServiceFacade({ storageAdapter: createLocalMindSpaceStorageAdapter(config.storageRoot), publicBaseUrl: config.publicBaseUrl, conversationPackageBackfill, conversationPackagePublicHtmlHydrator, logger, }), }; }