"""Recheck the bundled fictional exports with a local copy of Tera's parser. Requires Python 3.9+ and Node 20+. Does not fetch code or upload any data. """ import argparse import datetime as dt import hashlib import json import os from pathlib import Path import subprocess p = argparse.ArgumentParser() p.add_argument('--source', required=True, type=Path, help='Local chat-tools.mjs file') args = p.parse_args() source = args.source.resolve() root = Path(__file__).resolve().parent runner = "\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport {pathToFileURL} from 'node:url';\nconst root = process.argv[1];\nconst source = process.argv[2];\nconst {chatsFromFiles, analyze, inYear} = await import(pathToFileURL(source));\nconst {cases} = JSON.parse(fs.readFileSync(path.join(root,'manifest.json'),'utf8'));\nconst results=[];\nfor (const c of cases) {\n const files = c.files.map(name=>new File([fs.readFileSync(path.join(root,name))],name));\n const chats = await chatsFromFiles(files);\n const messages = await chats[0].load();\n const a = analyze(messages);\n const actual = {total:a.total,people:Object.fromEntries(a.people.map(p=>[p.name,p.messages])),\n years:Object.fromEntries(Object.keys(c.expected.years).map(y=>[y,inYear(messages,Number(y)).length])),\n first:{t:new Date(messages[0].t).toISOString(),name:messages[0].from,text:messages[0].text},\n last:{t:new Date(messages.at(-1).t).toISOString(),name:messages.at(-1).from,text:messages.at(-1).text},\n multiline_messages:messages.filter(m=>m.text.includes('\\n')).length};\n const errors=[];\n for(const k of ['total','multiline_messages']) if(actual[k]!==c.expected[k]) errors.push(k);\n for(const k of ['people','years']) if(JSON.stringify(Object.entries(actual[k]).sort())!==JSON.stringify(Object.entries(c.expected[k]).sort())) errors.push(k);\n for(const k of ['first','last']) {\n if(Date.parse(actual[k].t)!==Date.parse(c.expected[k].t)||actual[k].name!==c.expected[k].name||actual[k].text!==c.expected[k].text) errors.push(k);\n }\n results.push({id:c.id,passed:errors.length===0,errors,actual});\n}\nconsole.log(JSON.stringify({results},null,2));\n" run = subprocess.run(['node', '--input-type=module', '-e', runner, str(root), str(source)], check=True, capture_output=True, text=True, env={**os.environ, 'TZ': 'UTC'}) result = json.loads(run.stdout) result.update({'tested_at_utc': dt.datetime.now(dt.timezone.utc).isoformat(), 'source_sha256': hashlib.sha256(source.read_bytes()).hexdigest(), 'execution': 'Node using public parser and analyzer, not browser UI', 'competitor_results': 'Not run; no comparative conclusion'}) print(json.dumps(result, ensure_ascii=False, indent=2)) raise SystemExit(0 if all(r['passed'] for r in result['results']) else 1)