import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs"; import { basename, join, relative } from "node:path"; import { execFileSync } from "node:child_process"; import { fileURLToPath } from "node:url"; export const workspaceRoot = process.env.WORKSPACE_FATHER_PATH || "/Users/Tabitha/.openclaw/workspace_Father"; const outputPath = join(process.cwd(), "public", "local-context.json"); const giteaBaseUrl = "http://gitea.orson.tealthrone"; const folderSearchRoots = (process.env.WORKSPACE_FOLDER_SEARCH_ROOTS || `/Users/Tabitha,${workspaceRoot}`) .split(",") .map((root) => root.trim()) .filter(Boolean); const folderSearchMaxDepth = Number(process.env.WORKSPACE_FOLDER_SEARCH_MAX_DEPTH || 3); const skippedFolderNames = new Set([ ".git", ".Trash", "Applications", "Library", "Movies", "Music", "node_modules", "dist", "build", ".next", ".vite" ]); function runGit(args, cwd) { try { return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); } catch { return ""; } } function findGitWorktrees(root, maxDepth = 4) { const found = []; function walk(dir, depth) { if (depth > maxDepth) { return; } if (existsSync(join(dir, ".git"))) { found.push(dir); return; } let entries = []; try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; } for (const entry of entries) { if (!entry.isDirectory() || entry.name === "node_modules" || entry.name === ".git") { continue; } walk(join(dir, entry.name), depth + 1); } } walk(root, 0); return found.sort(); } function findFolderCandidates(roots, maxDepth = 3) { const found = new Map(); function addCandidate(path, source) { found.set(path, { name: basename(path), path, source }); } function walk(dir, depth, source) { if (depth > maxDepth || found.has(dir)) { return; } addCandidate(dir, source); let entries = []; try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; } for (const entry of entries) { if (!entry.isDirectory() || skippedFolderNames.has(entry.name)) { continue; } walk(join(dir, entry.name), depth + 1, source); } } for (const root of roots) { if (!existsSync(root)) { continue; } walk(root, 0, root === workspaceRoot ? "workspace-root" : "folder-search"); } return Array.from(found.values()) .filter((candidate) => candidate.path !== "/Users/Tabitha") .sort((a, b) => a.path.localeCompare(b.path)) .slice(0, 2000); } function normalizeGiteaRemote(remoteUrl) { if (!remoteUrl) { return null; } const match = remoteUrl.match( /(?:gitea\.orson\.tealthrone|100\.79\.253\.19(?::3000)?)[/:]([^/]+)\/([^/.]+)(?:\.git)?$/ ); if (!match) { return null; } const [, owner, name] = match; return { fullName: `${owner}/${name}`, owner, name, webUrl: `${giteaBaseUrl}/${owner}/${name}`, cloneUrl: `http://100.79.253.19:3000/${owner}/${name}.git` }; } function repoForWorktree(path) { const remoteNames = runGit(["remote"], path).split(/\s+/).filter(Boolean); const orderedNames = ["gitea", "hagia", "origin", ...remoteNames].filter((name, index, names) => { return remoteNames.includes(name) && names.indexOf(name) === index; }); for (const remoteName of orderedNames) { const repo = normalizeGiteaRemote(runGit(["remote", "get-url", remoteName], path)); if (repo) { return repo; } } return null; } export function buildContext() { const topLevelWorkspaces = readdirSync(workspaceRoot, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) .map((entry) => ({ name: entry.name, path: join(workspaceRoot, entry.name) })) .sort((a, b) => a.name.localeCompare(b.name)); const gitWorktrees = findGitWorktrees(workspaceRoot).map((path) => { const repo = repoForWorktree(path); const topWorkspaceName = relative(workspaceRoot, path).split("/")[0] || basename(path); return { name: basename(path), path, relativePath: relative(workspaceRoot, path), topWorkspaceName, branch: runGit(["branch", "--show-current"], path) || null, giteaRepo: repo }; }); const workspaces = topLevelWorkspaces.map((workspace) => ({ ...workspace, linkedRepos: gitWorktrees .filter((worktree) => worktree.topWorkspaceName === workspace.name && worktree.giteaRepo) .map((worktree) => ({ worktreePath: worktree.path, worktreeRelativePath: worktree.relativePath, fullName: worktree.giteaRepo.fullName, webUrl: worktree.giteaRepo.webUrl })) })); const workspaceData = gitWorktrees.find( (worktree) => worktree.path === join(workspaceRoot, "workspace_Data") ); return { generatedAt: "build-time-local-context", workspaceRoot, workspaces, gitWorktrees, folderCandidates: findFolderCandidates(folderSearchRoots, folderSearchMaxDepth), knownUrls: [ { label: "Automated Training Monitor", url: "http://100.66.226.22:23030/", workspacePath: join(workspaceRoot, "workspace_Data"), workspaceName: "workspace_Data", giteaRepo: workspaceData?.giteaRepo ?? { fullName: "jacob-mathison/home-grown-llm-data", webUrl: `${giteaBaseUrl}/jacob-mathison/home-grown-llm-data` } } ] }; } export function writeLocalContext() { mkdirSync(join(process.cwd(), "public"), { recursive: true }); if (!existsSync(workspaceRoot)) { if (existsSync(outputPath)) { console.log(`Workspace root missing; preserving ${outputPath}`); return; } throw new Error(`Workspace root does not exist: ${workspaceRoot}`); } const context = buildContext(); writeFileSync(outputPath, `${JSON.stringify(context, null, 2)}\n`); console.log(`Wrote ${outputPath} with ${context.workspaces.length} workspaces`); } if (process.argv[1] === fileURLToPath(import.meta.url)) { writeLocalContext(); }