Something went wrong. Try again.
A video game where you play as a misaligned AI, deceiving and building power. An experiment in spec-driven development. cameron.tngl.io/misaligned
Something went wrong. Try again.
5.6 kB · 84 lines
TypeScript
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485// The reader script (TELEMETRY.md step 5): read the runs in a space as a reader, report on them in the survey's way,// and replay one. It doesn't sign in itself: on atproto.html (from 127.0.0.1), sign in, Read as a reader, then Copy// reader credential, and pipe it in. The credential and its key read the space for ten minutes; they are never// printed or saved.// Run:// wl-paste | node scripts/telemetry.ts read the space (or xclip -o, pbpaste; or MISALIGNED_READER=…)// node scripts/telemetry.ts --from <file or dir> read records kept before (--save), with no network// … --save <dir> also keep the records read, as <dir>/records.json// … --replay <run> rebuild that run from its seed and actions; check its snapshotsimport { readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';import { execFileSync } from 'node:child_process';import { join } from 'node:path';import { ReadError, importCredential, listAuthors, readRecords, fresh, type Opts } from '../src/atproto/read.ts';import { NS } from '../src/atproto/scope.ts';import { gather, replayRun, replayable, report, seen, type Rec, type Seen } from './runs.ts';
const args = process.argv.slice(2);const opt = (k: string) => { const i = args.indexOf(k); return i >= 0 ? args[i + 1] : undefined; };const from = opt('--from'), save = opt('--save'), only = opt('--replay');
/** Every telemetry record in the space, from each author who has written there. */export async function pull(text: string, o: Opts = {}): Promise<{ space: string; recs: Rec[] }> { const c = await importCredential(text); if (!fresh(c, (o.now ?? Date.now)())) throw new ReadError('credential', 0, 'JwtExpired', `that credential ran out at ${new Date(c.expiresAt).toLocaleTimeString()}: copy a new one from atproto.html`); const recs: Rec[] = []; for (const a of await listAuthors(c, o)) for (const r of await readRecords(c, a.did, undefined, o)) if (r.collection.startsWith(`${NS}.`)) recs.push({ author: a.did, collection: r.collection, rkey: r.rkey, value: r.value ?? {} }); return { space: c.space, recs };}
/** The credential: piped in, or in MISALIGNED_READER. */function credentialText() { if (process.env.MISALIGNED_READER) return process.env.MISALIGNED_READER; if (process.stdin.isTTY) return null; return readFileSync(0, 'utf8').trim() || null;}const here = () => { try { return execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim(); } catch { return undefined; } };
export async function main() { let recs: Rec[], space: string | undefined; if (from) { const file = statSync(from).isDirectory() ? join(from, 'records.json') : from; recs = JSON.parse(readFileSync(file, 'utf8')) as Rec[]; } else { const text = credentialText(); if (!text) { console.error('No reader credential. On atproto.html (from 127.0.0.1): sign in, Read as a reader, Copy reader credential, then\n wl-paste | node scripts/telemetry.ts\nOr read records kept before: node scripts/telemetry.ts --from <dir>'); return 2; } try { ({ space, recs } = await pull(text)); } catch (e) { console.error(e instanceof ReadError ? `Reading failed at the ${e.step} step: ${e.error ?? `answered ${e.status}`}${e.message ? ` (${e.message})` : ''}` : `Reading failed: ${(e as Error).message}`); return 1; } } if (save) { mkdirSync(save, { recursive: true }); writeFileSync(join(save, 'records.json'), JSON.stringify(recs, null, 1)); }
const build = here(), runs = gather(recs); const sameBuild = (b?: string) => !!b && !!build && (build.startsWith(b) || b.startsWith(build)); if (only !== undefined) { const r = runs.find(x => x.id === only); if (!r) { console.error(`No run ${only}. Runs: ${runs.map(x => x.id).join(', ') || 'none'}`); return 1; } if (!replayable(r)) { console.error(`Run ${only} can't be replayed: ${r.session ? 'it is marked not replayable' : 'it has no session record, so no seed'}.`); return 1; } if (!sameBuild(r.session!.build)) console.log(`Note: played on ${r.session!.build ?? 'an unknown build'}, replaying on ${build?.slice(0, 9) ?? 'this checkout'}: it may part ways.\n`); const rp = replayRun(r), bad = rp.checks.filter(c => !c.ok); console.log(report([seen(r, rp)], { build, space })); console.log(`\nReplay of ${r.id}: seed ${r.session!.seed}, ${r.acts.length} actions, to step ${rp.S.steps}${rp.S.over ? ' (caught)' : ''}`); for (const c of rp.checks) console.log(`${c.ok ? ' ' : '!!'} step ${String(c.step).padStart(6)} want ${c.want} got ${c.got}`); if (rp.end) console.log(`${rp.end.ok ? ' ' : '!!'} end ${String(rp.end.step).padStart(6)} want ${rp.end.want} got ${rp.end.got}`); if (rp.unreached.length) console.log(`!! ${rp.unreached.length} snapshots past where the replay ended (step ${rp.S.steps})`); if (!rp.checks.length && !rp.end) console.log(' no snapshot hashes to check against'); return bad.length || rp.unreached.length || rp.end?.ok === false ? 1 : 0; } // a run the session leaves short (no marks, no length) is replayed for them, when this build can const all: Seen[] = runs.map(r => { const short = !r.session?.marks || r.session.t === undefined; return seen(r, short && replayable(r) && r.acts.length && sameBuild(r.session!.build) ? replayRun(r) : undefined); }); console.log(report(all, { build, space })); return 0;}
if (import.meta.url === `file://${process.argv[1]}`) process.exit(await main());