linkedin-studio/scripts/analytics/src/utils/storage.ts
Kjell Tore Guttormsen 40986575b6 feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase
analysis with external knowledge via parallel agent swarms. Produces research
briefs with triangulation, confidence ratings, and source quality assessment.

New command: /ultraresearch-local with modes --quick, --local, --external, --fg.
New agents: research-orchestrator (opus), docs-researcher, community-researcher,
security-researcher, contrarian-researcher, gemini-bridge (all sonnet).
New template: research-brief-template.md.

Integration: --research flag in /ultraplan-local accepts pre-built research
briefs (up to 3), enriches the interview and exploration phases. Planning
orchestrator cross-references brief findings during synthesis.

Design principle: Context Engineering — right information to right agent at
right time. Research briefs are structured artifacts in the pipeline:
ultraresearch → brief → ultraplan --research → plan → ultraexecute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 08:58:35 +02:00

290 lines
7.9 KiB
TypeScript

import { readFileSync, writeFileSync, readdirSync, existsSync, mkdirSync } from "node:fs";
import { join, resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import type { AnalyticsBatch, WeeklyReport, MonthlyReport, PostAnalytics } from "../models/types.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
/**
* Get the analytics root directory from environment or default location
* Default is assets/analytics relative to the plugin root
*/
export function getAnalyticsRoot(): string {
if (process.env.ANALYTICS_ROOT) {
return resolve(process.env.ANALYTICS_ROOT);
}
// Build output is at: scripts/analytics/build/utils/storage.js
// Plugin root is 4 levels up: ../../../../
// Then assets/analytics from there
const pluginRoot = resolve(__dirname, "../../../../");
return join(pluginRoot, "assets", "analytics");
}
/**
* Ensure required subdirectories exist under analytics root
*/
export function ensureDirectories(root: string): void {
const directories = ["exports", "posts", "weekly-reports", "monthly-reports"];
if (!existsSync(root)) {
mkdirSync(root, { recursive: true });
}
for (const dir of directories) {
const path = join(root, dir);
if (!existsSync(path)) {
mkdirSync(path, { recursive: true });
}
}
}
/**
* List all CSV export files in the exports directory
*/
export function listExports(root: string): string[] {
const exportsDir = join(root, "exports");
if (!existsSync(exportsDir)) {
return [];
}
return readdirSync(exportsDir)
.filter(file => file.endsWith(".csv"))
.sort();
}
/**
* Sanitize date string to only allow YYYY-MM-DD format
*/
function sanitizeDate(date: string): string {
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
throw new Error(`Invalid date format: ${date}. Expected YYYY-MM-DD`);
}
return date;
}
/**
* Sanitize ID string to only allow alphanumeric and hyphens
*/
function sanitizeId(id: string): string {
if (!/^[a-zA-Z0-9-]+$/.test(id)) {
throw new Error(`Invalid ID format: ${id}. Only alphanumeric and hyphens allowed`);
}
return id;
}
/**
* Verify that the resolved path is within the expected directory
*/
function verifyPathWithinDirectory(filepath: string, expectedDir: string): void {
const resolvedPath = resolve(filepath);
const resolvedDir = resolve(expectedDir);
if (!resolvedPath.startsWith(resolvedDir + "/") && resolvedPath !== resolvedDir) {
throw new Error(`Path traversal detected: ${filepath} is not within ${expectedDir}`);
}
}
/**
* Save an analytics batch to disk
* Returns the filename that was created
*/
export function saveBatch(root: string, batch: AnalyticsBatch): string {
ensureDirectories(root);
const postsDir = join(root, "posts");
// Sanitize inputs to prevent path traversal
const date = sanitizeDate(batch.dateRange.from);
const shortId = sanitizeId(batch.batchId.substring(0, 8));
const filename = `${date}-${shortId}.json`;
const filepath = join(postsDir, filename);
// Verify the resolved filepath is within postsDir
verifyPathWithinDirectory(filepath, postsDir);
writeFileSync(filepath, JSON.stringify(batch, null, 2), "utf-8");
return filename;
}
/**
* Load all analytics batches from disk
* Returns batches sorted by importedAt timestamp
*/
export function loadAllBatches(root: string): AnalyticsBatch[] {
const postsDir = join(root, "posts");
if (!existsSync(postsDir)) {
return [];
}
const batches: AnalyticsBatch[] = [];
for (const file of readdirSync(postsDir)) {
if (!file.endsWith(".json")) {
continue;
}
const filepath = join(postsDir, file);
const content = readFileSync(filepath, "utf-8");
try {
const batch = JSON.parse(content) as AnalyticsBatch;
batches.push(batch);
} catch {
// Skip corrupt batch file
continue;
}
}
return batches.sort((a, b) =>
a.importedAt.localeCompare(b.importedAt)
);
}
/**
* Load all posts from all batches, deduplicated by post ID
* Latest import wins. Sorted by publishedDate descending.
*/
export function loadAllPosts(root: string): PostAnalytics[] {
const batches = loadAllBatches(root);
// Use Map to deduplicate - key is post ID, value is { post, importedAt }
const postMap = new Map<string, { post: PostAnalytics; importedAt: string }>();
for (const batch of batches) {
for (const post of batch.posts) {
const existing = postMap.get(post.id);
// Keep post with latest importedAt timestamp
if (!existing || batch.importedAt > existing.importedAt) {
postMap.set(post.id, {
post,
importedAt: batch.importedAt
});
}
}
}
// Extract posts and sort by publishedDate descending
const posts = Array.from(postMap.values()).map(({ post }) => post);
return posts.sort((a, b) =>
b.publishedDate.localeCompare(a.publishedDate)
);
}
/**
* Sanitize week string to only allow ISO week format (YYYY-WXX)
*/
function sanitizeWeek(week: string): string {
if (!/^\d{4}-W\d{2}$/.test(week)) {
throw new Error(`Invalid week format: ${week}. Expected YYYY-WXX`);
}
return week;
}
/**
* Save a weekly report to disk
* Returns the filename that was created
*/
export function saveWeeklyReport(root: string, report: WeeklyReport): string {
ensureDirectories(root);
const reportsDir = join(root, "weekly-reports");
// Sanitize week to prevent path traversal
const week = sanitizeWeek(report.week);
const filename = `${week}.json`;
const filepath = join(reportsDir, filename);
// Verify the resolved filepath is within reportsDir
verifyPathWithinDirectory(filepath, reportsDir);
writeFileSync(filepath, JSON.stringify(report, null, 2), "utf-8");
return filename;
}
/**
* Load a specific weekly report by week identifier
* Returns null if not found
*/
export function loadWeeklyReport(root: string, week: string): WeeklyReport | null {
week = sanitizeWeek(week);
const reportsDir = join(root, "weekly-reports");
const filepath = join(reportsDir, `${week}.json`);
if (!existsSync(filepath)) {
return null;
}
const content = readFileSync(filepath, "utf-8");
return JSON.parse(content) as WeeklyReport;
}
/**
* Load all weekly reports from disk
* Returns reports sorted by week descending (newest first)
*/
export function loadAllWeeklyReports(root: string): WeeklyReport[] {
const reportsDir = join(root, "weekly-reports");
if (!existsSync(reportsDir)) {
return [];
}
const reports: WeeklyReport[] = [];
for (const file of readdirSync(reportsDir)) {
if (!file.endsWith(".json")) {
continue;
}
const filepath = join(reportsDir, file);
const content = readFileSync(filepath, "utf-8");
const report = JSON.parse(content) as WeeklyReport;
reports.push(report);
}
return reports.sort((a, b) =>
b.week.localeCompare(a.week)
);
}
/**
* Sanitize month string to only allow YYYY-MM format
*/
function sanitizeMonth(month: string): string {
if (!/^\d{4}-\d{2}$/.test(month)) {
throw new Error(`Invalid month format: ${month}. Expected YYYY-MM`);
}
return month;
}
/**
* Save a monthly report to disk
*/
export function saveMonthlyReport(root: string, report: MonthlyReport): string {
ensureDirectories(root);
const reportsDir = join(root, "monthly-reports");
const month = sanitizeMonth(report.month);
const filename = `${month}.json`;
const filepath = join(reportsDir, filename);
verifyPathWithinDirectory(filepath, reportsDir);
writeFileSync(filepath, JSON.stringify(report, null, 2), "utf-8");
return filename;
}
/**
* Load a specific monthly report by month identifier
*/
export function loadMonthlyReport(root: string, month: string): MonthlyReport | null {
month = sanitizeMonth(month);
const reportsDir = join(root, "monthly-reports");
const filepath = join(reportsDir, `${month}.json`);
if (!existsSync(filepath)) return null;
const content = readFileSync(filepath, "utf-8");
return JSON.parse(content) as MonthlyReport;
}