Files
memind/scripts/deploy-tang-meinput-pages-103.mjs
T
john be464a5b8d
Memind CI / Test, build, and release guards (push) Has been cancelled
Add Rain V0 for MeInput full-range chat analysis and delivery tooling.
Introduce rain-service orchestration, browser-safe chat skill filtering, MeInput
adapter helpers, and verify/deploy scripts so Rain mode can summarize recent input
without Memory V2 pollution.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-04 13:49:40 +08:00

177 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Deploy MeInput tutorial + case pages to 103 production under 唐 user.
* Usage: node scripts/deploy-tang-meinput-pages-103.mjs [--send-wechat]
*/
import { execSync } from 'node:child_process';
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
const HOST = 'john@58.38.22.103';
const REMOTE_ROOT = '/Users/john/Project/Memind';
const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db';
const JOHN_LOCAL = '1c99b83b-0454-474f-a5d2-129d34506a32';
const FILES = [
'meinput-tkmind-portrait-tutorial.html',
'meinput-tkmind-portrait-tutorial-wechat.html',
'behavior-pattern-analysis.html',
];
const PUBLIC_BASE = `https://m.tkmind.cn/MindSpace/${TANG}/public`;
const NODE103 = '/opt/homebrew/opt/node@24/bin/node';
const sendWechat = process.argv.includes('--send-wechat');
function sh(cmd) {
execSync(cmd, { stdio: 'inherit' });
}
const remoteScript = `
import crypto from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import mysql from 'mysql2/promise';
const TANG = '${TANG}';
const REMOTE_ROOT = '${REMOTE_ROOT}';
const FILES = ${JSON.stringify(FILES)};
const REQUEST_ID = 'deploy-meinput-tutorial-20260904';
const PUBLIC_BASE = '${PUBLIC_BASE}';
process.loadEnvFile(path.join(REMOTE_ROOT, '.env'));
const pool = mysql.createPool({ uri: process.env.DATABASE_URL, connectionLimit: 2 });
async function ensureReady(relativePath) {
const now = Date.now();
const id = crypto.randomUUID();
await pool.query(
\`INSERT INTO h5_page_delivery_contracts
(id, user_id, request_id, workspace_relative_path, data_mode, status, ready_at, created_at, updated_at)
VALUES (?, ?, ?, ?, 'static', 'ready', ?, ?, ?)
ON DUPLICATE KEY UPDATE status = 'ready', ready_at = VALUES(ready_at), failure_reason = NULL, updated_at = VALUES(updated_at)\`,
[id, TANG, REQUEST_ID, relativePath, now, now, now],
);
}
async function main() {
const results = [];
for (const name of FILES) {
const relativePath = 'public/' + name;
const abs = path.join(REMOTE_ROOT, 'MindSpace', TANG, relativePath);
const exists = fs.existsSync(abs);
const size = exists ? fs.statSync(abs).size : 0;
if (exists) await ensureReady(relativePath);
results.push({ file: name, exists, size, url: PUBLIC_BASE + '/' + name });
}
console.log(JSON.stringify({ ok: true, pages: results }, null, 2));
await pool.end();
}
main().catch((e) => { console.error(e); process.exit(1); });
`.trim();
async function sendWechatLinks() {
const remoteWechat = `
import path from 'node:path';
import mysql from 'mysql2/promise';
const TANG = '${TANG}';
const PUBLIC_BASE = '${PUBLIC_BASE}';
const FILES = ${JSON.stringify(FILES)};
process.loadEnvFile('${REMOTE_ROOT}/.env');
const pool = mysql.createPool({ uri: process.env.DATABASE_URL, connectionLimit: 2 });
const appId = process.env.H5_WECHAT_MP_APP_ID ?? process.env.WECHAT_MP_APP_ID;
const appSecret = process.env.H5_WECHAT_MP_APP_SECRET ?? process.env.WECHAT_MP_APP_SECRET;
if (!appId || !appSecret) throw new Error('missing wechat credentials');
const [ident] = await pool.query(
'SELECT openid FROM h5_user_wechat_identities WHERE user_id = ? AND app_id = ? LIMIT 1',
[TANG, appId],
);
const openid = ident?.[0]?.openid;
if (!openid) throw new Error('tang not bound to wechat');
const tokenRes = await fetch('https://api.weixin.qq.com/cgi-bin/stable_token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ grant_type: 'client_credential', appid: appId, secret: appSecret }),
});
const tokenPayload = await tokenRes.json();
if (!tokenPayload.access_token) throw new Error(JSON.stringify(tokenPayload));
const lines = [
'MeInput × TKMind 教程与案例已发布:',
'',
'📱 教程(公众号发布版)',
PUBLIC_BASE + '/meinput-tkmind-portrait-tutorial-wechat.html',
'',
'📖 教程(网页阅读版)',
PUBLIC_BASE + '/meinput-tkmind-portrait-tutorial.html',
'',
'🧭 案例:用户A 全景画像',
PUBLIC_BASE + '/behavior-pattern-analysis.html',
];
const text = lines.join('\\n').slice(0, 2048);
const sendRes = await fetch('https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + encodeURIComponent(tokenPayload.access_token), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ touser: openid, msgtype: 'text', text: { content: text } }),
});
const sendPayload = await sendRes.json();
if (Number(sendPayload.errcode ?? 0) !== 0) throw new Error(JSON.stringify(sendPayload));
console.log(JSON.stringify({ ok: true, sent: true, openid: openid.slice(0, 8) + '...' }));
await pool.end();
`.trim();
const tmp = `${REMOTE_ROOT}/.tmp-tang-meinput-wechat.mjs`;
fs.writeFileSync(path.join(root, '.tmp-tang-wechat.mjs'), remoteWechat);
sh(`scp -q ${path.join(root, '.tmp-tang-wechat.mjs')} ${HOST}:${tmp}`);
sh(`ssh -o BatchMode=yes ${HOST} 'cd ${REMOTE_ROOT} && ${NODE103} ${tmp} && rm -f ${tmp}'`);
fs.unlinkSync(path.join(root, '.tmp-tang-wechat.mjs'));
}
async function main() {
const localDir = path.join(root, 'MindSpace', JOHN_LOCAL, 'public');
for (const f of FILES) {
const src = path.join(localDir, f);
if (!fs.existsSync(src)) throw new Error(`missing local file: ${src}`);
}
const remotePublic = `${REMOTE_ROOT}/MindSpace/${TANG}/public`;
sh(`ssh -o BatchMode=yes ${HOST} 'mkdir -p ${remotePublic}'`);
for (const f of FILES) {
sh(`scp -q ${path.join(localDir, f)} ${HOST}:${remotePublic}/${f}`);
}
const tmpLocal = path.join(root, '.tmp-tang-deploy-103.mjs');
fs.writeFileSync(tmpLocal, remoteScript);
const tmpRemote = `${REMOTE_ROOT}/.tmp-tang-meinput-deploy.mjs`;
sh(`scp -q ${tmpLocal} ${HOST}:${tmpRemote}`);
sh(`ssh -o BatchMode=yes ${HOST} 'cd ${REMOTE_ROOT} && ${NODE103} ${tmpRemote} && rm -f ${tmpRemote}'`);
fs.unlinkSync(tmpLocal);
console.log('\n=== Production URLs ===');
for (const f of FILES) console.log(`${PUBLIC_BASE}/${f}`);
for (const f of FILES) {
const code = execSync(
`curl -sS -o /dev/null -w '%{http_code}' 'https://m.tkmind.cn/MindSpace/${TANG}/public/${f}'`,
{ encoding: 'utf8' },
).trim();
console.log(`${f}: HTTP ${code}`);
}
if (sendWechat) {
await sendWechatLinks();
}
}
main().catch((e) => {
console.error(e);
process.exit(1);
});