Add MindSpace page live edit, chat skills, and H5 deploy tooling.

Introduce page edit sessions with draft preview and patch API, chat skill picker, user memory profile, h5ApiBase resolution, voice WAV transport, and scripts for 105/g2 deployment and Plaza local dev.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
John
2026-06-15 22:09:38 -07:00
parent 3cd322ccfe
commit 6ee6fd64dd
94 changed files with 7015 additions and 2136 deletions
+37 -16
View File
@@ -8,7 +8,11 @@ const OAUTH_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo';
const STATE_TTL_MS = 10 * 60 * 1000;
const VALID_INTENTS = new Set(['login', 'register', 'bind']);
const VALID_AUTH_MODES = new Set(['mp', 'open']);
const VALID_AUTH_MODES = new Set(['mp', 'open', 'scan']);
function isPollScanAuthMode(authMode) {
return authMode === 'open' || authMode === 'scan';
}
function resolveCallbackUrl(env = process.env) {
const explicit = env.H5_WECHAT_OAUTH_CALLBACK_URL?.trim() ?? '';
@@ -71,6 +75,7 @@ function normalizeAuthMode(authMode) {
function buildAuthorizeUrl(config, { state, authMode = 'mp' }) {
const isOpen = authMode === 'open';
const useMp = authMode === 'mp' || authMode === 'scan';
const params = new URLSearchParams({
appid: isOpen ? config.openAppId : config.appId,
redirect_uri: config.callbackUrl,
@@ -238,21 +243,34 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
};
const startScanLogin = async (req) => {
if (!config.scanEnabled) {
throw new Error('微信扫码登录未配置');
}
const returnTo = sanitizeWechatReturnTo(req.query?.return_to, req);
const state = await createState({
const utmSource = typeof req.query?.utm_source === 'string' ? req.query.utm_source : null;
const utmMedium = typeof req.query?.utm_medium === 'string' ? req.query.utm_medium : null;
const utmCampaign = typeof req.query?.utm_campaign === 'string' ? req.query.utm_campaign : null;
const common = {
returnTo,
utmSource: typeof req.query?.utm_source === 'string' ? req.query.utm_source : null,
utmMedium: typeof req.query?.utm_medium === 'string' ? req.query.utm_medium : null,
utmCampaign: typeof req.query?.utm_campaign === 'string' ? req.query.utm_campaign : null,
utmSource,
utmMedium,
utmCampaign,
intent: 'login',
authMode: 'open',
});
};
if (config.scanEnabled) {
const state = await createState({ ...common, authMode: 'open' });
return {
mode: 'open',
state,
openAppId: config.openAppId,
redirectUri: config.callbackUrl,
expiresInMs: STATE_TTL_MS,
};
}
const state = await createState({ ...common, authMode: 'scan' });
return {
mode: 'mp',
state,
qrUrl: buildAuthorizeUrl(config, { state, authMode: 'open' }),
qrUrl: buildAuthorizeUrl(config, { state, authMode: 'scan' }),
expiresInMs: STATE_TTL_MS,
};
};
@@ -289,7 +307,7 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
let avatarUrl = null;
let unionid = tokenInfo.unionid;
if (authMode === 'mp' && config.scope === 'snsapi_userinfo') {
if ((authMode === 'mp' || authMode === 'scan') && config.scope === 'snsapi_userinfo') {
const profile = await fetchUserInfo(tokenInfo.accessToken, tokenInfo.openid);
nickname = profile.nickname;
avatarUrl = profile.avatarUrl;
@@ -325,7 +343,7 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
ip,
});
if (!result.ok) {
if (authMode === 'open') {
if (isPollScanAuthMode(authMode)) {
await markStateComplete(state, {
resultKind: 'error',
resultMessage: result.message || '微信登录失败',
@@ -340,7 +358,7 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
}
if (result.action === 'binding_gate') {
if (authMode === 'open') {
if (isPollScanAuthMode(authMode)) {
await markStateComplete(state, {
resultKind: 'binding_gate',
resultToken: result.pendingToken,
@@ -365,7 +383,7 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
};
}
if (authMode === 'open') {
if (isPollScanAuthMode(authMode)) {
await markStateComplete(state, {
resultKind: 'login',
resultToken: result.token,
@@ -405,8 +423,11 @@ export function createWechatOAuthService(pool, config, { userAuth } = {}) {
return {
enabled: true,
inWechat,
scanEnabled: Boolean(config.scanEnabled),
scanEnabled: true,
openScanEnabled: Boolean(config.scanEnabled),
appId: config.appId,
openAppId: config.scanEnabled ? config.openAppId : undefined,
oauthCallbackUrl: config.scanEnabled ? config.callbackUrl : undefined,
};
},
buildAuthorizeRedirect,