{"version":3,"sources":["../extension-src/workflower/internals/workflow-orchestration/runtime/artifacts/step-metrics-store.ts"],"sourcesContent":["import type { StepMetrics, WorkflowerConfig } from \"./step-metrics.types\";\nimport type { ModelConfig } from \"@/model-config\";\nimport { promises as fs } from \"fs\";\nimport { resolve } from \"path\";\n\nconst CONFIG_FILE_NAME = \"config.json\";\nconst DEFAULT_WORKFLOWER_DIR = \".workflower\";\n\nlet cachedConfig: WorkflowerConfig | null = null;\n\n/**\n * Gets the config path for a given workflower root directory.\n */\nfunction getConfigPath(workflowerRoot: string): string {\n  const workflowerDir = process.env.WORKFLOWER_DIR || DEFAULT_WORKFLOWER_DIR;\n  return resolve(workflowerRoot, workflowerDir, CONFIG_FILE_NAME);\n}\n\n/**\n * Reads the metricsEnabled setting from the Workflower configuration.\n * Returns safe defaults if the config doesn't exist or has an invalid schema.\n * Results are cached after the first read to avoid repeated file system operations.\n *\n * @param workflowerRoot - The root directory of the Workflower workflow\n * @returns A promise that resolves to the WorkflowerConfig object\n */\nexport async function readConfig(workflowerRoot: string): Promise<WorkflowerConfig> {\n  if (cachedConfig !== null) {\n    return cachedConfig;\n  }\n\n  const configPath = getConfigPath(workflowerRoot);\n\n  let config: ModelConfig | null = null;\n  try {\n    const fileContent = await fs.readFile(configPath, \"utf-8\");\n    config = JSON.parse(fileContent) as ModelConfig;\n  } catch {\n    // File doesn't exist or can't be read - use default\n  }\n\n  // If config is null or metricsEnabled is not a boolean, use default\n  if (!config || typeof config.metricsEnabled !== \"boolean\") {\n    cachedConfig = { metricsEnabled: true };\n    return cachedConfig;\n  }\n\n  cachedConfig = { metricsEnabled: config.metricsEnabled } as WorkflowerConfig;\n  return cachedConfig;\n}\n\n/**\n * Resets the configuration cache.\n * This is primarily used for testing to ensure a fresh config read.\n */\nexport function resetConfigCache(): void {\n  cachedConfig = null;\n}\n\n/**\n * Ensures the metrics directory exists for a given flower.\n * Creates the directory structure: .workflower/workflows-data/<garden>/<flower>/\n *\n * @param flowerPath - Absolute path to the flower's index.json file, which always\n *   lives under `<cwd>/.workflower/workflows/<garden>/<flower>` (see resolveWorkflowsRoot\n *   in workflower-home.ts) — the segment before \"workflows\" is already `.workflower`.\n * @returns Promise resolving to the absolute path of the metrics directory\n */\nexport async function ensureMetricsDir(flowerPath: string): Promise<string> {\n  const normalizedPath = flowerPath.replace(/\\\\/g, \"/\");\n  const parts = normalizedPath.split(\"/\");\n  const workflowsIndex = parts.indexOf(\"workflows\");\n\n  if (workflowsIndex === -1) {\n    throw new Error(`Invalid flower path: 'workflows' directory not found in ${flowerPath}`);\n  }\n\n  const garden = parts[workflowsIndex + 1];\n  const flower = parts[workflowsIndex + 2];\n\n  if (!garden || !flower) {\n    throw new Error(`Cannot extract garden and flower names from path: ${flowerPath}`);\n  }\n\n  const workflowerHome = parts.slice(0, workflowsIndex).join(\"/\");\n  const metricsDir = resolve(workflowerHome, \"workflows-data\", garden, flower);\n\n  await fs.mkdir(metricsDir, { recursive: true });\n  return metricsDir.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Appends step metrics to the metrics.json file for a flower.\n * Creates the file with a single-element array if it doesn't exist.\n * Appends to existing array if the file exists.\n * Uses atomic write pattern (temp file + rename) to prevent corruption.\n *\n * @param flowerPath - Absolute path to the flower's index.json file\n * @param stepMetrics - The step metrics to append\n * @returns Promise that resolves when the metrics have been written\n */\nexport async function appendStepMetrics(\n  flowerPath: string,\n  stepMetrics: StepMetrics,\n): Promise<void> {\n  const metricsDir = await ensureMetricsDir(flowerPath);\n  const metricsFile = resolve(metricsDir, \"metrics.json\");\n\n  let existing: StepMetrics[] = [];\n\n  try {\n    const content = await fs.readFile(metricsFile, \"utf-8\");\n    const parsed = JSON.parse(content);\n    if (Array.isArray(parsed)) {\n      existing = parsed;\n    }\n  } catch {\n    existing = [];\n  }\n\n  const updated = [...existing, stepMetrics];\n\n  const tempFile = `${metricsFile}.tmp.${Date.now()}`;\n  await fs.writeFile(tempFile, JSON.stringify(updated, null, 2));\n  await fs.rename(tempFile, metricsFile);\n}\n"],"mappings":";AAEA,SAAS,YAAY,UAAU;AAC/B,SAAS,eAAe;AAExB,IAAM,mBAAmB;AACzB,IAAM,yBAAyB;AAE/B,IAAI,eAAwC;AAK5C,SAAS,cAAc,gBAAgC;AACrD,QAAM,gBAAgB,QAAQ,IAAI,kBAAkB;AACpD,SAAO,QAAQ,gBAAgB,eAAe,gBAAgB;AAChE;AAUA,eAAsB,WAAW,gBAAmD;AAClF,MAAI,iBAAiB,MAAM;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,cAAc,cAAc;AAE/C,MAAI,SAA6B;AACjC,MAAI;AACF,UAAM,cAAc,MAAM,GAAG,SAAS,YAAY,OAAO;AACzD,aAAS,KAAK,MAAM,WAAW;AAAA,EACjC,QAAQ;AAAA,EAER;AAGA,MAAI,CAAC,UAAU,OAAO,OAAO,mBAAmB,WAAW;AACzD,mBAAe,EAAE,gBAAgB,KAAK;AACtC,WAAO;AAAA,EACT;AAEA,iBAAe,EAAE,gBAAgB,OAAO,eAAe;AACvD,SAAO;AACT;AAMO,SAAS,mBAAyB;AACvC,iBAAe;AACjB;AAWA,eAAsB,iBAAiB,YAAqC;AAC1E,QAAM,iBAAiB,WAAW,QAAQ,OAAO,GAAG;AACpD,QAAM,QAAQ,eAAe,MAAM,GAAG;AACtC,QAAM,iBAAiB,MAAM,QAAQ,WAAW;AAEhD,MAAI,mBAAmB,IAAI;AACzB,UAAM,IAAI,MAAM,2DAA2D,UAAU,EAAE;AAAA,EACzF;AAEA,QAAM,SAAS,MAAM,iBAAiB,CAAC;AACvC,QAAM,SAAS,MAAM,iBAAiB,CAAC;AAEvC,MAAI,CAAC,UAAU,CAAC,QAAQ;AACtB,UAAM,IAAI,MAAM,qDAAqD,UAAU,EAAE;AAAA,EACnF;AAEA,QAAM,iBAAiB,MAAM,MAAM,GAAG,cAAc,EAAE,KAAK,GAAG;AAC9D,QAAM,aAAa,QAAQ,gBAAgB,kBAAkB,QAAQ,MAAM;AAE3E,QAAM,GAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC9C,SAAO,WAAW,QAAQ,OAAO,GAAG;AACtC;AAYA,eAAsB,kBACpB,YACA,aACe;AACf,QAAM,aAAa,MAAM,iBAAiB,UAAU;AACpD,QAAM,cAAc,QAAQ,YAAY,cAAc;AAEtD,MAAI,WAA0B,CAAC;AAE/B,MAAI;AACF,UAAM,UAAU,MAAM,GAAG,SAAS,aAAa,OAAO;AACtD,UAAM,SAAS,KAAK,MAAM,OAAO;AACjC,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,iBAAW;AAAA,IACb;AAAA,EACF,QAAQ;AACN,eAAW,CAAC;AAAA,EACd;AAEA,QAAM,UAAU,CAAC,GAAG,UAAU,WAAW;AAEzC,QAAM,WAAW,GAAG,WAAW,QAAQ,KAAK,IAAI,CAAC;AACjD,QAAM,GAAG,UAAU,UAAU,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC7D,QAAM,GAAG,OAAO,UAAU,WAAW;AACvC;","names":[]}