// _sessions.mjs — shared session-loader for the cost-tracking namespace. // // Iter 73 consolidation. Six analytics scripts (anomaly, burn, conversation, // counterfactual, projection, budget) previously duplicated identical // boilerplate for reading sessions from the `cost-tracking` namespace. // This module is the single source of truth. Math is byte-identical to // the per-script copies it replaces. // // CONTRACT // - `memoryListSessionKeys(ns)` → keys starting with `session-` only // - `memoryListAllKeys(ns)` → ALL keys in namespace (for non-session lookups) // - `memoryRetrieve(ns, key)` → parsed JSON value, or null on failure // - `loadSessions(ns)` → array of session records (filters out nulls) // - `sessionTs(rec)` → ms-since-epoch from capturedAt|endedAt|startedAt // - `parseDurationMs(spec)` → ms for "Nh"/"Nd"/"Nw"/"Nm"; null on parse fail import { spawnSync } from 'node:child_process'; const CLI_PKG = process.env.CLI_CORE === '1' ? '@claude-flow/cli-core@alpha' : '@claude-flow/cli@latest'; export function memoryListAllKeys(namespace) { const r = spawnSync('npx', [ CLI_PKG, 'memory', 'list', '--namespace', namespace, '--format', 'json', ], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', shell: process.platform === 'win32' }); if (r.status !== 0) return []; const m = /\[[\s\S]*\]/.exec(r.stdout || ''); if (!m) return []; let entries; try { entries = JSON.parse(m[0]); } catch { return []; } return entries .map((e) => e.key) .filter((k) => typeof k === 'string'); } export function memoryListSessionKeys(namespace) { return memoryListAllKeys(namespace).filter((k) => k.startsWith('session-')); } export function memoryRetrieve(namespace, key) { const r = spawnSync('npx', [ CLI_PKG, 'memory', 'retrieve', '--namespace', namespace, '--key', key, ], { stdio: ['ignore', 'pipe', 'pipe'], encoding: 'utf-8', shell: process.platform === 'win32' }); if (r.status !== 0) return null; const m = /\{[\s\S]*\}/.exec(r.stdout || ''); if (!m) return null; try { return JSON.parse(m[0]); } catch { return null; } } export function loadSessions(namespace) { const keys = memoryListSessionKeys(namespace); const records = []; for (const k of keys) { const rec = memoryRetrieve(namespace, k); if (rec) records.push(rec); } return records; } export function sessionTs(rec) { const t = rec.capturedAt || rec.endedAt || rec.startedAt; return t ? new Date(t).getTime() : 0; } export function parseDurationMs(spec) { const m = /^(\d+)([hdwm])$/.exec(spec); if (!m) return null; const n = parseInt(m[1], 10); const unit = { h: 3600_000, d: 86400_000, w: 7 * 86400_000, m: 30 * 86400_000 }[m[2]]; return unit ? n * unit : null; }