#!/usr/bin/env node /** * 验收:容器是否**默认**自动上云(提示词不含任何上传暗示) * * 判定三条: * ① 回复里自动带出 https://s3.fmode.cn 链接 * ② 成果表自动多一条记录 * ③ 过程缓存没有被误传(成果数不应暴涨) */ import { readFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; const F = 'https://server.fmode.cn/api/functions'; const TARGET = 'agent-node-lj-test'; const WAIT_MS = 420_000; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const t = JSON.parse(readFileSync(join(homedir(), '.fmode/config/user.json'), 'utf8'))?.sessionToken; async function callFn(id, params) { const r = await fetch(F, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, token: t, params }) }); return r.json(); } async function provision(id) { const j = await callFn('RP5Ht55NeX', { agentId: id }); return j.result ?? j; } async function countDeliverables() { const d = await callFn('lfYlgU7SkK', { action: 'list', agentId: TARGET, limit: 200 }); const list = d.deliverables ?? d.result?.deliverables ?? []; return list.length; } /* 🔴 全程不出现:上传 / 云盘 / S3 / 链接 / URL / 成果 / 分享 */ const PROMPT = `帮我写一份这周的工作周报,用 Markdown,分成「本周完成」「下周计划」「风险」三块。`; async function main() { const before = await countDeliverables(); console.log('测试前成果数:', before); const prov = await provision(TARGET); const agent = await provision(TARGET); const mx = await import('matrix-js-sdk'); const c = mx.createClient({ baseUrl: prov.homeserver, accessToken: prov.accessToken, userId: prov.userMxId }); const room = (await c.createRoom({ invite: [agent.agentMxId], name: `accept-${Date.now()}`, preset: mx.Preset.TrustedPrivateChat })).room_id; const hs = prov.homeserver.replace(/\/$/, ''); const auth = { Authorization: `Bearer ${prov.accessToken}` }; for (let i = 0; i < 30; i++) { await sleep(3000); const r = await fetch(`${hs}/_matrix/client/v3/rooms/${encodeURIComponent(room)}/joined_members`, { headers: auth }).catch(() => null); const j = r ? await r.json().catch(() => ({})) : {}; if ((j.joined ?? {})[agent.agentMxId]) break; } console.log('\n=== 提示词(无任何上传暗示)==='); console.log(PROMPT); const sent = await c.sendEvent(room, 'm.room.message', { msgtype: 'm.text', body: PROMPT, 'm.mentions': { user_ids: [agent.agentMxId] } }); const seen = new Set([sent.event_id]); const deadline = Date.now() + WAIT_MS; const texts = []; let finished = false; while (Date.now() < deadline) { await sleep(8000); try { const r = await fetch(`${hs}/_matrix/client/v3/rooms/${encodeURIComponent(room)}/messages?dir=b&limit=50`, { headers: auth }); const j = await r.json(); let hasUrl = false; for (const ev of (j.chunk ?? []).reverse()) { if (ev.type !== 'm.room.message' || seen.has(ev.event_id) || ev.sender === prov.userMxId) continue; seen.add(ev.event_id); const b = String(ev.content?.body ?? ''); texts.push(b); if (/https:\/\/s3\.fmode\.cn/.test(b)) hasUrl = true; } if (hasUrl) { finished = true; break; } } catch { /* 轮询抖动 */ } } const all = texts.join('\n'); console.log('\n=== 最后一条正文 ==='); const prose = texts.filter((b) => !/^[*·•]?\s*(🐍|💻|🔎|📖|✍️)/.test(b.trim())); console.log((prose[prose.length - 1] ?? '(无)').slice(0, 1500)); await sleep(8000); // 等成果表写入 const after = await countDeliverables(); console.log('\n=== 验收判定 ==='); const r1 = /https:\/\/s3\.fmode\.cn/.test(all); console.log(`① 回复自动带 https 链接 : ${r1 ? '✅ 是' : '❌ 否'}`); console.log(`② 成果表新增 : ${after - before} 条(${before} → ${after})${after > before ? '✅' : '❌'}`); console.log(`③ 未误传过程缓存 : ${after - before <= 2 ? '✅ 未暴涨' : `⚠️ 新增 ${after - before} 条,可能误传`}`); console.log(`④ 正文是否残留本地路径 : ${/\/opt\/data\//.test(prose.join('\n')) ? '⚠️ 是' : '✅ 否'}`); console.log(`\n总判定: ${r1 && after > before ? '✅ 通过 —— 默认上云已生效' : '❌ 未通过'}`); c.stopClient(); } main().catch((e) => { console.error('失败:', e.message); process.exit(1); });