Files
memind/scripts/verify-tang-cursor-channel-103.mjs
T
john 617ff0d1dd
Memind CI / Test, build, and release guards (push) Has been cancelled
Add per-feature Cursor channel toggles for admin and runtime.
Expose page/data/scheduled-task/chat-bridge switches in the智趣体验通道 config so Tang can roll out Cursor paths independently with DeepSeek fallback.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-31 09:40:52 +08:00

174 lines
6.0 KiB
JavaScript

#!/usr/bin/env node
/**
* 唐用户智趣通道 103 只读巡检 + 推荐配置输出
*
* Usage:
* node scripts/verify-tang-cursor-channel-103.mjs
* node scripts/verify-tang-cursor-channel-103.mjs --print-recommended-patch
*/
import process from 'node:process';
import { execSync } from 'node:child_process';
import mysql from 'mysql2/promise';
import {
CURSOR_CHANNEL_FEATURES,
defaultCursorChannelFeatures,
normalizeCursorChannelFeatures,
} from '../cursor-channel-features.mjs';
const TANG = 'a70ff537-8908-486e-9b6c-042e07cc25db';
const TANG_USERNAME = 'wx_ul610et8';
const RECOMMENDED_FEATURES = {
...defaultCursorChannelFeatures(),
[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE]: { enabled: true },
[CURSOR_CHANNEL_FEATURES.PAGE_DATA]: { enabled: true },
[CURSOR_CHANNEL_FEATURES.EXCEL_ANALYSIS]: { enabled: false },
[CURSOR_CHANNEL_FEATURES.CHAT_BRIDGE]: { enabled: false },
[CURSOR_CHANNEL_FEATURES.SCHEDULED_TASKS]: { enabled: true },
};
const args = process.argv.slice(2);
const printPatch = args.includes('--print-recommended-patch');
function pass(label, detail = '') {
console.log(`${label}${detail ? `: ${detail}` : ''}`);
}
function warn(label, detail = '') {
console.warn(`${label}${detail ? `: ${detail}` : ''}`);
}
function fail(label, detail = '') {
console.error(`${label}${detail ? `: ${detail}` : ''}`);
}
async function loadProdDatabaseUrl() {
return execSync(
"ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 \"grep '^DATABASE_URL=' /Users/john/Project/Memind/.env | cut -d= -f2-\"",
{ encoding: 'utf8' },
).trim();
}
async function read103Env(pattern) {
try {
const out = execSync(
`ssh -o BatchMode=yes -o ConnectTimeout=8 john@58.38.22.103 'grep -E "${pattern}" /Users/john/Project/Memind/.env || true'`,
{ encoding: 'utf8' },
).trim();
return out.split('\n').filter(Boolean);
} catch {
return [];
}
}
async function main() {
let failed = 0;
const databaseUrl = await loadProdDatabaseUrl();
const pool = mysql.createPool({ uri: databaseUrl, connectionLimit: 2 });
const [users] = await pool.query(
'SELECT id, username, display_name FROM h5_users WHERE id = ? LIMIT 1',
[TANG],
);
const user = users[0];
if (user?.username === TANG_USERNAME) pass('唐用户身份', `${user.display_name} / ${user.username}`);
else { fail('唐用户身份'); failed += 1; }
const envLines = await read103Env('^(MEMIND_CURSOR|MEMIND_CURSOR_CHAT_BRIDGE|H5_SCHEDULED_TASK)');
const envMap = Object.fromEntries(
envLines.map((line) => {
const idx = line.indexOf('=');
return [line.slice(0, idx), line.slice(idx + 1)];
}),
);
if (envMap.MEMIND_CURSOR_EXECUTOR_ENABLED === '1') pass('Cursor 基础设施', 'MEMIND_CURSOR_EXECUTOR_ENABLED=1');
else { fail('Cursor 基础设施', '未开启 MEMIND_CURSOR_EXECUTOR_ENABLED'); failed += 1; }
if (envMap.MEMIND_CURSOR_CHAT_BRIDGE_ENABLED === '1') {
pass('Chat Bridge 基础设施', '已配置');
} else {
warn('Chat Bridge 基础设施', '未开 MEMIND_CURSOR_CHAT_BRIDGE_ENABLED;后台 chatBridge 开关暂不会生效');
}
if (envMap.H5_SCHEDULED_TASK_WORKER_ENABLED === '1') pass('定时任务 Worker', '已开启');
else { warn('定时任务 Worker', 'H5_SCHEDULED_TASK_WORKER_ENABLED 未开'); }
const [cursorRows] = await pool.query(
'SELECT config_json FROM h5_wechat_cursor_executor_config WHERE config_scope = ? LIMIT 1',
['global'],
);
const stored = cursorRows[0]?.config_json ?? null;
const parsed = typeof stored === 'string' ? JSON.parse(stored) : (stored ?? {});
const features = normalizeCursorChannelFeatures(parsed.features, {
intentAllowlist: parsed.intentAllowlist,
});
if (parsed.enabled) pass('智趣总开关', 'enabled');
else { warn('智趣总开关', '当前关闭'); }
const allowlist = Array.isArray(parsed.userAllowlist) ? parsed.userAllowlist : [];
if (allowlist.map((v) => v.toLowerCase()).includes(TANG.toLowerCase())) {
pass('唐在白名单', `${allowlist.length}`);
} else {
fail('唐在白名单', `当前: ${allowlist.join(', ') || '(空)'}`);
failed += 1;
}
for (const [key, label] of [
[CURSOR_CHANNEL_FEATURES.PAGE_GENERATE, '页面生成'],
[CURSOR_CHANNEL_FEATURES.PAGE_DATA, '问卷 Page Data'],
[CURSOR_CHANNEL_FEATURES.SCHEDULED_TASKS, '定时任务 Cursor'],
[CURSOR_CHANNEL_FEATURES.CHAT_BRIDGE, '普通聊天 Bridge'],
]) {
const on = features[key]?.enabled === true;
const want = RECOMMENDED_FEATURES[key]?.enabled === true;
if (on === want) pass(`能力 ${label}`, on ? '已开' : '已关(符合推荐)');
else warn(`能力 ${label}`, `当前=${on ? '开' : '关'},推荐=${want ? '开' : '关'}`);
}
const [activeTasks] = await pool.query(
`SELECT COUNT(*) AS c FROM h5_scheduled_tasks WHERE user_id = ? AND status IN ('active','locked')`,
[TANG],
);
const activeCount = Number(activeTasks[0]?.c ?? 0);
if (activeCount > 0) pass('active 定时任务', String(activeCount));
else warn('active 定时任务', '0 — 需重建新闻/天气任务后才能验证 scheduledTasks');
const [failedTasks] = await pool.query(
`SELECT title, last_error FROM h5_scheduled_tasks
WHERE user_id = ? AND status = 'failed'
ORDER BY updated_at DESC LIMIT 5`,
[TANG],
);
if (failedTasks.length) {
console.log('\n--- 最近 failed 定时任务 ---');
for (const row of failedTasks) {
console.log(`- ${row.title}: ${String(row.last_error ?? '').slice(0, 120)}`);
}
}
const recommendedPatch = {
enabled: true,
userAllowlist: [TANG],
channelAllowlist: ['h5', 'wechat_mp'],
features: RECOMMENDED_FEATURES,
fallbackToDeepseek: true,
};
if (printPatch) {
console.log('\n--- 推荐 memind_adm 保存 payload ---');
console.log(JSON.stringify(recommendedPatch, null, 2));
} else {
console.log('\n提示: node scripts/verify-tang-cursor-channel-103.mjs --print-recommended-patch');
}
await pool.end();
process.exit(failed > 0 ? 1 : 0);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});