617ff0d1dd
Memind CI / Test, build, and release guards (push) Has been cancelled
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>
174 lines
6.0 KiB
JavaScript
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);
|
|
});
|