Fix scheduled task page delivery unlock and notification links.
Memind CI / Test, build, and release guards (push) Failing after 5s

Finalize page delivery contracts after worker execution, fetch session messages via tkmindProxy, and append public page URLs to WeChat notifications.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-31 09:06:07 +08:00
parent 6acd79e4fa
commit 005612029f
3 changed files with 97 additions and 4 deletions
+91 -4
View File
@@ -1,5 +1,11 @@
import crypto from 'node:crypto'; import crypto from 'node:crypto';
import path from 'node:path';
import { buildChatSkillPrompt, SCHEDULED_TASK_AUTOMATION_SKILL_NAME } from './chat-skills.mjs'; import { buildChatSkillPrompt, SCHEDULED_TASK_AUTOMATION_SKILL_NAME } from './chat-skills.mjs';
import { markPageDeliveryContractReady } from './mindspace-delivery-contract.mjs';
import {
collectOwnPublicHtmlRelativePaths,
materializeMissingPublicHtmlWrites,
} from './mindspace-public-finish-sync.mjs';
import { localDateLabel } from './schedule-time.mjs'; import { localDateLabel } from './schedule-time.mjs';
function messageText(message) { function messageText(message) {
@@ -63,10 +69,65 @@ export function formatScheduledTaskDeliveryMessage(task, deliveryText) {
return `${header}\n\n${body}`.trim(); return `${header}\n\n${body}`.trim();
} }
export async function finalizeScheduledTaskPageDelivery({
pool,
userId,
sessionId,
messages,
publishDir,
currentUser = null,
logger = console,
} = {}) {
if (!pool || !userId || !publishDir) return [];
const materialized = materializeMissingPublicHtmlWrites({ messages, publishDir });
const relativePaths = new Set(
collectOwnPublicHtmlRelativePaths({
messages,
currentUser: currentUser ?? { id: userId },
publishDir,
materialized: materialized.materialized,
skipped: materialized.skipped,
}),
);
if (sessionId) {
const [rows] = await pool.query(
`SELECT workspace_relative_path
FROM h5_page_delivery_contracts
WHERE user_id = ? AND request_id = ? AND status = 'preparing'`,
[userId, sessionId],
);
for (const row of rows ?? []) {
if (row?.workspace_relative_path) relativePaths.add(row.workspace_relative_path);
}
}
const readyPaths = [];
for (const relativePath of relativePaths) {
const ready = await markPageDeliveryContractReady({
pool,
userId,
relativePath,
}).catch(() => false);
if (ready) readyPaths.push(relativePath);
else {
logger.warn?.('[ScheduledTask] delivery contract not ready', {
userId,
sessionId,
relativePath,
});
}
}
return readyPaths;
}
export async function executeScheduledTask(task, { export async function executeScheduledTask(task, {
userAuth, userAuth,
tkmindProxy, tkmindProxy,
sessionSnapshotService = null, sessionSnapshotService = null,
pool = null,
h5Root = null,
timeoutMs = 15 * 60 * 1000, timeoutMs = 15 * 60 * 1000,
logger = console, logger = console,
} = {}) { } = {}) {
@@ -113,19 +174,45 @@ export async function executeScheduledTask(task, {
); );
let messages = []; let messages = [];
if (typeof sessionSnapshotService?.refresh === 'function') { if (typeof tkmindProxy.fetchSessionConversationForUser === 'function') {
const refreshed = await sessionSnapshotService.refresh(sessionId).catch(() => null); messages = await tkmindProxy.fetchSessionConversationForUser(task.userId, sessionId).catch(() => []);
messages = refreshed?.messages ?? refreshed?.conversation?.messages ?? [];
} else if (typeof sessionSnapshotService?.get === 'function') { } else if (typeof sessionSnapshotService?.get === 'function') {
const snapshot = await sessionSnapshotService.get(sessionId).catch(() => null); const snapshot = await sessionSnapshotService.get(sessionId).catch(() => null);
messages = snapshot?.messages ?? snapshot?.conversation?.messages ?? []; messages = snapshot?.messages ?? snapshot?.conversation?.messages ?? [];
} }
const deliveryText = extractScheduledTaskDeliveryText(messages, task); const publishDir = h5Root && task.userId
? path.join(h5Root, 'MindSpace', task.userId)
: null;
const readyPaths = await finalizeScheduledTaskPageDelivery({
pool,
userId: task.userId,
sessionId,
messages,
publishDir,
logger,
}).catch((error) => {
logger.warn?.('[ScheduledTask] finalize page delivery failed:', error);
return [];
});
let deliveryText = extractScheduledTaskDeliveryText(messages, task);
if (
readyPaths.length > 0
&& !/https?:\/\//i.test(deliveryText)
&& task.userId
) {
const links = readyPaths.map(
(relativePath) => `https://m.tkmind.cn/MindSpace/${task.userId}/${relativePath}`,
);
deliveryText = `${deliveryText}\n\n页面链接:\n${links.join('\n')}`.trim();
}
return { return {
sessionId, sessionId,
requestId, requestId,
deliveryText, deliveryText,
messages, messages,
readyPaths,
}; };
} }
+4
View File
@@ -11,6 +11,8 @@ export function startScheduledTaskWorker({
tkmindProxy = null, tkmindProxy = null,
sessionSnapshotService = null, sessionSnapshotService = null,
notificationDispatcher = null, notificationDispatcher = null,
pool = null,
h5Root = null,
logger = console, logger = console,
intervalMs = Number(process.env.H5_SCHEDULED_TASK_SCAN_INTERVAL_MS ?? 30_000), intervalMs = Number(process.env.H5_SCHEDULED_TASK_SCAN_INTERVAL_MS ?? 30_000),
maxAttempts = Number(process.env.H5_SCHEDULED_TASK_MAX_ATTEMPTS ?? 3), maxAttempts = Number(process.env.H5_SCHEDULED_TASK_MAX_ATTEMPTS ?? 3),
@@ -70,6 +72,8 @@ export function startScheduledTaskWorker({
userAuth, userAuth,
tkmindProxy, tkmindProxy,
sessionSnapshotService, sessionSnapshotService,
pool,
h5Root,
timeoutMs: executionTimeoutMs, timeoutMs: executionTimeoutMs,
logger, logger,
}); });
@@ -286,6 +286,8 @@ export async function bootstrapPortalIntegrationServices({
tkmindProxy, tkmindProxy,
sessionSnapshotService, sessionSnapshotService,
notificationDispatcher, notificationDispatcher,
pool,
h5Root,
logger, logger,
}); });
logger.log('Scheduled task worker enabled'); logger.log('Scheduled task worker enabled');