Intelligence

Artifacts

Browse the repository, read documents, and manage the governance folders. Source, runtime, and infrastructure are read-only.

Repository
README.md
CONSTITUTION_COMPLIANCE_AUDIT_V1.mdREADME.md
repositories/aaf-holdings/hq01/lib/content/artifacts.ts
3.3 KB
import fs from "node:fs";
import path from "node:path";
import { cache } from "react";
import { CONTENT_ROOT, IGNORED_DIRS } from "./config";
import type { ArtifactNode } from "./types";

const TEXT_EXTENSIONS = new Set([
  ".md", ".mdx", ".txt", ".yaml", ".yml", ".json", ".ts", ".tsx", ".js",
  ".jsx", ".css", ".scss", ".html", ".sh", ".env", ".toml", ".ini", ".csv",
  ".sql", ".py", ".rb", ".go", ".rs", ".gitignore", ".dockerfile",
]);

function buildTree(dir: string, rel: string, depth: number): ArtifactNode[] {
  if (depth > 8) return [];
  let entries: fs.Dirent[];
  try {
    entries = fs.readdirSync(dir, { withFileTypes: true });
  } catch {
    return [];
  }

  const nodes: ArtifactNode[] = [];
  for (const entry of entries) {
    if (entry.name.startsWith(".") && entry.name !== ".gitignore") continue;
    if (IGNORED_DIRS.has(entry.name)) continue;
    // Never surface the HQ01 app's own build output as content.
    if (entry.name === "hq01" && rel === "repositories/aaf-holdings") {
      // still allow browsing source, but skip heavy generated dirs handled above
    }

    const full = path.join(dir, entry.name);
    const childRel = rel ? `${rel}/${entry.name}` : entry.name;

    if (entry.isDirectory()) {
      nodes.push({
        name: entry.name,
        path: childRel,
        type: "dir",
        children: buildTree(full, childRel, depth + 1),
      });
    } else if (entry.isFile()) {
      let size: number | undefined;
      try {
        size = fs.statSync(full).size;
      } catch {
        size = undefined;
      }
      nodes.push({
        name: entry.name,
        path: childRel,
        type: "file",
        ext: path.extname(entry.name).toLowerCase(),
        size,
      });
    }
  }

  return nodes.sort((a, b) => {
    if (a.type !== b.type) return a.type === "dir" ? -1 : 1;
    return a.name.localeCompare(b.name);
  });
}

export const getArtifactTree = cache((): ArtifactNode[] => {
  return buildTree(CONTENT_ROOT, "", 0);
});

/** Resolve a workspace-relative path safely (no escaping the content root). */
function resolveSafe(relativePath: string): string | null {
  const normalized = path
    .normalize(relativePath)
    .replace(/^([/\\])+/, "");
  const full = path.join(CONTENT_ROOT, normalized);
  const rootWithSep = CONTENT_ROOT.endsWith(path.sep)
    ? CONTENT_ROOT
    : CONTENT_ROOT + path.sep;
  if (full !== CONTENT_ROOT && !full.startsWith(rootWithSep)) return null;
  return full;
}

export interface ArtifactFile {
  path: string;
  name: string;
  ext: string;
  size: number;
  isText: boolean;
  content: string | null;
}

export const getArtifactFile = cache((relativePath: string): ArtifactFile | null => {
  const full = resolveSafe(relativePath);
  if (!full) return null;
  let stat: fs.Stats;
  try {
    stat = fs.statSync(full);
  } catch {
    return null;
  }
  if (!stat.isFile()) return null;

  const ext = path.extname(full).toLowerCase();
  const name = path.basename(full);
  const isText =
    TEXT_EXTENSIONS.has(ext) ||
    TEXT_EXTENSIONS.has(name.toLowerCase()) ||
    stat.size < 256 * 1024;

  let content: string | null = null;
  if (isText && stat.size < 1024 * 1024) {
    try {
      content = fs.readFileSync(full, "utf8");
    } catch {
      content = null;
    }
  }

  return {
    path: relativePath,
    name,
    ext,
    size: stat.size,
    isText: Boolean(content !== null),
    content,
  };
});

root · /srv/aaf