fix(wechat): deliver failure notices and hot-swappable wechat-mp bundle

Notify users when HTML publish guards reject stub pages instead of silently
failing, send real page links when non-stub files exist, and split wechat-mp
into wechat-mp.bundle.mjs for fast production updates without full portal rebuilds.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-04 12:52:59 +08:00
parent 5ed655b6c3
commit 6bc5d128d2
9 changed files with 327 additions and 116 deletions
+7
View File
@@ -369,6 +369,7 @@ async function writeMetadata() {
'',
'Bundled alongside server.mjs (required for sandbox-fs MCP):',
' mindspace-sandbox-mcp.mjs (esbuild bundle; includes schedule-service deps)',
' wechat-mp.bundle.mjs (esbuild bundle; hot-swappable WeChat MP module)',
'',
'Post-deploy validation (scripts/check-mindspace-public-links.mjs):',
' Scans MindSpace/*/public/*.html for missing relative href/src/cover targets.',
@@ -423,10 +424,16 @@ async function writeMetadata() {
);
}
async function bundleWechatMp() {
console.log('==> 打包 WeChat MP runtime bundle');
await run('node', ['scripts/build-wechat-mp-runtime.mjs']);
}
async function main() {
await buildFrontend();
await copyRuntimeAssets();
await bundleServer();
await bundleWechatMp();
await bundleSandboxMcp();
await bundleAgentRunWorker();
if (runtimeInstallMode === 'bundle-node-modules' && !skipNodeModules) {
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env node
import fs from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawn } from 'node:child_process';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const root = path.join(__dirname, '..');
const runtimeRoot = path.join(root, '.runtime', 'portal');
const esbuildBin = path.join(root, 'node_modules', '.pnpm', 'node_modules', '.bin', 'esbuild');
const runtimeNodeTarget = process.env.PORTAL_RUNTIME_NODE_TARGET || 'node24';
const outputArg = process.argv.find((arg) => arg.startsWith('--outfile='));
const outfile = outputArg
? outputArg.slice('--outfile='.length)
: path.join(runtimeRoot, 'wechat-mp.bundle.mjs');
const externalPackages = [
'undici',
'mysql2',
'mysql2/promise',
'sharp',
'fsevents',
];
function run(command, args) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, { cwd: root, stdio: 'inherit', env: process.env });
child.on('exit', (code) => {
if (code === 0) resolve();
else reject(new Error(`${command} ${args.join(' ')} exited with code ${code ?? 1}`));
});
child.on('error', reject);
});
}
async function main() {
await fs.mkdir(path.dirname(outfile), { recursive: true });
const args = [
'wechat-mp.mjs',
'--bundle',
'--platform=node',
'--format=esm',
`--target=${runtimeNodeTarget}`,
`--outfile=${outfile}`,
'--banner:js=import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);',
];
for (const pkg of externalPackages) {
args.push(`--external:${pkg}`);
}
await run(esbuildBin, args);
console.log(`WeChat MP runtime bundle written: ${outfile}`);
}
main().catch((err) => {
console.error(err instanceof Error ? err.message : err);
process.exit(1);
});
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
HOST="${MEMIND_PROD_HOST:-john@58.38.22.103}"
REMOTE_ROOT="${MEMIND_PROD_ROOT:-/Users/john/Project/Memind}"
BUNDLE=".runtime/portal/wechat-mp.bundle.mjs"
echo "[deploy-wechat-mp] build bundle"
node scripts/build-wechat-mp-runtime.mjs
if [[ ! -f "${BUNDLE}" ]]; then
echo "missing bundle: ${BUNDLE}" >&2
exit 1
fi
echo "[deploy-wechat-mp] upload ${BUNDLE} -> ${HOST}:${REMOTE_ROOT}/wechat-mp.bundle.mjs"
scp "${BUNDLE}" "${HOST}:${REMOTE_ROOT}/wechat-mp.bundle.mjs"
echo "[deploy-wechat-mp] restart portal"
ssh "${HOST}" 'launchctl kickstart -k "gui/$(id -u)/cn.tkmind.memind-portal"'
echo "[deploy-wechat-mp] health check"
ssh "${HOST}" 'curl -s -o /dev/null -w "portal=%{http_code}\n" http://127.0.0.1:8081/api/status'
echo "[deploy-wechat-mp] done"
+1 -1
View File
@@ -151,7 +151,7 @@ fi
verify_runtime_artifact() {
local missing=0
for required in server.mjs mindspace-sandbox-mcp.mjs mindspace-public-links.mjs dist package.json scripts/run-memind-portal-prod.sh scripts/check-mindspace-public-links.mjs scripts/load-env.mjs scripts/wechat-mp-menu.mjs scripts/memind-portal-tunnel.sh; do
for required in server.mjs wechat-mp.bundle.mjs mindspace-sandbox-mcp.mjs mindspace-public-links.mjs dist package.json scripts/run-memind-portal-prod.sh scripts/check-mindspace-public-links.mjs scripts/load-env.mjs scripts/wechat-mp-menu.mjs scripts/memind-portal-tunnel.sh; do
if [[ ! -e "${RUNTIME_ROOT}/${required}" ]]; then
echo "runtime 产物缺失: ${RUNTIME_ROOT}/${required}" >&2
missing=1