feat(image): complete reviewed generation delivery
Memind CI / Test, build, and release guards (pull_request) Successful in 2m47s

This commit is contained in:
john
2026-07-20 20:04:44 +08:00
parent fc3d27aae0
commit 4b15610aa8
42 changed files with 1764 additions and 97 deletions
+44 -6
View File
@@ -17,8 +17,10 @@ import { createWorkspacePageDeliverService } from './mindspace-workspace-page-de
import { createUserDataSpaceService } from './user-data-space-service.mjs';
import { createToolGateway } from './tool-gateway.mjs';
import { createAssetGatewayConfigService } from './asset-gateway.mjs';
import { createImageMakeAdminConfigService } from './image-make-admin-config.mjs';
import { createImageMakeClientFromEnv } from './image-make-client.mjs';
import { createMindSpaceImageGenerationService } from './mindspace-image-generation.mjs';
import { createMindSpaceImageReviewService } from './mindspace-image-review.mjs';
import { attachMindSpaceImageGenerationRoutes } from './mindspace-image-generation-routes.mjs';
import { createAgentRunGateway } from './agent-run-gateway.mjs';
import {
@@ -177,6 +179,7 @@ import {
} from './mindspace-chat-docx-package.mjs';
import { scanContent } from './mindspace-content-scan.mjs';
import { scanWorkspaceFilesForProhibitedBrowserStorage } from './mindspace-browser-storage-policy.mjs';
import { listRecentlyModifiedPublicHtmlRelativePaths } from './mindspace-run-public-html-scope.mjs';
import { renderImageAssetViewerHtml, wantsInlineImageViewer } from './mindspace-asset-preview.mjs';
import { DEFAULT_IMAGE_UPLOAD_MAX_BYTES } from './user-image-normalize.mjs';
import { createRechargeService } from './billing-recharge.mjs';
@@ -367,6 +370,7 @@ function endSessionPageDelivery(sessionId) {
let chatIntentRouter = null;
let toolGateway = null;
let assetGatewayConfigService = null;
let imageMakeAdminConfigService = null;
let mindSpaceImageGeneration = null;
let directChatService = null;
let sessionSnapshotService = null;
@@ -670,6 +674,11 @@ async function bootstrapUserAuth() {
apiSecret: API_SECRET,
});
assetGatewayConfigService = createAssetGatewayConfigService(pool, { llmProviderService });
imageMakeAdminConfigService = createImageMakeAdminConfigService(pool, {
env: process.env,
llmProviderService,
});
await imageMakeAdminConfigService.ensureSchema();
let imageMakeClient = null;
try {
imageMakeClient = createImageMakeClientFromEnv(process.env);
@@ -680,6 +689,12 @@ async function bootstrapUserAuth() {
configService: assetGatewayConfigService,
assetService: mindSpaceAssets,
imageMakeClient,
imageReviewService: createMindSpaceImageReviewService({
llmProviderService,
env: process.env,
logger: console,
}),
env: process.env,
logger: console,
});
wordFilterService = createWordFilterService(pool);
@@ -2085,6 +2100,7 @@ api.use(async (req, res, next) => {
if (req.path === '/agent/mindspace_asset_delete') return next();
if (req.path === '/agent/mindspace_asset_download') return next();
if (req.path === '/agent/mindspace_image_generate') return next();
if (req.path === '/internal/image-make/runtime-config') return next();
if (req.path === '/config/blocked-words') return next();
if (req.method === 'GET' && /^\/mindspace\/v1\/assets\/[^/]+\/download$/.test(req.path)) {
return next();
@@ -2125,6 +2141,22 @@ api.use(async (req, res, next) => {
return res.status(401).json({ message: '未授权,请重新登录' });
});
api.get('/internal/image-make/runtime-config', async (req, res) => {
if (!imageMakeAdminConfigService?.authorizeRuntimeRequest) {
return res.status(503).json({ message: 'image_make 配置服务未启用' });
}
const authHeader = String(req.get('authorization') ?? '');
const token = authHeader.startsWith('Bearer ') ? authHeader.slice(7).trim() : '';
if (!imageMakeAdminConfigService.authorizeRuntimeRequest(token)) {
return res.status(401).json({ message: '未授权' });
}
const runtime = await imageMakeAdminConfigService.getRuntimeConfig();
if (!runtime.ok) {
return res.status(503).json({ message: runtime.message ?? 'image_make 运行时配置无效' });
}
return res.json(runtime);
});
attachAsrRoutes(api, { sendError, sendData });
attachMindSpaceImageGenerationRoutes(api, {
getService: () => mindSpaceImageGeneration,
@@ -3412,12 +3444,18 @@ async function syncUserGeneratedPages(userId, { sessionId = null, sinceMs = null
const discoveredRelativePaths = sessionId
? await listSessionPublicHtmlRelativePaths(userId, sessionId, { sinceMs })
: null;
// An empty artifact package does not prove that this run wrote no page: a
// normal static-page-publish flow may only leave a public HTML workspace
// file. `null` means "discover current workspace output"; `[]` would tell
// the sync service to inspect nothing and make a completed page run fail.
const pageDataRelativePaths = discoveredRelativePaths?.length
? discoveredRelativePaths
// A normal static-page-publish flow may only leave a workspace HTML file and
// no conversation artifact. Discover only files written around this run;
// `null` would rescan every historical page and let stale Page Data pages
// block an unrelated delivery.
const recentWorkspaceRelativePaths = sessionId && !discoveredRelativePaths?.length
? listRecentlyModifiedPublicHtmlRelativePaths(
resolveMindSpaceUserPublishDir(__dirname, { id: userId }),
{ sinceMs },
)
: [];
const pageDataRelativePaths = sessionId
? (discoveredRelativePaths?.length ? discoveredRelativePaths : recentWorkspaceRelativePaths)
: null;
if (workspacePageDeliver?.syncAndDeliver) {
return await workspacePageDeliver.syncAndDeliver(userId, { pageDataRelativePaths });