import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { fileURLToPath } from "node:url"; export const SKILL_SLUG = "knowledge-one-view-cli"; export const LEGACY_SKILL_SLUG = "oracle-sales-intelligence-direct"; export const DISPLAY_RUNTIME_HOME = "/knowledge-one-view-cli/runtime"; export const DISPLAY_STATE_PATH = "/knowledge-one-view-cli/runtime/auth-state.json"; export const DISPLAY_META_PATH = "/knowledge-one-view-cli/runtime/session-meta.json"; export const DISPLAY_PROFILE_DIR = "/knowledge-one-view-cli/runtime/chrome-profile"; export const DISPLAY_CHROME_PATH = "auto-detected by platform"; export const LEGACY_DISPLAY_HOME = `~/.codex/${LEGACY_SKILL_SLUG}`; const helperDir = path.dirname(fileURLToPath(import.meta.url)); export const SKILL_HOME = path.resolve(helperDir, "..", ".."); export const CODEX_HOME = path.join(os.homedir(), ".codex"); export const LEGACY_HOME = path.join(os.homedir(), ".codex", LEGACY_SKILL_SLUG); export const LEGACY_STATE_PATH = path.join(LEGACY_HOME, "auth-state.json"); export const LEGACY_META_PATH = path.join(LEGACY_HOME, "session-meta.json"); export const LEGACY_PROFILE_DIR = path.join(LEGACY_HOME, "chrome-profile"); function pathExists(targetPath) { return Boolean(targetPath) && fs.existsSync(targetPath); } function normalizeDir(targetPath) { return path.resolve(targetPath); } function normalizeWorkspaceRuntimeHomeCandidate(targetPath) { const resolved = normalizeDir(targetPath); const runtimeName = path.basename(resolved).toLowerCase(); if (runtimeName === "runtime") { return resolved; } if (!runtimeName.startsWith("runtime-")) { return resolved; } const parentDir = path.dirname(resolved); if (path.basename(parentDir) !== SKILL_SLUG) { return resolved; } // Keep one canonical runtime path in the workspace skill folder. return path.join(parentDir, "runtime"); } function looksLikeTemporaryFileName(fileName) { const lower = String(fileName || "").toLowerCase(); return ( lower.startsWith("tmp-") || lower.startsWith("temp-") || lower.startsWith("scratch-") || lower.startsWith("intermediate-") || lower.startsWith("render-output-") || lower.includes(".tmp.") || lower.endsWith(".tmp") || lower.endsWith(".temp") ); } function isSameOrInside(targetPath, parentPath) { const relative = path.relative(parentPath, targetPath); return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative)); } function looksLikeInstalledSkillHome(targetPath) { return ["SKILL.md", "scripts", "docs", "references", "tests"].some((name) => pathExists(path.join(targetPath, name)) ); } function looksLikeWorkspaceSkillHome(targetPath) { if (path.basename(targetPath) !== SKILL_SLUG) return false; if (looksLikeInstalledSkillHome(targetPath)) return false; return true; } function candidateChromePaths() { if (process.platform === "win32") { return [ process.env.ORACLE_SI_CHROME_PATH, process.env.LOCALAPPDATA ? path.join(process.env.LOCALAPPDATA, "Google", "Chrome", "Application", "chrome.exe") : null, process.env["PROGRAMFILES"] ? path.join(process.env["PROGRAMFILES"], "Google", "Chrome", "Application", "chrome.exe") : null, process.env["PROGRAMFILES(X86)"] ? path.join(process.env["PROGRAMFILES(X86)"], "Google", "Chrome", "Application", "chrome.exe") : null, ].filter(Boolean); } if (process.platform === "darwin") { return [ process.env.ORACLE_SI_CHROME_PATH, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", path.join(os.homedir(), "Applications", "Google Chrome.app", "Contents", "MacOS", "Google Chrome"), ].filter(Boolean); } return [ process.env.ORACLE_SI_CHROME_PATH, "/usr/bin/google-chrome", "/usr/bin/chromium", "/usr/bin/chromium-browser", ].filter(Boolean); } export function resolveWorkspaceRoot(candidatePath = process.cwd()) { const cwd = normalizeDir(candidatePath); const codexHome = normalizeDir(CODEX_HOME); if (isSameOrInside(cwd, codexHome)) { throw new Error( `Cannot infer a workspace root from ${cwd}. Run this command from the target workspace or pass --runtime-home (or ORACLE_SI_RUNTIME_HOME) explicitly. Refusing to create runtime files inside ${codexHome}.` ); } if (looksLikeWorkspaceSkillHome(cwd)) { return path.dirname(cwd); } return cwd; } export function buildRuntimeHomeFromWorkspace(workspaceRoot) { return path.join(normalizeDir(workspaceRoot), SKILL_SLUG, "runtime"); } export function buildLegacyRuntimeHomeFromWorkspace(workspaceRoot) { return path.join(normalizeDir(workspaceRoot), LEGACY_SKILL_SLUG, "runtime"); } export function resolveWorkspaceSkillHome(runtimeHome) { return path.dirname(resolveRuntimeHome(runtimeHome)); } export function resolveLegacyWorkspaceSkillHome(runtimeHome) { return path.dirname(resolveLegacyRuntimeHomeCandidate(runtimeHome)); } export function resolveRuntimeHome(explicitPath) { if (explicitPath) return normalizeWorkspaceRuntimeHomeCandidate(explicitPath); if (process.env.ORACLE_SI_RUNTIME_HOME) { return normalizeWorkspaceRuntimeHomeCandidate(process.env.ORACLE_SI_RUNTIME_HOME); } return buildRuntimeHomeFromWorkspace(resolveWorkspaceRoot()); } export function resolveLegacyRuntimeHomeCandidate(runtimeHome) { const resolvedRuntimeHome = resolveRuntimeHome(runtimeHome); const workspaceRoot = path.dirname(path.dirname(resolvedRuntimeHome)); return buildLegacyRuntimeHomeFromWorkspace(workspaceRoot); } export function resolveOutputDir(runtimeHome) { return path.join(resolveWorkspaceSkillHome(runtimeHome), "output"); } export function resolveOutputFilePath(explicitPath, runtimeHome) { if (!explicitPath) return null; if (path.isAbsolute(explicitPath)) return path.resolve(explicitPath); const workspaceSkillHome = resolveWorkspaceSkillHome(runtimeHome); if (path.dirname(explicitPath) === ".") { // Keep user-facing/final outputs in output/, but route temporary/intermediate // file names directly into tmp/ to avoid polluting output/. const fileName = path.basename(explicitPath); if (looksLikeTemporaryFileName(fileName)) { return path.join(resolveTmpDir(runtimeHome), fileName); } return path.join(resolveOutputDir(runtimeHome), explicitPath); } return path.join(workspaceSkillHome, explicitPath); } export function resolveTmpDir(runtimeHome) { return path.join(resolveWorkspaceSkillHome(runtimeHome), "tmp"); } export function resolveTestRunsDir(runtimeHome) { return path.join(resolveOutputDir(runtimeHome), "test-runs"); } export function resolveTmpTestInputsDir(runtimeHome) { return resolveTmpDir(runtimeHome); } export function resolveTmpRenderedSqlDir(runtimeHome) { return resolveTmpDir(runtimeHome); } export function resolveTmpScratchDir(runtimeHome) { return resolveTmpDir(runtimeHome); } export function resolveStateWritePath(options = {}) { if (options.explicitPath) return path.resolve(options.explicitPath); if (process.env.ORACLE_SI_STATE_PATH) return path.resolve(process.env.ORACLE_SI_STATE_PATH); return path.join(resolveRuntimeHome(options.runtimeHome), "auth-state.json"); } export function resolveMetaWritePath(options = {}) { if (options.explicitPath) return path.resolve(options.explicitPath); if (process.env.ORACLE_SI_META_PATH) return path.resolve(process.env.ORACLE_SI_META_PATH); return path.join(resolveRuntimeHome(options.runtimeHome), "session-meta.json"); } export function resolveProfileDir(options = {}) { if (options.explicitPath) return path.resolve(options.explicitPath); if (process.env.ORACLE_SI_PROFILE_DIR) return path.resolve(process.env.ORACLE_SI_PROFILE_DIR); return path.join(resolveRuntimeHome(options.runtimeHome), "chrome-profile"); } export function resolveStateReadPath(options = {}) { const preferred = resolveStateWritePath(options); if (pathExists(preferred)) return preferred; const legacyWorkspacePath = path.join( resolveLegacyRuntimeHomeCandidate(options.runtimeHome), "auth-state.json" ); if (pathExists(legacyWorkspacePath)) return legacyWorkspacePath; return pathExists(LEGACY_STATE_PATH) ? LEGACY_STATE_PATH : preferred; } export function resolveMetaReadPath(options = {}) { const preferred = resolveMetaWritePath(options); if (pathExists(preferred)) return preferred; const legacyWorkspacePath = path.join( resolveLegacyRuntimeHomeCandidate(options.runtimeHome), "session-meta.json" ); if (pathExists(legacyWorkspacePath)) return legacyWorkspacePath; return pathExists(LEGACY_META_PATH) ? LEGACY_META_PATH : preferred; } export function resolveChromePath(explicitPath) { if (explicitPath) return path.resolve(explicitPath); if (process.env.ORACLE_SI_CHROME_PATH) return path.resolve(process.env.ORACLE_SI_CHROME_PATH); for (const candidate of candidateChromePaths()) { if (pathExists(candidate)) { return candidate; } } return candidateChromePaths()[0] || "google-chrome"; } export async function ensureDir(dirPath) { await fs.promises.mkdir(dirPath, { recursive: true }); } export async function ensureParentDir(filePath) { await ensureDir(path.dirname(filePath)); } async function migrateLegacyTmpSubdirs(tmpDir) { const legacySubdirs = ["test-inputs", "rendered-sql", "scratch"]; const moveFileToTmpRoot = async (sourcePath) => { const ext = path.extname(sourcePath); const base = path.basename(sourcePath, ext); let candidateName = path.basename(sourcePath); let targetPath = path.join(tmpDir, candidateName); let suffix = 1; while (pathExists(targetPath)) { candidateName = `${base}-migrated-${suffix}${ext}`; targetPath = path.join(tmpDir, candidateName); suffix += 1; } await fs.promises.rename(sourcePath, targetPath); }; const walkAndMoveFiles = async (currentDir) => { const entries = await fs.promises.readdir(currentDir, { withFileTypes: true }); for (const entry of entries) { const absolutePath = path.join(currentDir, entry.name); if (entry.isDirectory()) { await walkAndMoveFiles(absolutePath); continue; } await moveFileToTmpRoot(absolutePath); } }; for (const subdirName of legacySubdirs) { const legacyDir = path.join(tmpDir, subdirName); if (!pathExists(legacyDir)) continue; await walkAndMoveFiles(legacyDir); await fs.promises.rm(legacyDir, { recursive: true, force: true }); } } async function migrateTemporaryOutputFiles(outputDir, tmpDir) { if (!pathExists(outputDir)) return; const entries = await fs.promises.readdir(outputDir, { withFileTypes: true }); for (const entry of entries) { if (!entry.isFile()) continue; if (!looksLikeTemporaryFileName(entry.name)) continue; const sourcePath = path.join(outputDir, entry.name); let candidateName = entry.name; let targetPath = path.join(tmpDir, candidateName); let suffix = 1; const ext = path.extname(entry.name); const base = path.basename(entry.name, ext); while (pathExists(targetPath)) { candidateName = `${base}-migrated-${suffix}${ext}`; targetPath = path.join(tmpDir, candidateName); suffix += 1; } await fs.promises.rename(sourcePath, targetPath); } } export async function ensureRuntimeTree(runtimeHome) { const resolvedRuntimeHome = resolveRuntimeHome(runtimeHome); const workspaceSkillHome = resolveWorkspaceSkillHome(resolvedRuntimeHome); const outputDir = resolveOutputDir(resolvedRuntimeHome); const testRunsDir = resolveTestRunsDir(resolvedRuntimeHome); const tmpDir = resolveTmpDir(resolvedRuntimeHome); const tmpTestInputsDir = resolveTmpTestInputsDir(resolvedRuntimeHome); const tmpRenderedSqlDir = resolveTmpRenderedSqlDir(resolvedRuntimeHome); const tmpScratchDir = resolveTmpScratchDir(resolvedRuntimeHome); const profileDir = resolveProfileDir({ runtimeHome: resolvedRuntimeHome }); await ensureDir(workspaceSkillHome); await ensureDir(resolvedRuntimeHome); await ensureDir(outputDir); await ensureDir(testRunsDir); await ensureDir(tmpDir); await migrateLegacyTmpSubdirs(tmpDir); await migrateTemporaryOutputFiles(outputDir, tmpDir); await ensureDir(tmpTestInputsDir); await ensureDir(tmpRenderedSqlDir); await ensureDir(tmpScratchDir); await ensureDir(profileDir); return { workspaceSkillHome, runtimeHome: resolvedRuntimeHome, outputDir, testRunsDir, tmpDir, tmpTestInputsDir, tmpRenderedSqlDir, tmpScratchDir, profileDir, }; }