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:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
Executable
+28
@@ -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"
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user