2baf29b3ae
Deliver encrypted health zone, observation API, baseline maturity pipeline, page-data bindings, and H5/WeChat channel integration for health P0. Co-authored-by: Cursor <cursoragent@cursor.com>
128 lines
4.4 KiB
JavaScript
128 lines
4.4 KiB
JavaScript
import path from 'node:path';
|
|
import { extractStaticPageLinks } from './mindspace-chat-save.mjs';
|
|
import { HEALTH_ASSISTANT_SKILL_NAME } from './health-feature.mjs';
|
|
import {
|
|
HEALTH_REPORT_FILENAME_PATTERN,
|
|
healthReportPageExists,
|
|
} from './health-report-page.mjs';
|
|
|
|
const MARKDOWN_LINK_PATTERN = /\[([^\]]+)\]\((https?:\/\/[^)]+|\/MindSpace\/[^)]+)\)/g;
|
|
const BARE_MINDSPACE_HTML_PATTERN = /https?:\/\/[^\s)]+?\/MindSpace\/[^\s)]+?\.html/gi;
|
|
|
|
function messageText(message) {
|
|
const content = message?.content;
|
|
if (typeof content === 'string') return content;
|
|
if (!Array.isArray(content)) return '';
|
|
return content
|
|
.filter((item) => item?.type === 'text')
|
|
.map((item) => String(item.text ?? ''))
|
|
.join('\n');
|
|
}
|
|
|
|
function setMessageText(message, text) {
|
|
if (!message || typeof message !== 'object') return message;
|
|
const next = { ...message };
|
|
if (Array.isArray(next.content)) {
|
|
next.content = next.content.map((item) => {
|
|
if (item?.type !== 'text') return item;
|
|
return { ...item, text };
|
|
});
|
|
} else {
|
|
next.content = [{ type: 'text', text }];
|
|
}
|
|
if (next.metadata && typeof next.metadata === 'object') {
|
|
next.metadata = {
|
|
...next.metadata,
|
|
displayText: text,
|
|
};
|
|
}
|
|
return next;
|
|
}
|
|
|
|
export function readSelectedChatSkill(messages) {
|
|
for (let index = (Array.isArray(messages) ? messages.length : 0) - 1; index >= 0; index -= 1) {
|
|
const message = messages[index];
|
|
if (message?.role !== 'user') continue;
|
|
const skill = message?.metadata?.memindRun?.selectedChatSkill
|
|
?? message?.metadata?.selectedChatSkill;
|
|
if (typeof skill === 'string' && skill.trim()) return skill.trim();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function isHealthReportArtifactPath(relativePath) {
|
|
const basename = path.posix.basename(String(relativePath ?? '').replace(/\\/g, '/'));
|
|
return HEALTH_REPORT_FILENAME_PATTERN.test(basename);
|
|
}
|
|
|
|
export function healthReportLinkIsVerified(link, { h5Root, userId }) {
|
|
const relativePath = String(link?.relativePath ?? '');
|
|
if (!isHealthReportArtifactPath(relativePath)) return true;
|
|
return healthReportPageExists(h5Root, userId, relativePath);
|
|
}
|
|
|
|
export function sanitizeHealthReportDeliveryText(
|
|
text,
|
|
{ userId, username = null, h5Root = process.cwd() } = {},
|
|
) {
|
|
const original = String(text ?? '');
|
|
if (!original.trim()) return { text: original, stripped: false, removed: [] };
|
|
|
|
const removed = [];
|
|
let next = original;
|
|
|
|
for (const match of original.matchAll(MARKDOWN_LINK_PATTERN)) {
|
|
const full = match[0];
|
|
const url = match[2];
|
|
const links = extractStaticPageLinks(url, { userId, username });
|
|
const healthLinks = links.filter((link) => isHealthReportArtifactPath(link.relativePath));
|
|
if (healthLinks.length === 0) continue;
|
|
const invalid = healthLinks.some((link) => !healthReportLinkIsVerified(link, { h5Root, userId }));
|
|
if (!invalid) continue;
|
|
next = next.replace(full, '(报告页链接已移除:文件尚未落盘,请重新生成或回复「生成健康报告」)');
|
|
removed.push(full);
|
|
}
|
|
|
|
for (const match of original.matchAll(BARE_MINDSPACE_HTML_PATTERN)) {
|
|
const url = match[0];
|
|
const links = extractStaticPageLinks(url, { userId, username });
|
|
const healthLinks = links.filter((link) => isHealthReportArtifactPath(link.relativePath));
|
|
if (healthLinks.length === 0) continue;
|
|
const invalid = healthLinks.some((link) => !healthReportLinkIsVerified(link, { h5Root, userId }));
|
|
if (!invalid) continue;
|
|
next = next.replace(url, '(报告页链接已移除:文件尚未落盘)');
|
|
removed.push(url);
|
|
}
|
|
|
|
return {
|
|
text: next,
|
|
stripped: removed.length > 0,
|
|
removed,
|
|
};
|
|
}
|
|
|
|
export function sanitizeHealthAssistantReportDelivery(messages, {
|
|
userId,
|
|
username = null,
|
|
h5Root = process.cwd(),
|
|
} = {}) {
|
|
const list = Array.isArray(messages) ? messages : [];
|
|
if (readSelectedChatSkill(list) !== HEALTH_ASSISTANT_SKILL_NAME) {
|
|
return { messages: list, changed: false, removed: [] };
|
|
}
|
|
|
|
const removed = [];
|
|
let changed = false;
|
|
const sanitized = list.map((message) => {
|
|
if (message?.role !== 'assistant') return message;
|
|
const text = messageText(message);
|
|
const result = sanitizeHealthReportDeliveryText(text, { userId, username, h5Root });
|
|
if (!result.stripped) return message;
|
|
changed = true;
|
|
removed.push(...result.removed);
|
|
return setMessageText(message, result.text);
|
|
});
|
|
|
|
return { messages: sanitized, changed, removed };
|
|
}
|