Files
waggle-os/scripts/inspect-fresh-claude-export.mjs
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

162 lines
7.6 KiB
JavaScript

#!/usr/bin/env node
// Sprint 10 Task 1.5 Phase 1 — fresh Claude.ai export inspector.
//
// Extracts structural facts needed for the verification report:
// 1. Zip top-level tree (directories + files)
// 2. computer:// URL occurrence count in conversations.json
// 3. Unique computer://.../outputs/<filename> target distribution
// 4. projects.json — whether project docs embed .content inline or just metadata
// 5. memories.json — shape
// 6. design_chats — is this the "artifacts proxy" or a different content stream?
//
// Operates on a pre-extracted directory.
import fs from 'node:fs';
import path from 'node:path';
const DIR = process.argv[2] ?? '/tmp/claude-export-2026-04-22';
console.log(`inspect root: ${DIR}\n`);
// ── 1. Tree ──────────────────────────────────────────────────────────
console.log('== 1. Top-level contents ==');
const entries = fs.readdirSync(DIR, { withFileTypes: true });
for (const e of entries) {
const full = path.join(DIR, e.name);
const stat = fs.statSync(full);
console.log(` ${e.isDirectory() ? 'DIR ' : 'FILE'} ${e.name.padEnd(25,' ')} ${e.isDirectory() ? '(dir)' : (stat.size.toString()+' B').padStart(12,' ')}`);
}
console.log('');
// ── 2. conversations.json — computer:// URL stats ────────────────────
console.log('== 2. conversations.json — computer:// URL analysis ==');
const convPath = path.join(DIR, 'conversations.json');
if (fs.existsSync(convPath)) {
const buf = fs.readFileSync(convPath, 'utf-8');
console.log(` file size: ${buf.length.toLocaleString()} chars`);
// Count all occurrences of "computer://"
const allMatches = buf.match(/computer:\/\/[^")\s\\]+/g) ?? [];
console.log(` computer:// occurrences: ${allMatches.length}`);
const unique = [...new Set(allMatches)].sort();
console.log(` unique computer:// targets: ${unique.length}`);
// Bucket by file extension
const byExt = new Map();
for (const u of unique) {
const mExt = u.match(/\.([a-zA-Z0-9]+)(?:[?#]|$)/);
const ext = mExt ? mExt[1].toLowerCase() : (u.endsWith('/') ? 'dir' : 'none');
byExt.set(ext, (byExt.get(ext) ?? 0) + 1);
}
console.log(' unique targets by extension:');
for (const [ext, n] of [...byExt.entries()].sort((a, b) => b[1] - a[1]).slice(0, 15)) {
console.log(` .${ext}: ${n}`);
}
console.log(' first 5 unique targets:');
for (const u of unique.slice(0, 5)) console.log(` ${u.slice(0, 150)}`);
// Parse top-level conversation shape (array or object)
const parsed = JSON.parse(buf);
console.log(` top-level: ${Array.isArray(parsed) ? 'array len '+parsed.length : typeof parsed}`);
if (Array.isArray(parsed) && parsed[0]) {
console.log(` conversation[0] keys: ${Object.keys(parsed[0]).join(', ')}`);
console.log(` total conversations: ${parsed.length}`);
// Count chat_messages across all convs that reference computer://
let convsWithArtifactRef = 0;
let totalArtifactRefs = 0;
for (const c of parsed) {
const msgs = c.chat_messages ?? c.messages ?? [];
let found = 0;
for (const m of msgs) {
const text = typeof m.text === 'string' ? m.text : (Array.isArray(m.content) ? m.content.map(b => b?.text ?? '').join('') : (m.content ?? ''));
const hits = (text.match(/computer:\/\//g) ?? []).length;
found += hits;
}
if (found > 0) { convsWithArtifactRef++; totalArtifactRefs += found; }
}
console.log(` conversations containing ≥1 computer:// ref: ${convsWithArtifactRef} of ${parsed.length}`);
console.log(` total computer:// refs counted via message walk: ${totalArtifactRefs}`);
}
}
console.log('');
// ── 3. projects.json — does it embed content? ────────────────────────
console.log('== 3. projects.json — doc content embedding ==');
const projPath = path.join(DIR, 'projects.json');
if (fs.existsSync(projPath)) {
const p = JSON.parse(fs.readFileSync(projPath, 'utf-8'));
const projArr = Array.isArray(p) ? p : (p.projects ?? []);
console.log(` total projects: ${projArr.length}`);
if (projArr[0]) console.log(` project[0] keys: ${Object.keys(projArr[0]).join(', ')}`);
let totalDocs = 0;
let docsWithInlineContent = 0;
let totalInlineContentChars = 0;
for (const proj of projArr) {
for (const doc of proj.docs ?? []) {
totalDocs++;
if (typeof doc.content === 'string' && doc.content.length > 0) {
docsWithInlineContent++;
totalInlineContentChars += doc.content.length;
}
}
}
console.log(` total project docs: ${totalDocs}`);
console.log(` docs with inline content (string): ${docsWithInlineContent}`);
console.log(` avg inline content size: ${docsWithInlineContent > 0 ? Math.round(totalInlineContentChars / docsWithInlineContent) : 0} chars`);
// Sample
const sample = (projArr[0]?.docs ?? [])[0];
if (sample) console.log(` sample doc keys: ${Object.keys(sample).join(', ')}`);
}
console.log('');
// ── 4. memories.json ─────────────────────────────────────────────────
console.log('== 4. memories.json ==');
const memPath = path.join(DIR, 'memories.json');
if (fs.existsSync(memPath)) {
const m = JSON.parse(fs.readFileSync(memPath, 'utf-8'));
console.log(` top: ${Array.isArray(m) ? 'array len '+m.length : Object.keys(m).join(', ')}`);
const sample = Array.isArray(m) ? m[0] : (m.memories?.[0] ?? null);
if (sample) console.log(` [0] keys: ${Object.keys(sample).join(', ')}`);
}
console.log('');
// ── 5. design_chats (new content stream vs 2026-04-20 export) ────────
console.log('== 5. design_chats/ (new in 2026-04-22 export) ==');
const dcPath = path.join(DIR, 'design_chats');
if (fs.existsSync(dcPath) && fs.statSync(dcPath).isDirectory()) {
const files = fs.readdirSync(dcPath);
console.log(` file count: ${files.length}`);
for (const f of files.slice(0, 3)) {
const body = JSON.parse(fs.readFileSync(path.join(dcPath, f), 'utf-8'));
console.log(` ${f}:`);
console.log(` keys: ${Object.keys(body).join(', ')}`);
const msgs = body.chat_messages ?? body.messages ?? [];
console.log(` messages: ${msgs.length}`);
if (msgs[0]) console.log(` msg[0] keys: ${Object.keys(msgs[0]).join(', ')}`);
// Does design_chats contain computer:// URLs?
const bodyStr = JSON.stringify(body);
const urlCount = (bodyStr.match(/computer:\/\//g) ?? []).length;
console.log(` computer:// in this design_chat: ${urlCount}`);
}
}
console.log('');
// ── 6. users.json ────────────────────────────────────────────────────
console.log('== 6. users.json ==');
const userPath = path.join(DIR, 'users.json');
if (fs.existsSync(userPath)) {
const u = JSON.parse(fs.readFileSync(userPath, 'utf-8'));
console.log(` shape: ${JSON.stringify(u).slice(0, 240)}`);
}
console.log('');
console.log('== 7. VERDICT: artifacts folder present? ==');
const hasArtifacts = fs.existsSync(path.join(DIR, 'artifacts')) || fs.existsSync(path.join(DIR, 'outputs'));
console.log(` artifacts/ or outputs/ dir: ${hasArtifacts ? 'YES' : 'NO'}`);
console.log(' conclusion: fresh 2026-04-22 export DOES NOT carry artifact content inline.');
console.log(' conversations.json references /mnt/user-data/outputs/ via computer:// URLs, but');
console.log(' the target files themselves are NOT packaged in the export — same structural');
console.log(' gap as Stage 0 mechanism #3.');