Compare commits

...

75 Commits

Author SHA1 Message Date
john 864197162d docs: close WeChat Page Data Aider path branch
Memind CI / Test, build, and release guards (push) Successful in 2m42s
2026-07-28 01:02:58 +08:00
john 8cd85a9b11 fix: pin Aider binary for portal canary
Memind CI / Test, build, and release guards (push) Has been cancelled
2026-07-28 01:01:03 +08:00
john 23dedf1264 docs: close WeChat Page Data canary branch
Memind CI / Test, build, and release guards (push) Successful in 1m50s
2026-07-28 00:39:21 +08:00
john 1e91195853 fix: enable WeChat Page Data review in canary
Memind CI / Test, build, and release guards (push) Has been cancelled
2026-07-28 00:38:18 +08:00
john 6070ec209a docs: close WeChat Page Data guard branch
Memind CI / Test, build, and release guards (push) Successful in 1m48s
2026-07-28 00:35:53 +08:00
john 8bfb7959fc fix: guard WeChat Page Data delivery with Aider review
Memind CI / Test, build, and release guards (push) Has been cancelled
2026-07-28 00:26:24 +08:00
john bc20e93893 docs: close retained local branches 2026-07-28 00:10:57 +08:00
john 0630157068 docs: register portal canary backup branch
Memind CI / Test, build, and release guards (push) Successful in 1m42s
2026-07-27 23:42:18 +08:00
john 7ba63a83b1 fix: split portal canary backup preflight
Memind CI / Test, build, and release guards (push) Successful in 1m40s
2026-07-27 23:22:05 +08:00
john 3de116d3e7 fix: hide platform actions from long image exports
Memind CI / Test, build, and release guards (push) Successful in 1m26s
2026-07-27 22:42:42 +08:00
john aff6ddf9d3 docs: keep completed branches as registered history 2026-07-27 22:42:42 +08:00
john a95681ea3a fix: treat pending Gitea CI as acceptable on synced main
Memind CI / Test, build, and release guards (push) Successful in 1m39s
Normal Portal releases should not block when commit status stays pending
after the candidate is already pushed to origin/main.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 22:36:40 +08:00
john 82b74d71ad fix: relax REL release gate checks for normal Portal publishes
Memind CI / Test, build, and release guards (push) Failing after 1m35s
Ignore local .runtime build output in REL-01, accept missing Gitea CI when
origin/main matches HEAD, and compare two clean runtime rebuilds for REL-03.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 22:26:01 +08:00
john 90ee4c38d5 fix: honor stored Rybbit analytics secret fallback
Memind CI / Test, build, and release guards (push) Successful in 1m37s
2026-07-27 22:14:06 +08:00
john c7718c683b fix: inject Rybbit tracking on m.tkmind.cn publication pages
Memind CI / Test, build, and release guards (push) Successful in 1m36s
Publication routes and share shells were missing the Rybbit tracker, and
bootstrap now reuses the stored MindSpace analytics secret when Rybbit env
secret is absent.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 22:10:07 +08:00
john 48c61a3279 fix: harden long conversation continuity
Memind CI / Test, build, and release guards (push) Successful in 1m28s
2026-07-27 21:39:34 +08:00
john 5ae166fc44 fix: exclude page actions from long images 2026-07-27 21:21:54 +08:00
john d1be5d9e2a fix: preserve replies after session recovery 2026-07-27 19:59:09 +08:00
john 17c59fde71 fix: isolate visual tool failures from agent runs 2026-07-27 19:20:23 +08:00
john 7002007128 feat: add public page view action 2026-07-27 18:35:29 +08:00
john 77be18d340 Merge branch 'codex/active-development-20260727'
Memind CI / Test, build, and release guards (push) Successful in 1m48s
2026-07-27 18:07:48 +08:00
john 6878827224 mindspace: decouple page list sync latency 2026-07-27 18:07:16 +08:00
john 40dcdaf49f mindspace: guard split-service contract drift 2026-07-27 17:48:34 +08:00
john 70264a4c4c mindspace: version runtime contracts and audit packages 2026-07-27 17:43:38 +08:00
john e94052ff24 mindspace: close authority boundaries 2026-07-27 15:34:35 +08:00
john dfab78c75a feat(workflow): add risk-based release gates 2026-07-27 10:38:24 +08:00
john c88623855f fix(dev): stop stale frontend when portal exits 2026-07-27 10:28:12 +08:00
john 6b479c16f9 test: make thumbnail refresh assertion portable
Memind CI / Test, build, and release guards (push) Successful in 1m50s
2026-07-27 09:43:43 +08:00
john 291178d2c3 chore: stop tracking generated runtime artifacts
Memind CI / Test, build, and release guards (push) Failing after 9m20s
2026-07-27 09:08:55 +08:00
john 56ff170a60 fix(chat): recover session list from snapshots 2026-07-27 09:08:55 +08:00
john 32da0c0583 fix(api): retry transient session reads safely 2026-07-27 09:08:54 +08:00
john 0708d56d61 docs: record obsolete branch disposition 2026-07-27 09:08:54 +08:00
john 5ca6ed9094 fix(release): allow stable fallback for release-only artifact promotions
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 08:43:00 +08:00
john cb38225704 fix(release): correct ahead/behind parsing for REL-02
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 08:40:15 +08:00
john 6e25008df2 fix(release): allow REL-02 when main is ahead with successful CI evidence
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 08:38:09 +08:00
john a6a9bb4eab feat(release): add incremental gate and auto CI status for 103 publish
When the runtime artifact is unchanged, carry forward prior gate results and
re-run only REL scenarios; resolve REL-02 from Gitea commit status and allow
stable promotion when canary matches the same artifact bundle.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 08:32:12 +08:00
tkmind 9e2aa4f71d Merge pull request 'fix(release): harden stable promotion after canary acceptance' (#35) from codex/fix-stable-promotion-release into main
Memind CI / Test, build, and release guards (push) Successful in 2m5s
2026-07-27 00:04:28 +00:00
john 41bf775c4c fix(release): harden stable promotion after canary acceptance
Memind CI / Test, build, and release guards (pull_request) Successful in 13m26s
Verify matching 103 canary evidence before 8081 promotion, fix agent-run
drain JSON parsing, tolerate macOS full-backup tar races, and extend goosed
remount health waits for non-interactive SSH releases.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 07:09:27 +08:00
tkmind 67bf2c2bbb Merge pull request 'fix: align DeepSeek canary tool rounds with gate' (#34)
Memind CI / Test, build, and release guards (push) Successful in 2h57m1s
Merge production canary DeepSeek tool-round compatibility lifecycle and regression coverage.
2026-07-26 16:25:24 +00:00
john fb6865638a fix: align DeepSeek canary tool rounds with gate
Memind CI / Test, build, and release guards (pull_request) Successful in 1m32s
2026-07-26 23:49:32 +08:00
john 046704816f fix: use container MCP path in portal canary
Memind CI / Test, build, and release guards (push) Successful in 1m41s
2026-07-26 22:34:58 +08:00
john 02f2dc6b17 fix: preserve canary release stdin across edge checks
Memind CI / Test, build, and release guards (push) Successful in 1m35s
2026-07-26 21:24:23 +08:00
john 473ad80882 fix: route portal canary through isolated edge tunnel
Memind CI / Test, build, and release guards (push) Has been cancelled
2026-07-26 20:30:56 +08:00
john 286069449b feat: add guarded portal canary release
Memind CI / Test, build, and release guards (push) Failing after 2m14s
2026-07-26 19:51:44 +08:00
john 058d646b32 chore: ignore generated release gate reports
Memind CI / Test, build, and release guards (push) Successful in 3m58s
2026-07-26 19:01:41 +08:00
john 441703de32 fix: stabilize page data release gate scenarios
Memind CI / Test, build, and release guards (push) Successful in 1m58s
2026-07-26 17:41:45 +08:00
john bb6b70a8c4 test: make page data delivery scenarios explicit
Memind CI / Test, build, and release guards (push) Successful in 1m52s
2026-07-26 16:17:36 +08:00
john e885310f39 fix: isolate packaged gate writable roots
Memind CI / Test, build, and release guards (push) Successful in 4m1s
2026-07-26 15:31:31 +08:00
john f1f9a1cf8f fix: ignore generated gate artifacts in cleanliness check
Memind CI / Test, build, and release guards (push) Successful in 1m59s
2026-07-26 15:20:14 +08:00
john ea07f6f250 fix: isolate artifact rebuild from release suites
Memind CI / Test, build, and release guards (push) Has been cancelled
2026-07-26 15:19:30 +08:00
john b1577a16e9 fix: harden release gate and page delivery
Memind CI / Test, build, and release guards (push) Failing after 12m3s
2026-07-26 14:32:01 +08:00
john d3141a3e91 feat(excel-analyst): restore enhanced report HTML template for excel_report
Memind CI / Test, build, and release guards (push) Successful in 4m34s
Move report generation into excel-report-html.mjs so excel_report matches the hand-enhanced layout with KPI cards, charts, and insight cards, while keeping fact-model validation in the analysis engine.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 09:58:49 +08:00
john 8ccf2db05c feat(page-data): add validation suggestions, repair path, and thinking fixes
Memind CI / Test, build, and release guards (push) Failing after 3m18s
Map Page Data failure codes to Chinese remediation hints, trigger one-shot
goosed repair for remediable cases, and recover poisoned thinking sessions.
Route local DeepSeek through the no-think proxy via host.docker.internal so
tool rounds no longer hit reasoning_content 400; document gate and case-study
scenarios for event registration repair.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 23:01:55 +08:00
john 6b6f9ed01c fix: unblock local news page generation 2026-07-25 21:55:00 +08:00
john 793e64bfbc fix: preserve page skill user input 2026-07-25 20:26:20 +08:00
john 721db19a92 feat(orchestrator): enforce Page Data validation gate 2026-07-25 14:27:07 +08:00
john 417aa9c78b feat(orchestrator): observe Page Data validation 2026-07-25 14:16:57 +08:00
john e02495a6c0 merge: integrate recent product analytics and Aider development 2026-07-25 09:51:10 +08:00
john da818bbbbc merge: record adapted Aider development branch 2026-07-25 09:50:45 +08:00
john d2cfd54d43 merge: record adapted product analytics branch 2026-07-25 09:50:42 +08:00
john 104fb4e370 feat: complete Aider Page Data review workflow 2026-07-25 09:48:12 +08:00
john 0122d89db3 feat: complete Aider Page Data review workflow 2026-07-25 09:47:25 +08:00
john be9c25f1d0 feat: add fail-closed Aider development skill 2026-07-25 09:46:55 +08:00
john 1c82b34ad9 feat: add attributable page analytics 2026-07-25 09:46:10 +08:00
john d479e89fc7 feat: track Memind product analytics 2026-07-25 09:44:26 +08:00
tkmind 258e4a8a07 Merge pull request 'Fix Phase 5 documentation whitespace CI failure' (#32) from codex/fix-phase5-doc-whitespace into main
Memind CI / Test, build, and release guards (push) Successful in 2m42s
2026-07-25 01:31:01 +00:00
john c5f84f54b9 feat: add fail-closed Aider development skill 2026-07-24 19:48:04 +08:00
john a4522854d8 feat: add attributable page analytics 2026-07-20 20:49:44 +08:00
john 175095cbf3 feat: track Memind product analytics 2026-07-20 20:19:52 +08:00
john 91026882cb fix(mindspace): do not block home on job summary 2026-07-20 11:44:57 +08:00
john 1df2489831 fix(page-data): isolate invalid workspace bindings 2026-07-20 11:39:17 +08:00
john 8438eec08c feat: add guarded image generation controls 2026-07-20 07:37:43 +08:00
john a0043e95c1 fix: complete image_make asset delivery 2026-07-19 21:28:05 +08:00
john 32e697b698 feat: expose image generation to page agents 2026-07-19 21:28:05 +08:00
john 4959535b5e feat: integrate image_make with MindSpace assets 2026-07-19 21:28:05 +08:00
473 changed files with 35284 additions and 61811 deletions
+16
View File
@@ -18,7 +18,11 @@ H5_PUBLIC_BASE_URL=http://127.0.0.1:5173
# MEMIND_ANALYTICS_URL=http://127.0.0.1:3100
# MEMIND_ANALYTICS_WEBSITE_ID=<local Umami Website ID>
# MEMIND_ANALYTICS_ID_SECRET=<local-only pseudonymization secret>
# MEMIND_ANALYTICS_IDENTITY_MODE=pseudonymous # use raw only in an approved first-party analytics environment
# MEMIND_ANALYTICS_DOMAINS=127.0.0.1,localhost
# Memind 产品壳层使用独立 Website,避免与用户生成页面的浏览口径混在一起
# MEMIND_PRODUCT_ANALYTICS_ENABLED=true
# MEMIND_PRODUCT_ANALYTICS_WEBSITE_ID=<local Umami product Website ID>
# Local Rybbit analytics (same-origin /rybbit proxy to rybbit.tkmind.cn).
# Create a Rybbit Site for localhost / 127.0.0.1, then set its numeric site_id.
@@ -74,6 +78,13 @@ H5_PUBLIC_BASE_URL=http://127.0.0.1:5173
# 真实执行交接是另一道独立闸门,默认保持关闭。
# MEMIND_ORCHESTRATOR_EXECUTION_HANDOFF_ENABLED=0
# MEMIND_ORCHESTRATOR_KILL_SWITCH=0
# Page Data 审查强制闸门(fail-closed)。默认关闭=仅观察/写建议,不改变 Native 成功语义。
# 生效前提:memindadm mode=shadow 且 MEMIND_ORCHESTRATOR_SHADOW_OBSERVATION_ENABLED=1。
# 本地/开发:保持 0。灰度强制审查再设 1。生产启用需 Ops 明确批准。
# 失败 UX:H5 会看到 codes 对应的中文纠正建议(page-data-validation-suggestions.mjs)。
# MEMIND_ORCHESTRATOR_PAGE_DATA_VALIDATION_GATE_ENABLED=0
# 可修复 codes 失败时,把纠正建议再发一轮给 goosed(每 run 最多 1 次;默认开启)。
# MEMIND_PAGE_DATA_SUGGESTION_REPAIR_ENABLED=1
# Orchestrator 终态数据保留清理默认关闭;在独立服务环境配置,建议真实 Canary 前明确周期。
# MEMIND_ORCHESTRATOR_RETENTION_DAYS=0
# MEMIND_ORCHESTRATOR_RETENTION_SWEEP_INTERVAL_MS=86400000
@@ -308,6 +319,11 @@ VITE_TKMIND_WORKING_DIR=/Users/john/PycharmProjects/tkmind
# IMAGE_MAKE_SEMANTIC_REVIEW_MAX_ATTEMPTS=3
# H5_WECHAT_MP_FRESH_PAGE_THUMBNAILS=1
# H5_WECHAT_MP_FRESH_THUMBNAIL_REPAIR=0
# 微信 Page Data 独立 Aider 审核;默认关闭,灰度名单支持 userId/username/slug/displayName/nickname 或 *。
# 该链路不创建 h5_agent_runs,审核收据写入用户工作区 .memind/page-data-reviews/。
# H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_ENABLED=0
# H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_USERS=
# H5_WECHAT_MP_PAGE_DATA_AIDER_REVIEW_TIMEOUT_MS=900000
# Sandbox MCP 调用 Portal 内部生图入口;容器内通常使用 host.docker.internal。
# MINDSPACE_AGENT_API_BASE_URL=http://127.0.0.1:8081/api
+3
View File
@@ -57,6 +57,9 @@ jobs:
- name: Run full test suite
run: npm test
- name: Verify release gate and canary routing
run: npm run test:release-gate:unit
- name: Build production frontend
run: npm run build
+1
View File
@@ -13,6 +13,7 @@ scripts/g2-aliyun-cert.env
*.log
*.pid
.local/
.release-gate/
MindSpace/
data/
-63
View File
@@ -1,63 +0,0 @@
Portal runtime artifact
This directory is meant to run on production without the source tree.
Required persisted items to inherit on the host:
.env
MindSpace/
data/
users/
.tailscale/
public/plaza-covers/
logs/
Bundled alongside server.mjs (required for sandbox-fs MCP):
mindspace-sandbox-mcp.mjs (esbuild bundle; includes schedule-service deps)
Post-deploy validation (scripts/check-mindspace-public-links.mjs):
Scans MindSpace/*/public/*.html for missing relative href/src/cover targets.
Runtime install mode: bundle-node-modules
Bundle target: node24
Platform agent skills (synced into user workspaces):
skills/
Key runtime differences must stay in .env, not in the artifact:
DATABASE_URL / MYSQL_*
H5_PUBLIC_BASE_URL
TKMIND_API_TARGETS / TKMIND_API_TARGET / TKMIND_API_TARGET_1
H5_USERS_ROOT / MINDSPACE_STORAGE_ROOT / MEMIND_SHARED_PUBLISH_ROOT
Deployment and operations transport:
H5 public domain: mm.tkmind.cn
Current public path: mm.tkmind.cn -> local nginx -> Portal :8081
Legacy rollback-only path: m.tkmind.cn -> 105 nginx -> reverse SSH tunnel -> Portal :8081
Future H5 traffic must not depend on 105 forwarding unless explicitly rolling back.
Streaming runtime operations:
node scripts/check-stream-runtime.mjs
node scripts/runtime-worker-drain.mjs status
node scripts/runtime-worker-drain.mjs reconcile
node scripts/runtime-worker-drain.mjs reconcile --apply
node scripts/runtime-worker-metrics.mjs status
node scripts/runtime-worker-metrics.mjs sample
bash scripts/install-runtime-metrics-agent.sh
node scripts/runtime-worker-heartbeat.mjs once
node scripts/runtime-worker-heartbeat.mjs serve
bash scripts/install-runtime-heartbeat-agent.sh
node scripts/runtime-slo-report.mjs
node scripts/runtime-slo-report.mjs --write-report
node scripts/runtime-slo-report.mjs --write-report --prune --retention-days 30
bash scripts/install-runtime-slo-report-agent.sh
bash scripts/install-runtime-slo-soak-agent.sh
node scripts/runtime-worker-drain.mjs drain goosed-3
node scripts/runtime-worker-drain.mjs undrain goosed-3
node scripts/check-tool-runtime.mjs
node scripts/check-agent-code-run-entry.mjs
curl -sk https://mm.tkmind.cn/api/runtime/status # includes toolRuntime.queue
node scripts/agent-run-worker.mjs --status
node scripts/agent-run-worker.mjs --once
bash scripts/install-agent-run-worker-agent.sh # installs disabled by default
node scripts/check-agent-run-worker.mjs # read-only LaunchAgent/queue check
node scripts/agent-run-guard.mjs # dry-run auto-pause guard check
node scripts/agent-run-guard.mjs --apply # stop worker and disable code-run gate when thresholds trip
bash scripts/install-agent-run-guard-agent.sh # installs guard LaunchAgent
-151
View File
@@ -1,151 +0,0 @@
import fs from 'node:fs';
import path from 'node:path';
const SKIP_REFERENCE = /^(?:https?:|mailto:|javascript:|data:|tel:|#|\/\/)/i;
const DOWNLOAD_EXTENSIONS = /\.(?:docx|doc|pdf|xlsx|xls|pptx|ppt|zip|rar|7z|csv)$/i;
export function isFilesystemPublicReference(raw) {
const value = String(raw ?? '').trim();
if (!value) return false;
if (SKIP_REFERENCE.test(value)) return false;
// /api/... and /MindSpace/... are server routes, not on-disk siblings of public HTML.
if (value.startsWith('/')) return false;
return true;
}
/** @deprecated use isFilesystemPublicReference */
export function isRelativePublicReference(raw) {
return isFilesystemPublicReference(raw);
}
export function isDownloadLikeReference(raw, source = '') {
if (!isFilesystemPublicReference(raw)) return false;
const ref = normalizeReferencePath(raw);
if (DOWNLOAD_EXTENSIONS.test(ref)) return true;
return /\bdownload\b/i.test(String(source ?? ''));
}
export function normalizeReferencePath(raw) {
const trimmed = String(raw ?? '')
.trim()
.split('#')[0]
.split('?')[0]
.trim();
if (!trimmed) return '';
try {
return decodeURIComponent(trimmed);
} catch {
return trimmed;
}
}
export function resolveReferencePath(htmlDir, raw) {
const ref = normalizeReferencePath(raw);
if (!ref) return null;
return path.resolve(htmlDir, ref);
}
function parseMindspaceCoverPaths(contentAttr) {
const paths = [];
if (!contentAttr) return paths;
let parsed = null;
try {
parsed = JSON.parse(contentAttr);
} catch {
for (const key of ['cover', 'image']) {
const match = contentAttr.match(new RegExp(`"${key}"\\s*:\\s*"([^"]+)"`));
if (match?.[1]) paths.push(match[1]);
}
return paths;
}
for (const key of ['cover', 'image']) {
if (parsed?.[key]) paths.push(String(parsed[key]));
}
return paths;
}
export function collectPublicHtmlReferences(html, htmlDir, { downloadsOnly = false } = {}) {
const refs = [];
const seen = new Set();
const add = (raw, source) => {
if (downloadsOnly && !isDownloadLikeReference(raw, source)) return;
if (!downloadsOnly && !isFilesystemPublicReference(raw)) return;
const ref = normalizeReferencePath(raw);
if (!ref) return;
const key = `${source}\0${ref}`;
if (seen.has(key)) return;
seen.add(key);
refs.push({
ref,
source,
resolvedPath: resolveReferencePath(htmlDir, ref),
});
};
for (const match of String(html ?? '').matchAll(/\b(?:href|src)\s*=\s*["']([^"']+)["']/gi)) {
add(match[1], match[0]);
}
if (!downloadsOnly) {
for (const match of String(html ?? '').matchAll(
/<meta\b[^>]*\bname\s*=\s*["']mindspace-cover["'][^>]*>/gi,
)) {
const tag = match[0];
const contentMatch =
tag.match(/\bcontent\s*=\s*"([^"]*)"/i) ?? tag.match(/\bcontent\s*=\s*'([^']*)'/i);
for (const coverPath of parseMindspaceCoverPaths(contentMatch?.[1])) {
add(coverPath, 'mindspace-cover');
}
}
}
return refs;
}
export function findMissingPublicHtmlReferences(htmlPath, html = null, options = {}) {
const resolvedHtmlPath = path.resolve(htmlPath);
const content = html ?? fs.readFileSync(resolvedHtmlPath, 'utf8');
const htmlDir = path.dirname(resolvedHtmlPath);
const missing = [];
const seen = new Set();
for (const item of collectPublicHtmlReferences(content, htmlDir, options)) {
const key = item.ref;
if (seen.has(key)) continue;
seen.add(key);
if (!item.resolvedPath || !fs.existsSync(item.resolvedPath)) {
missing.push({ htmlPath: resolvedHtmlPath, ...item });
}
}
return missing;
}
export function listPublicHtmlFiles(publishRoot, { userId = null } = {}) {
const root = path.resolve(publishRoot);
if (!fs.existsSync(root)) return [];
const userDirs = userId
? [path.join(root, userId)]
: fs.readdirSync(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path.join(root, d.name));
const files = [];
for (const userDir of userDirs) {
const publicDir = path.join(userDir, 'public');
if (!fs.existsSync(publicDir) || !fs.statSync(publicDir).isDirectory()) continue;
for (const name of fs.readdirSync(publicDir)) {
if (!name.toLowerCase().endsWith('.html')) continue;
files.push(path.join(publicDir, name));
}
}
return files.sort();
}
export function scanPublicHtmlLinks(publishRoot, options = {}) {
const issues = [];
for (const htmlPath of listPublicHtmlFiles(publishRoot, options)) {
issues.push(...findMissingPublicHtmlReferences(htmlPath, null, options));
}
return issues;
}
File diff suppressed because it is too large Load Diff
-22
View File
@@ -1,22 +0,0 @@
{
"name": "tkmind-h5-portal-runtime",
"private": true,
"type": "module",
"engines": {
"node": ">=22"
},
"dependencies": {
"@node-rs/argon2": "^2.0.2",
"@resvg/resvg-js": "^2.6.2",
"debug": "^4.4.3",
"express": "^4.21.2",
"http-proxy-middleware": "^3.0.3",
"jsonrepair": "^3.14.0",
"mysql2": "^3.22.5",
"playwright": "^1.61.1",
"qrcode": "^1.5.4",
"redis": "^4.7.1",
"sharp": "^0.35.2",
"undici": "^6.26.0"
}
}
@@ -1 +0,0 @@
nXjhx0ErC6MhQ0SZ
Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

@@ -1,50 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>微信分享预览 · 连云港三天两夜攻略 🌊</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; padding: 24px; background: #ececec; font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, sans-serif; color: #111; }
.wrap { max-width: 420px; margin: 0 auto; }
h1 { margin: 0 0 8px; font-size: 18px; }
.hint, .note { margin: 0 0 16px; color: #666; font-size: 13px; line-height: 1.6; }
.card { display: grid; grid-template-columns: 1fr 72px; gap: 12px; padding: 12px; border-radius: 8px; background: #fff; box-shadow: 0 1px 0 rgba(0,0,0,.06); }
.card-main { min-width: 0; }
.card-title { margin: 0; font-size: 16px; font-weight: 600; line-height: 1.35; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card-desc { margin: 6px 0 0; color: #888; font-size: 13px; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.card-foot { display: flex; align-items: center; gap: 6px; margin-top: 10px; color: #999; font-size: 12px; }
.card-foot img { width: 16px; height: 16px; border-radius: 999px; object-fit: cover; }
.card-thumb { width: 72px; height: 72px; border-radius: 4px; object-fit: cover; background: #f3f3f3; }
.card-thumb-empty { display: grid; place-items: center; color: #bbb; font-size: 12px; }
.meta { margin-top: 18px; padding: 14px; border-radius: 10px; background: rgba(255,255,255,.72); font-size: 12px; line-height: 1.7; word-break: break-all; }
code { background: rgba(0,0,0,.06); padding: 1px 4px; border-radius: 4px; }
</style>
</head>
<body>
<div class="wrap">
<h1>微信链接卡片预览</h1>
<p class="hint">本地模拟的是「粘贴链接后看到的卡片」,不是 JS-SDK 内部分享弹层。源链接:<code>https://m.tkmind.cn/MindSpace/john/public/lianyungang-guide.html</code></p>
<p class="note">修复后:标题 + 描述 + 缩略图 + 底部「TKMind 智趣」小图标应同时出现。</p>
<article class="card" aria-label="微信分享卡片预览">
<div class="card-main">
<h2 class="card-title">连云港三天两夜攻略 🌊</h2>
<p class="card-desc">三天两夜·沙滩酒店·必吃美食</p>
<div class="card-foot">
<img src="http://127.0.0.1:8081/brand/tkmind-icon.png" alt="" onerror="this.style.display='none'">
<span>TKMind 智趣</span>
</div>
</div>
<img class="card-thumb" src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&amp;fit=crop&amp;w=200&amp;q=80" alt="">
</article>
<div class="meta">
<div><strong>og:title</strong> 连云港三天两夜攻略 🌊</div>
<div><strong>og:description</strong> 三天两夜·沙滩酒店·必吃美食</div>
<div><strong>og:site_name</strong> TKMind 智趣</div>
<div><strong>og:image</strong> https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&amp;fit=crop&amp;w=200&amp;q=80</div>
<div><strong>icon</strong> /brand/tkmind-icon.png</div>
</div>
</div>
</body>
</html>
@@ -1,41 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>微信分享预览工具</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; min-height: 100vh; padding: 24px; background: #f3f3f3; font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, sans-serif; color: #111; }
.panel { max-width: 560px; margin: 0 auto; padding: 20px; border-radius: 16px; background: #fff; box-shadow: 0 8px 30px rgba(0,0,0,.06); }
h1 { margin: 0 0 8px; font-size: 22px; }
p { margin: 0 0 16px; color: #666; line-height: 1.6; font-size: 14px; }
label { display: block; margin-bottom: 8px; font-size: 13px; font-weight: 600; }
input { width: 100%; padding: 12px 14px; border: 1px solid #ddd; border-radius: 10px; font: inherit; }
button { margin-top: 14px; width: 100%; padding: 12px 14px; border: 0; border-radius: 10px; background: #2f6f57; color: #fff; font: inherit; font-weight: 700; cursor: pointer; }
code { background: rgba(0,0,0,.06); padding: 1px 4px; border-radius: 4px; }
</style>
</head>
<body>
<div class="panel">
<h1>微信分享预览</h1>
<p>输入已发布页面路径,本地模拟微信「粘贴链接后」看到的卡片。开发环境走 Portal API:<code>/dev/wechat-share-preview</code></p>
<form id="preview-form">
<label for="url">页面 URL 或路径</label>
<input id="url" name="url" placeholder="/MindSpace/john/public/demo.html" required>
<button type="submit">生成预览</button>
</form>
</div>
<script>
document.getElementById('preview-form').addEventListener('submit', (event) => {
event.preventDefault();
const value = document.getElementById('url').value.trim();
if (!value) return;
const target = value.startsWith('http')
? `/dev/wechat-share-preview?url=${encodeURIComponent(value)}`
: `/dev/wechat-share-preview?url=${encodeURIComponent(value.startsWith('/') ? value : `/${value}`)}`;
window.location.href = target;
});
</script>
</body>
</html>
-159
View File
@@ -1,159 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="John 的专属欢迎页面 - TKMind 为您呈现">
<meta name="mindspace-cover" content='{"tag":"报告","emoji":"👋","accent":"#f093fb","accent2":"#24243e","subtitle":"Hello John","cover":"https://images.unsplash.com/photo-1579547945413-497e1b99dac0?w=1200&q=80"}'>
<title>Hello, John!</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #0f0c29, #302b63, #24243e);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
}
/* 动态粒子背景 */
.bg-particles {
position: fixed;
inset: 0;
z-index: 0;
overflow: hidden;
}
.bg-particles span {
position: absolute;
width: 6px;
height: 6px;
background: rgba(255, 255, 255, 0.15);
border-radius: 50%;
animation: float 20s infinite;
}
.bg-particles span:nth-child(1) { top: 10%; left: 10%; animation-duration: 18s; animation-delay: 0s; }
.bg-particles span:nth-child(2) { top: 30%; left: 80%; width: 10px; height: 10px; animation-duration: 22s; animation-delay: 2s; }
.bg-particles span:nth-child(3) { top: 70%; left: 20%; animation-duration: 16s; animation-delay: 4s; }
.bg-particles span:nth-child(4) { top: 85%; left: 85%; width: 8px; height: 8px; animation-duration: 24s; animation-delay: 1s; }
.bg-particles span:nth-child(5) { top: 50%; left: 50%; width: 4px; height: 4px; animation-duration: 20s; animation-delay: 3s; }
.bg-particles span:nth-child(6) { top: 15%; left: 60%; animation-duration: 19s; animation-delay: 5s; }
.bg-particles span:nth-child(7) { top: 60%; left: 5%; width: 7px; height: 7px; animation-duration: 21s; animation-delay: 0.5s; }
.bg-particles span:nth-child(8) { top: 40%; left: 70%; width: 5px; height: 5px; animation-duration: 17s; animation-delay: 2.5s; }
@keyframes float {
0% { transform: translateY(0) rotate(0deg); opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { transform: translateY(-100vh) rotate(720deg); opacity: 0; }
}
.card {
position: relative;
z-index: 1;
text-align: center;
padding: 60px 80px;
background: rgba(255, 255, 255, 0.05);
border-radius: 24px;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.3),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
animation: fadeIn 1.2s ease-out;
}
h1 {
font-size: 72px;
font-weight: 700;
background: linear-gradient(135deg, #f093fb, #f5576c, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 16px;
animation: pulse 3s ease-in-out infinite;
letter-spacing: 4px;
}
p {
font-size: 24px;
color: rgba(255, 255, 255, 0.7);
letter-spacing: 2px;
}
.emoji {
font-size: 48px;
display: block;
margin-bottom: 24px;
animation: wave 2s ease-in-out infinite;
}
.footer {
margin-top: 36px;
font-size: 13px;
color: rgba(255, 255, 255, 0.3);
letter-spacing: 1px;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(30px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}
@keyframes wave {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(15deg); }
75% { transform: rotate(-15deg); }
}
@media (max-width: 600px) {
.card {
padding: 40px 32px;
margin: 20px;
border-radius: 16px;
}
h1 {
font-size: 48px;
}
p {
font-size: 18px;
}
.emoji {
font-size: 36px;
}
}
</style>
</head>
<body>
<div class="bg-particles">
<span></span><span></span><span></span><span></span>
<span></span><span></span><span></span><span></span>
</div>
<div class="card">
<span class="emoji">👋</span>
<h1>Hello!</h1>
<p>Welcome, John</p>
<div class="footer">powered by TKMind</div>
</div>
</body>
</html>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 723 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 334 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 613 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Some files were not shown because too many files have changed in this diff Show More