Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Fork goose with custom MCP widgets, platform extensions (aider, git, web, search), MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { PUBLISH_ROOT_DIR } from './user-publish.mjs';
|
||||
|
||||
const URL_PATTERN =
|
||||
/https?:\/\/[^\s<>"')\]]+\/(?:MindSpace|temp)\/([a-z0-9._-]+)\/([^\s<>"')\]]+\.html)/gi;
|
||||
|
||||
function decodePathSegment(segment) {
|
||||
try {
|
||||
return decodeURIComponent(segment);
|
||||
} catch {
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
|
||||
export function extractStaticPageLinks(content, { username } = {}) {
|
||||
const text = String(content ?? '');
|
||||
const links = [];
|
||||
const seen = new Set();
|
||||
for (const match of text.matchAll(URL_PATTERN)) {
|
||||
const owner = decodePathSegment(match[1]).toLowerCase();
|
||||
const relativePath = decodePathSegment(match[2]);
|
||||
if (username && owner !== String(username).trim().toLowerCase()) continue;
|
||||
const key = `${owner}/${relativePath}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
links.push({
|
||||
publicUrl: match[0],
|
||||
owner,
|
||||
relativePath,
|
||||
filename: path.basename(relativePath),
|
||||
});
|
||||
}
|
||||
return links;
|
||||
}
|
||||
|
||||
export function resolvePublishHtmlAbsolutePath(h5Root, username, relativePath) {
|
||||
const slug = String(username ?? '').trim().toLowerCase();
|
||||
const clean = String(relativePath ?? '')
|
||||
.replace(/^\/+/, '')
|
||||
.split('/')
|
||||
.filter((part) => part && part !== '.' && part !== '..')
|
||||
.join('/');
|
||||
if (!slug || !clean || !clean.toLowerCase().endsWith('.html')) {
|
||||
throw Object.assign(new Error('无效的页面路径'), { code: 'invalid_page_path' });
|
||||
}
|
||||
const publishRoot = path.resolve(h5Root, PUBLISH_ROOT_DIR, slug);
|
||||
const absolute = path.resolve(publishRoot, clean);
|
||||
if (absolute !== publishRoot && !absolute.startsWith(`${publishRoot}${path.sep}`)) {
|
||||
throw Object.assign(new Error('页面路径越界'), { code: 'invalid_page_path' });
|
||||
}
|
||||
return absolute;
|
||||
}
|
||||
|
||||
export async function readPublishHtml(h5Root, username, relativePath) {
|
||||
const absolute = resolvePublishHtmlAbsolutePath(h5Root, username, relativePath);
|
||||
const content = await fs.readFile(absolute, 'utf8');
|
||||
if (!content.trim()) {
|
||||
throw Object.assign(new Error('页面内容为空'), { code: 'empty_page_content' });
|
||||
}
|
||||
return { absolute, content, relativePath, filename: path.basename(relativePath) };
|
||||
}
|
||||
|
||||
function titleFromHtml(html) {
|
||||
const match = String(html).match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
return match?.[1]?.trim() ?? '';
|
||||
}
|
||||
|
||||
function summaryFromHtml(html) {
|
||||
const stripped = String(html)
|
||||
.replace(/<script[\s\S]*?<\/script>/gi, ' ')
|
||||
.replace(/<style[\s\S]*?<\/style>/gi, ' ')
|
||||
.replace(/<[^>]+>/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
return stripped.slice(0, 180);
|
||||
}
|
||||
|
||||
export function analyzeChatMessageForSave({
|
||||
content,
|
||||
username,
|
||||
h5Root,
|
||||
selectedLinkIndex = 0,
|
||||
}) {
|
||||
const links = extractStaticPageLinks(content, { username });
|
||||
const text = String(content ?? '').replace(/\s+/g, ' ').trim();
|
||||
const suggestedTitleFromText = text
|
||||
.replace(/^#{1,6}\s*/, '')
|
||||
.replace(/[*_`~[\]]/g, '')
|
||||
.trim()
|
||||
.slice(0, 48);
|
||||
|
||||
if (links.length === 0) {
|
||||
return {
|
||||
contentMode: 'markdown',
|
||||
links: [],
|
||||
selectedLink: null,
|
||||
suggestedTitle: suggestedTitleFromText || 'AI 创作页面',
|
||||
suggestedSummary: text.slice(0, 160),
|
||||
previewUrl: null,
|
||||
relativePath: null,
|
||||
filename: null,
|
||||
};
|
||||
}
|
||||
|
||||
const index = Math.min(Math.max(0, selectedLinkIndex), links.length - 1);
|
||||
const selectedLink = links[index];
|
||||
return {
|
||||
contentMode: 'static_html',
|
||||
links,
|
||||
selectedLink,
|
||||
suggestedTitle:
|
||||
selectedLink.filename.replace(/\.html$/i, '').replace(/[-_]/g, ' ') ||
|
||||
suggestedTitleFromText ||
|
||||
'AI 生成页面',
|
||||
suggestedSummary: text.slice(0, 160),
|
||||
previewUrl: selectedLink.publicUrl,
|
||||
relativePath: selectedLink.relativePath,
|
||||
filename: selectedLink.filename,
|
||||
h5Root,
|
||||
username,
|
||||
};
|
||||
}
|
||||
|
||||
export async function resolveStaticHtmlContent(analysis) {
|
||||
if (analysis.contentMode !== 'static_html' || !analysis.selectedLink) {
|
||||
return null;
|
||||
}
|
||||
const loaded = await readPublishHtml(
|
||||
analysis.h5Root,
|
||||
analysis.username,
|
||||
analysis.selectedLink.relativePath,
|
||||
);
|
||||
const title = titleFromHtml(loaded.content);
|
||||
const summary = summaryFromHtml(loaded.content);
|
||||
return {
|
||||
...loaded,
|
||||
suggestedTitle: title || analysis.suggestedTitle,
|
||||
suggestedSummary: summary || analysis.suggestedSummary,
|
||||
publicUrl: analysis.selectedLink.publicUrl,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user