{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/graphql/syncCookies.ts","../src/lib/requests/pullPrivacyRequests.ts","../src/lib/helpers/readSafe.ts","../src/lib/api-keys/listDirectories.ts","../src/lib/ai/TranscendPromptManager.ts","../src/lib/ai/removeLinks.ts","../src/lib/ai/filterNullishValuesFromObject.ts","../src/lib/ai/getGitFilesThatChanged.ts"],"sourcesContent":["// import { keyBy } from 'lodash-es';\nimport {\n  fetchConsentManagerId,\n  makeGraphQLRequest,\n  UPDATE_OR_CREATE_COOKIES,\n} from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk } from 'lodash-es';\n\nimport { CookieInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\nconst MAX_PAGE_SIZE = 100;\n\n/**\n * Update or create cookies that already existed\n *\n * @param client - GraphQL client\n * @param cookieInputs - List of cookie input\n */\nexport async function updateOrCreateCookies(\n  client: GraphQLClient,\n  cookieInputs: CookieInput[],\n): Promise<void> {\n  const airgapBundleId = await fetchConsentManagerId(client, { logger });\n\n  // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n  // const purposes = await fetchAllPurposes(client);\n  // const purposeNameToId = keyBy(purposes, 'name');\n\n  await mapSeries(chunk(cookieInputs, MAX_PAGE_SIZE), async (page) => {\n    await makeGraphQLRequest(client, UPDATE_OR_CREATE_COOKIES, {\n      variables: {\n        airgapBundleId,\n        cookies: page.map((cookie) => ({\n          name: cookie.name,\n          trackingPurposes:\n            cookie.trackingPurposes && cookie.trackingPurposes.length > 0\n              ? cookie.trackingPurposes\n              : undefined,\n          // TODO: https://transcend.height.app/T-19841 - add with custom purposes\n          // purposeIds: cookie.trackingPurposes\n          //   ? cookie.trackingPurposes\n          //       .filter((purpose) => purpose !== 'Unknown')\n          //       .map((purpose) => purposeNameToId[purpose].id)\n          // : undefined,\n          description: cookie.description,\n          service: cookie.service,\n          status: cookie.status,\n          attributes: cookie.attributes,\n          isRegex: cookie.isRegex,\n          // TODO: https://transcend.height.app/T-23718\n          // owners,\n          // teams,\n        })),\n      },\n      logger,\n    });\n  });\n}\n\n/**\n * Sync the set of cookies from the YML interface into the product\n *\n * @param client - GraphQL client\n * @param cookies - Cookies to sync\n * @returns True upon success, false upon failure\n */\nexport async function syncCookies(client: GraphQLClient, cookies: CookieInput[]): Promise<boolean> {\n  let encounteredError = false;\n  logger.info(colors.magenta(`Syncing \"${cookies.length}\" cookies...`));\n\n  // Ensure no duplicates are being uploaded\n  const notUnique = cookies.filter(\n    (cookie) =>\n      cookies.filter((cook) => cookie.name === cook.name && cookie.isRegex === cook.isRegex)\n        .length > 1,\n  );\n  if (notUnique.length > 0) {\n    throw new Error(\n      `Failed to upload cookies as there were non-unique entries found: ${notUnique\n        .map(({ name }) => name)\n        .join(',')}`,\n    );\n  }\n\n  try {\n    logger.info(colors.magenta(`Upserting \"${cookies.length}\" new cookies...`));\n    await updateOrCreateCookies(client, cookies);\n    logger.info(colors.green(`Successfully synced ${cookies.length} cookies!`));\n  } catch (err) {\n    encounteredError = true;\n    logger.error(colors.red(`Failed to create cookies! - ${err.message}`));\n  }\n\n  return !encounteredError;\n}\n","import { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n  buildTranscendGraphQLClient,\n  createSombraGotInstance,\n  fetchAllRequestIdentifiers,\n  validateSombraVersion,\n  type RequestIdentifier,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests } from '../graphql/index.js';\nimport { formatRequestForCsv, CsvRow, ExportedPrivacyRequest } from './formatRequestForCsv.js';\nimport { splitDateRange } from './splitDateRange.js';\n\n/**\n * Pull down a list of privacy requests\n *\n * @param options - Options\n * @returns The requests with request identifiers and requests formatted for CSV\n */\nexport async function pullPrivacyRequests({\n  auth,\n  sombraAuth,\n  actions = [],\n  statuses = [],\n  identifierSearch,\n  pageLimit = 100,\n  concurrency = 1,\n  transcendUrl = DEFAULT_TRANSCEND_API,\n  createdAtBefore,\n  skipRequestIdentifiers = false,\n  createdAtAfter,\n  updatedAtBefore,\n  updatedAtAfter,\n  isTest,\n}: {\n  /** Transcend API key authentication */\n  auth: string;\n  /** Search for a specific identifier */\n  identifierSearch?: string;\n  /** Sombra API key authentication */\n  sombraAuth?: string;\n  /** API URL for Transcend backend */\n  transcendUrl?: string;\n  /** Statuses to filter on */\n  statuses?: RequestStatus[];\n  /** The request action to fetch */\n  actions?: RequestAction[];\n  /** Page limit when fetching requests */\n  pageLimit?: number;\n  /** Number of parallel date-range chunks */\n  concurrency?: number;\n  /** Filter for requests created before this date */\n  createdAtBefore?: Date;\n  /** Filter for requests created after this date */\n  createdAtAfter?: Date;\n  /** Filter for requests updated before this date */\n  updatedAtBefore?: Date;\n  /** Filter for requests updated after this date */\n  updatedAtAfter?: Date;\n  /** Return test requests */\n  isTest?: boolean;\n  /** Skip fetching request identifier */\n  skipRequestIdentifiers?: boolean;\n}): Promise<{\n  /** All request information with attached identifiers */\n  requestsWithRequestIdentifiers: ExportedPrivacyRequest[];\n  /** Requests that are formatted for CSV */\n  requestsFormattedForCsv: CsvRow[];\n}> {\n  const client = buildTranscendGraphQLClient(transcendUrl, auth);\n  const sombra = await createSombraGotInstance(transcendUrl, auth, {\n    logger,\n    sombraApiKey: sombraAuth,\n    sombraUrl: process.env.SOMBRA_URL,\n  });\n\n  // Log date range\n  let dateRange = '';\n  if (createdAtBefore) {\n    dateRange += ` before ${createdAtBefore.toISOString()}`;\n  }\n  if (createdAtAfter) {\n    dateRange += `${dateRange ? ', and' : ''} after ${createdAtAfter.toISOString()}`;\n  }\n  logger.info(\n    colors.magenta(\n      `${\n        actions.length > 0\n          ? `Pulling requests of type \"${actions.join('\" , \"')}\"`\n          : 'Pulling all requests'\n      }${dateRange}`,\n    ),\n  );\n\n  // Split into parallel date-range chunks when possible\n  const useChunks = concurrency > 1 && createdAtAfter && createdAtBefore;\n  const chunks = useChunks\n    ? splitDateRange(createdAtAfter, createdAtBefore, concurrency)\n    : [{ createdAtAfter, createdAtBefore }];\n\n  if (useChunks) {\n    logger.info(colors.magenta(`Splitting date range into ${concurrency} parallel chunks`));\n  }\n\n  // Fetch requests across all chunks in parallel\n  const chunkResults = await map(\n    chunks,\n    (chunk) =>\n      fetchAllRequests(client, {\n        actions,\n        text: identifierSearch,\n        statuses,\n        createdAtBefore: chunk.createdAtBefore,\n        createdAtAfter: chunk.createdAtAfter,\n        updatedAtBefore,\n        updatedAtAfter,\n        isTest,\n      }),\n    { concurrency: useChunks ? concurrency : 1 },\n  );\n  const requests = chunkResults.flat();\n\n  // Validate Sombra version once before bulk-fetching identifiers\n  if (!skipRequestIdentifiers) {\n    await validateSombraVersion(client, { logger });\n  }\n\n  // Fetch the request identifiers for those requests\n  const requestsWithRequestIdentifiers = skipRequestIdentifiers\n    ? requests.map((request) => ({\n        ...request,\n        requestIdentifiers: [] as RequestIdentifier[],\n      }))\n    : await map(\n        requests,\n        async (request) => {\n          const requestIdentifiers = await fetchAllRequestIdentifiers(client, sombra, {\n            filterBy: { requestId: request.id },\n            skipSombraCheck: true,\n            logger,\n          });\n          return {\n            ...request,\n            requestIdentifiers,\n          };\n        },\n        {\n          concurrency: pageLimit,\n        },\n      );\n\n  logger.info(colors.magenta(`Pulled ${requestsWithRequestIdentifiers.length} requests`));\n\n  const data = requestsWithRequestIdentifiers.map(formatRequestForCsv);\n\n  return { requestsWithRequestIdentifiers, requestsFormattedForCsv: data };\n}\n","import { readFileSync } from 'node:fs';\n\n/**\n * Safely reads the contents of a file as a UTF-8 string.\n * Returns an empty string if the path is not provided or if reading fails.\n *\n * @param p - The path to the file to read.\n * @returns The file contents as a string, or an empty string on error.\n */\nexport function readSafe(p?: string): string {\n  try {\n    return p ? readFileSync(p, 'utf8') : '';\n  } catch {\n    return '';\n  }\n}\n","import { readdirSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * List the folders in a directory\n *\n * @param startDir - The base directory to list from\n * @returns The list of folders in that directory\n */\nexport function listDirectories(startDir: string): string[] {\n  return readdirSync(startDir).filter((entryName) =>\n    statSync(join(startDir, entryName)).isDirectory(),\n  );\n}\n","import {\n  HandlebarsInput,\n  createHandlebarsWithHelpers,\n  type Handlebars,\n} from '@transcend-io/handlebars-utils';\nimport {\n  PromptStatus,\n  ChatCompletionRole,\n  PromptRunProductArea,\n  QueueStatus,\n  LargeLanguageModelClient,\n} from '@transcend-io/privacy-types';\nimport {\n  type Agent,\n  type AgentFile,\n  type AgentFileFilterBy,\n  buildTranscendGraphQLClient,\n  fetchAllAgentFiles,\n  fetchAllAgents,\n  fetchAllLargeLanguageModels,\n  fetchAllPromptThreads,\n  fetchPromptsWithVariables,\n  type LargeLanguageModel,\n  type PromptThread,\n  reportPromptRun,\n  type ReportPromptRunInput,\n  type TranscendPromptTemplated,\n  type TranscendPromptsAndVariables,\n} from '@transcend-io/sdk';\nimport { Secret } from '@transcend-io/secret-value';\n/* eslint-disable max-lines */\nimport { Optionalize, Requirize, apply, decodeCodec, getValues } from '@transcend-io/type-utils';\nimport { mapSeries } from '@transcend-io/utils';\nimport { GraphQLClient } from 'graphql-request';\nimport * as t from 'io-ts';\nimport { groupBy, keyBy, uniq, chunk } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\n\n/**\n * An LLM Prompt definition\n */\nexport type TranscendPrompt<TInputParams extends t.Any, TOutputCodec extends t.Any> = (\n  | {\n      /** ID of the prompt */\n      id: string;\n      /** Title of the prompt  */\n      title?: string;\n    }\n  | {\n      /** ID of the prompt */\n      id?: string;\n      /** Title of the prompt  */\n      title: string;\n    }\n) & {\n  /** The names of the agents that should be loaded along with the prompt */\n  agentNames?: string[];\n  /** Codec to validate runtime input shape */\n  paramCodec: TInputParams;\n  /** Codec to validate output response */\n  outputCodec: TOutputCodec;\n  /**\n   * If the output that needs to be parsed is returned within a tag\n   * e.g. <tag> or <json> - this is the name of the tag\n   */\n  extractFromTag?: string;\n};\n\nexport interface PromptRunResult {\n  /** The ID of the prompt run created on Transcend */\n  promptRunId: string;\n  /** The URL of the prompt run on Transcend */\n  promptRunUrl: string;\n}\n\n/**\n * Create a regex to extract data from a tag\n *\n * Input:\n * \"here is some data <tag>Data</tag>...\"\n *\n * @param tagName - Name of tag\n * @returns Data within the tag\n */\nexport function createRegexForTag(tagName: string): RegExp {\n  return new RegExp(`<${tagName}>([\\\\s\\\\S]+?)<\\\\/${tagName}>`);\n}\n\n/**\n * Helper function to declare prompts\n *\n * @param prompts - Prompt config\n * @returns Prompts as identity function - but type enforced\n */\nexport function defineTranscendPrompts<\n  TPromptNames extends string,\n  TPrompts extends { [k in TPromptNames]: TranscendPrompt<t.Any, t.Any> },\n>(prompts: TPrompts): TPrompts {\n  return prompts;\n}\n\n/**\n * Helper to get the type of the parameter for a given prompt\n */\nexport type GetPromptParamType<\n  TPromptName extends keyof TPrompts,\n  TPrompts extends { [k in TPromptName]: TranscendPrompt<t.Any, t.Any> },\n> = t.TypeOf<TPrompts[TPromptName]['paramCodec']>;\n\n/**\n * Helper to get the type of the parameter for a given prompt\n */\nexport type GetPromptResponseType<\n  TPromptName extends keyof TPrompts,\n  TPrompts extends { [k in TPromptName]: TranscendPrompt<t.Any, t.Any> },\n> = t.TypeOf<TPrompts[TPromptName]['outputCodec']>;\n\n/**\n * Input for reporting a prompt run\n */\nexport interface ReportPromptRunOptions extends Optionalize<\n  Omit<ReportPromptRunInput, 'status' | 'promptId' | 'largeLanguageModelId'>,\n  'name' | 'productArea'\n> {\n  /** The large language model being run. Either the ID of the LLM or the client/name pairing */\n  largeLanguageModel:\n    | string\n    | {\n        /** Name of client */\n        client: LargeLanguageModelClient;\n        /** Name of the model */\n        name: string;\n      };\n}\n\nconst jsonParseSafe = (obj: string): unknown => {\n  try {\n    return JSON.parse(obj);\n  } catch {\n    return obj;\n  }\n};\n\n/**\n * A class that is capable of loading and insert variables into prompts from\n * Transcend's Prompt Manager\n */\nexport class TranscendPromptManager<\n  TPromptNames extends string,\n  TPrompts extends { [k in TPromptNames]: TranscendPrompt<t.Any, t.Any> },\n> {\n  /** Prompt definitions */\n  public prompts: TPrompts;\n\n  /** Options for configuring handlebars */\n  public handlebarsOptions!: HandlebarsInput;\n\n  /** Prompt name -> content map, populated by call to Transcend API */\n  public promptContentMap?: { [k in TPromptNames]: TranscendPromptTemplated };\n\n  /** The large language models that are registered to this organization for reporting */\n  public largeLanguageModels: LargeLanguageModel[] = [];\n\n  /** The agent definitions registered to this organization */\n  private agentsByName: { [name in string]: Agent } = {};\n\n  /** The agent definitions registered to this organization */\n  private agentsByAgentId: { [id in string]: Agent } = {};\n\n  /** The GraphQL client that can be used to call Transcend */\n  public graphQLClient: GraphQLClient;\n\n  /** The set of variables to expose in handlebars context specified at class initiation */\n  public defaultVariables: { [k in string]: unknown };\n\n  /**\n   * The set of variables to expose in handlebars context,\n   * merges defaults with calculated variables from the inventory\n   */\n  public variables: { [k in string]: unknown };\n\n  /** Handlebars compiler */\n  public handlebars: typeof Handlebars;\n\n  /** The Transcend API key */\n  public transcendApiKey: string | Secret<string>;\n\n  /** API of Transcend to call */\n  public transcendUrl: string;\n\n  /** When true, throw an error if the prompt is not approved */\n  public requireApproval: boolean;\n\n  /**\n   * The cache duration in ms for how long prompts and associated metadata should be cached\n   * When undefined - prompts are cached indefinitely unless explicitly re-requested\n   * When 0, prompts and metadata are fetched every time (not recommended)\n   * Setting this to be an hour is a good rate that optimizes for performance and keeping up to date\n   */\n  public cacheDuration?: number;\n\n  /**\n   * The last time the metadata was fetched\n   */\n  public lastUpdatedAt?: Date;\n\n  /**\n   * Constructor\n   *\n   * @param options - Options\n   */\n  constructor({\n    prompts,\n    handlebarsOptions = {},\n    transcendUrl = DEFAULT_TRANSCEND_API,\n    transcendApiKey,\n    requireApproval = true,\n    cacheDuration,\n    defaultVariables = {},\n  }: {\n    /** Prompt definitions to load */\n    prompts: TPrompts;\n    /** Options for configuring handlebars */\n    handlebarsOptions?: HandlebarsInput;\n    /** The Transcend API key */\n    transcendApiKey: string | Secret<string>;\n    /** API of Transcend to call */\n    transcendUrl?: string;\n    /** When true, throw an error if the prompt is not approved */\n    requireApproval?: boolean;\n    /** The set of variables to expose in handlebars context specified at class initiation */\n    defaultVariables?: { [k in string]: unknown };\n    /**\n     * The cache duration in ms for how long prompts and associated metadata should be cached\n     * When undefined - prompts are cached indefinitely unless explicitly re-requested\n     * When 0, prompts and metadata are fetched every time (not recommended)\n     * Setting this to be an hour is a good rate that optimizes for performance and keeping up to date\n     */\n    cacheDuration?: number;\n  }) {\n    this.prompts = prompts;\n    this.transcendUrl = transcendUrl;\n    this.transcendApiKey = transcendApiKey;\n    this.variables = defaultVariables;\n    this.defaultVariables = defaultVariables;\n    this.graphQLClient = buildTranscendGraphQLClient(\n      transcendUrl,\n      typeof transcendApiKey === 'object' ? transcendApiKey.release() : transcendApiKey,\n    );\n    this.requireApproval = requireApproval;\n    this.cacheDuration = cacheDuration;\n    this.handlebarsOptions = handlebarsOptions;\n    this.handlebars = createHandlebarsWithHelpers(handlebarsOptions);\n  }\n\n  /**\n   * Fetch prompt metadata from Transcend and cache to the class definition\n   *\n   * @returns A function that can be used to call the prompt\n   */\n  async fetchPromptsAndMetadata(): Promise<TranscendPromptsAndVariables> {\n    // Determine what to fetch\n    const promptDefinitions = getValues(this.prompts) as TranscendPrompt<t.Any, t.Any>[];\n    const promptIds = promptDefinitions.map(({ id }) => id).filter((x): x is string => !!x);\n    const promptTitles = promptDefinitions\n      .map(({ title }) => title)\n      .filter((x): x is string => !!x);\n    const agentNames = uniq(promptDefinitions.map(({ agentNames }) => agentNames || []).flat());\n\n    // Fetch prompts and data\n    const [response, largeLanguageModels, agents] = await Promise.all([\n      fetchPromptsWithVariables(this.graphQLClient, {\n        logger,\n        filterBy: { ids: promptIds, titles: promptTitles },\n      }),\n      fetchAllLargeLanguageModels(this.graphQLClient, { logger }),\n      fetchAllAgents(this.graphQLClient, { logger, filterBy: { names: agentNames } }),\n    ]);\n    this.agentsByName = keyBy(agents, 'name');\n    this.agentsByAgentId = keyBy(agents, 'agentId');\n    this.largeLanguageModels = largeLanguageModels.filter(\n      (model) => model.isTranscendHosted === false,\n    );\n\n    // Lookup prompts by id/title\n    const promptByTitle = keyBy(response.prompts, 'title');\n    const promptById = keyBy(response.prompts, 'id');\n\n    // Update variables\n    this.variables = {\n      ...response.calculatedVariables.reduce(\n        (acc, v) =>\n          Object.assign(acc, {\n            [v.name]: v.data ? JSON.parse(v.data) : v.data,\n          }),\n        {},\n      ),\n      ...this.defaultVariables,\n    };\n\n    // Update partials\n    this.handlebars = createHandlebarsWithHelpers({\n      ...this.handlebarsOptions,\n      templates: [\n        ...(this.handlebarsOptions.templates || []),\n        ...response.promptPartials.map((partial) => ({\n          name: partial.slug,\n          content: partial.content,\n        })),\n      ],\n    });\n\n    // Create mapping from prompt to content\n    this.promptContentMap = apply(this.prompts, ({ id, title }) => {\n      const result = id ? promptById[id] : title ? promptByTitle[title] : undefined;\n      if (!result) {\n        throw new Error(`Failed to find prompt with title: \"${title}\" and id: \"${id}\"`);\n      }\n      return result;\n    });\n\n    // For cache\n    this.lastUpdatedAt = new Date();\n\n    return response;\n  }\n\n  /**\n   * Get an agent definition by name\n   *\n   * @param name - Name of the agent to grab\n   * @returns Large language model configuration\n   */\n  async getAgentByName(name: string): Promise<Agent | undefined> {\n    const agent = this.agentsByName[name];\n    if (agent) {\n      return agent;\n    }\n    const [remoteAgent] = await fetchAllAgents(this.graphQLClient, {\n      logger,\n      filterBy: { names: [name] },\n    });\n    if (!remoteAgent) {\n      return undefined;\n    }\n    this.agentsByName[remoteAgent.name] = remoteAgent;\n    this.agentsByAgentId[remoteAgent.agentId] = remoteAgent;\n    return remoteAgent;\n  }\n\n  /**\n   * Get a prompt thread by ts\n   *\n   * @param ts - the slack message timestamp to look up thread for\n   * @returns Large language model configuration\n   */\n  async getPromptThreadBySlackTs(ts: string): Promise<PromptThread | undefined> {\n    const [thread] = await fetchAllPromptThreads(this.graphQLClient, {\n      logger,\n      filterBy: { slackMessageTs: [ts] },\n    });\n    return thread;\n  }\n\n  /**\n   * Get a list of agent definitions by name.\n   * Pulls from cache and may return less agents\n   * than requested if some are not found\n   *\n   * @param names - Names of agents to fetch\n   * @returns The agents that were found matching the names\n   */\n  async getAgentsByName(names: string[]): Promise<Agent[]> {\n    if (names.length < 1) {\n      throw new Error('Expected at least one name to be provided');\n    }\n    const { hasCache = [], missingCache = [] } = groupBy(names, (name) =>\n      this.agentsByName[name] ? 'hasCache' : 'missingCache',\n    );\n    const cachedAgents = hasCache.map((name) => this.agentsByName[name]);\n    if (missingCache.length === 0) {\n      return cachedAgents;\n    }\n    const chunkedNames = chunk(missingCache, 50);\n    const remoteAgents: Agent[] = [];\n    await mapSeries(chunkedNames, async (chunkedName) => {\n      const pageOfAgents = await fetchAllAgents(this.graphQLClient, {\n        logger,\n        filterBy: { names: chunkedName },\n      });\n      pageOfAgents.forEach((agent) => {\n        this.agentsByName[agent.name] = agent;\n        this.agentsByAgentId[agent.agentId] = agent;\n      });\n      remoteAgents.push(...pageOfAgents);\n    });\n    return [...cachedAgents, ...remoteAgents];\n  }\n\n  /**\n   * Fetch metadata on agent files\n   *\n   * @param filterBy - Filter by parameters\n   * @returns The files found matching the filter\n   */\n  getAgentFiles(filterBy: AgentFileFilterBy): Promise<AgentFile[]> {\n    return fetchAllAgentFiles(this.graphQLClient, { logger, filterBy });\n  }\n\n  /**\n   * Look up the large language model to report on\n   *\n   * @param largeLanguageModel - Input\n   * @returns Large language model configuration\n   */\n  getLargeLanguageModel(\n    largeLanguageModel: ReportPromptRunOptions['largeLanguageModel'],\n  ): LargeLanguageModel {\n    const matching = this.largeLanguageModels.find((model) =>\n      typeof largeLanguageModel === 'string'\n        ? model.id === largeLanguageModel\n        : model.name === largeLanguageModel.name && model.client === largeLanguageModel.client,\n    );\n    if (!matching) {\n      throw new Error(\n        `Failed to find model matching: ${\n          typeof largeLanguageModel === 'string'\n            ? largeLanguageModel\n            : JSON.stringify(largeLanguageModel)\n        }`,\n      );\n    }\n    return matching;\n  }\n\n  /**\n   * Get a prompt definition by name\n   *\n   * @param promptName - Prompt to compile\n   * @returns Parsed content\n   */\n  async getPromptDefinition<TPromptName extends TPromptNames>(\n    promptName: TPromptName,\n  ): Promise<TranscendPromptTemplated> {\n    // Determine if prompts need to be fetched\n    if (\n      // never been fetched\n      !this.lastUpdatedAt ||\n      // fetch every run\n      this.cacheDuration === 0 ||\n      // If cache duration met\n      (this.cacheDuration && Date.now() - this.lastUpdatedAt.getTime() > this.cacheDuration)\n    ) {\n      await this.fetchPromptsAndMetadata();\n    }\n\n    // Lookup prompt\n    const { promptContentMap } = this;\n    if (!promptContentMap) {\n      throw new Error('Expected this.promptContentMap to be defined');\n    }\n    const promptTemplate = promptContentMap[promptName];\n    if (!promptTemplate) {\n      throw new Error(`Expected this.promptContentMap[${promptName}] to be defined`);\n    }\n    return promptTemplate;\n  }\n\n  /**\n   * Validate the shape of the response from AI\n   *\n   * @param promptName - Prompt to compile\n   * @param params - Runtime parameters\n   * @returns Parsed content\n   */\n  async compilePrompt<TPromptName extends TPromptNames>(\n    promptName: TPromptName,\n    params: t.TypeOf<TPrompts[TPromptName]['paramCodec']>,\n  ): Promise<string> {\n    // Grab the prompt\n    const promptTemplate = await this.getPromptDefinition(promptName);\n    const promptInput = this.prompts[promptName];\n    if (!promptInput) {\n      throw new Error(`Expected this.prompts[${promptName}] to be defined`);\n    }\n\n    // Ensure prompt is approved\n    if (this.requireApproval && promptTemplate.status !== PromptStatus.Approved) {\n      throw new Error(\n        `Assessment \"${promptTemplate.title}\" cannot be used because its in status: \"${promptTemplate.status}\"`,\n      );\n    }\n\n    // If prompt is rejected, throw error\n    if (promptTemplate.status === PromptStatus.Rejected) {\n      throw new Error(\n        `Assessment \"${promptTemplate.title}\" cannot be used because it's in status: \"${promptTemplate.status}\"`,\n      );\n    }\n\n    // Validate params\n    decodeCodec(promptInput.paramCodec, params);\n\n    // Compile prompt and template\n    return this.handlebars.compile(promptTemplate.content)({\n      // template in currentDate by default\n      currentDate: new Date().toISOString(),\n      ...this.variables,\n      ...params,\n    });\n  }\n\n  /**\n   * Validate the shape of the response from AI\n   *\n   * @param promptName - Prompt to parse\n   * @param response - AI response as string\n   * @returns Parsed content\n   */\n  parseAiResponse<TPromptName extends TPromptNames>(\n    promptName: TPromptName,\n    response: string,\n  ): t.TypeOf<TPrompts[TPromptName]['outputCodec']> {\n    // Look up prompt info\n    const promptInput = this.prompts[promptName];\n    if (!promptInput) {\n      throw new Error(`Expected this.prompts[${promptName}] to be defined`);\n    }\n\n    // Extract from tag if needed\n    const extracted = promptInput.extractFromTag\n      ? (createRegexForTag(promptInput.extractFromTag).exec(response) || [])[1] || response\n      : response;\n\n    // Parse via codec\n    return decodeCodec(promptInput.outputCodec, jsonParseSafe(extracted), false);\n  }\n\n  /**\n   * Parse the AI response and  report back to Transcend\n   *\n   * @param promptName - Prompt to parse\n   * @param options - Options for reporting\n   * @returns Parsed content\n   */\n  async reportAndParsePromptRun<TPromptName extends TPromptNames>(\n    promptName: TPromptName,\n    { largeLanguageModel, ...options }: ReportPromptRunOptions,\n  ): Promise<\n    PromptRunResult & {\n      /** Resulting prompt run */\n      result: t.TypeOf<TPrompts[TPromptName]['outputCodec']>;\n    }\n  > {\n    // Determine if prompts need to be fetched\n    if (\n      // never been fetched\n      !this.lastUpdatedAt ||\n      // fetch every run\n      this.cacheDuration === 0 ||\n      // If cache duration met\n      (this.cacheDuration && Date.now() - this.lastUpdatedAt.getTime() > this.cacheDuration)\n    ) {\n      await this.fetchPromptsAndMetadata();\n    }\n\n    const name = options.name || `@transcend-io/cli-prompt-run-${new Date().toISOString()}`;\n\n    if (!this.promptContentMap) {\n      throw new Error('Expected this.promptContentMap to be defined');\n    }\n    // Look up prompt info\n    const promptInput = this.promptContentMap[promptName];\n    if (!promptInput) {\n      throw new Error(`Expected this.prompts[${promptName}] to be defined`);\n    }\n\n    // Ensure the first message in `promptRunMessages` is of type=system\n    if (options.promptRunMessages.length === 0) {\n      throw new Error('promptRunMessages is expected to have length > 0');\n    }\n    if (options.promptRunMessages[0].role !== ChatCompletionRole.System) {\n      throw new Error(`promptRunMessages[0].role is expected to be = ${ChatCompletionRole.System}`);\n    }\n    if (\n      options.promptRunMessages[options.promptRunMessages.length - 1].role !==\n      ChatCompletionRole.Assistant\n    ) {\n      throw new Error(\n        `promptRunMessages[${\n          options.promptRunMessages.length - 1\n        }].role is expected to be = ${ChatCompletionRole.Assistant}`,\n      );\n    }\n    const response = options.promptRunMessages[options.promptRunMessages.length - 1].content;\n\n    let parsed: t.TypeOf<TPrompts[TPromptName]['outputCodec']>;\n    try {\n      // Parse the response\n      parsed = this.parseAiResponse(promptName, response);\n    } catch (err) {\n      await reportPromptRun(\n        this.graphQLClient,\n        {\n          productArea: PromptRunProductArea.PromptManager,\n          ...options,\n          name,\n          error: err.message,\n          status: QueueStatus.Error,\n          ...(typeof largeLanguageModel === 'string'\n            ? { largeLanguageModelId: largeLanguageModel }\n            : {\n                largeLanguageModelName: largeLanguageModel.name,\n                largeLanguageModelClient: largeLanguageModel.client,\n              }),\n          promptId: promptInput.id,\n          promptRunMessages: options.promptRunMessages.map((message, ind) => ({\n            ...message,\n            ...(ind === 0 ? { template: promptInput.content } : {}),\n          })),\n        },\n        { logger },\n      );\n      throw err;\n    }\n\n    // report successful run\n    const promptRunId = await reportPromptRun(\n      this.graphQLClient,\n      {\n        productArea: PromptRunProductArea.PromptManager,\n        ...options,\n        name,\n        status: QueueStatus.Resolved,\n        ...(typeof largeLanguageModel === 'string'\n          ? { largeLanguageModelId: largeLanguageModel }\n          : {\n              largeLanguageModelName: largeLanguageModel.name,\n              largeLanguageModelClient: largeLanguageModel.client,\n            }),\n        promptId: promptInput.id,\n        promptRunMessages: options.promptRunMessages.map((message, ind) => ({\n          ...message,\n          ...(ind === 0 ? { template: promptInput.content } : {}),\n        })),\n      },\n      { logger },\n    );\n\n    return {\n      result: parsed,\n      promptRunId,\n      promptRunUrl: `https://app.transcend.io/prompts/runs/${promptRunId}`,\n    };\n  }\n\n  /**\n   * Report an error for a particular prompt\n   *\n   * @param promptName - Prompt to parse\n   * @param options - Options for reporting\n   * @returns Parsed content\n   */\n  async reportPromptRunError<TPromptName extends TPromptNames>(\n    promptName: TPromptName,\n    { largeLanguageModel, ...options }: Requirize<ReportPromptRunOptions, 'error'>,\n  ): Promise<PromptRunResult> {\n    // Determine if prompts need to be fetched\n    if (\n      // never been fetched\n      !this.lastUpdatedAt ||\n      // fetch every run\n      this.cacheDuration === 0 ||\n      // If cache duration met\n      (this.cacheDuration && Date.now() - this.lastUpdatedAt.getTime() > this.cacheDuration)\n    ) {\n      await this.fetchPromptsAndMetadata();\n    }\n\n    const name = options.name || `@transcend-io/cli-prompt-run-${new Date().toISOString()}`;\n\n    if (!this.promptContentMap) {\n      throw new Error('Expected this.promptContentMap to be defined');\n    }\n    // Look up prompt info\n    const promptInput = this.promptContentMap[promptName];\n    if (!promptInput) {\n      throw new Error(`Expected this.prompts[${promptName}] to be defined`);\n    }\n\n    // Ensure the first message in `promptRunMessages` is of type=system\n    if (options.promptRunMessages.length === 0) {\n      throw new Error('promptRunMessages is expected to have length > 0');\n    }\n    if (options.promptRunMessages[0].role !== ChatCompletionRole.System) {\n      throw new Error(`promptRunMessages[0].role is expected to be = ${ChatCompletionRole.System}`);\n    }\n\n    const promptRunId = await reportPromptRun(\n      this.graphQLClient,\n      {\n        productArea: PromptRunProductArea.PromptManager,\n        ...options,\n        name,\n        status: QueueStatus.Error,\n        ...(typeof largeLanguageModel === 'string'\n          ? { largeLanguageModelId: largeLanguageModel }\n          : {\n              largeLanguageModelName: largeLanguageModel.name,\n              largeLanguageModelClient: largeLanguageModel.client,\n            }),\n        promptId: promptInput.id,\n        promptRunMessages: options.promptRunMessages.map((message, ind) => ({\n          ...message,\n          ...(ind === 0 ? { template: promptInput.content } : {}),\n        })),\n      },\n      { logger },\n    );\n\n    return {\n      promptRunId,\n      promptRunUrl: `https://app.transcend.io/prompts/runs/${promptRunId}`,\n    };\n  }\n}\n/* eslint-enable max-lines */\n","/**\n * Remove links from a string\n *\n * @param inputString - String\n * @returns String without links\n */\nexport function removeLinks(inputString: string): string {\n  const regex = /(https?:\\/\\/[^\\s]+)/g;\n  return inputString.replace(regex, '<link-omitted>');\n}\n","import { ObjByString } from '@transcend-io/type-utils';\n\n/**\n * Given an object, remove all keys that are null-ish\n *\n * @param obj - Object\n * @returns Object with null-ish values removed\n */\nexport function filterNullishValuesFromObject<T extends ObjByString>(obj: T): T {\n  return Object.entries(obj).reduce(\n    (acc, [k, v]) =>\n      v !== null &&\n      v !== undefined &&\n      v !== '' &&\n      !(Array.isArray(v) && v.length === 0) &&\n      !(typeof v === 'object' && Object.keys(v).length === 0)\n        ? Object.assign(acc, { [k]: v })\n        : acc,\n    {} as T,\n  );\n}\n","import { execSync } from 'child_process';\n\nimport fastGlob from 'fast-glob';\nimport { difference } from 'lodash-es';\n\n/**\n * Function thats gets the git files that have changed\n * and returns the code\n *\n * @param options - Options\n * @returns Changes files and diffs\n */\nexport function getGitFilesThatChanged({\n  baseBranch,\n  rootDirectory,\n  githubRepo,\n  excludedGlob = [],\n  fileBlockList = [],\n}: {\n  /** Base branch */\n  baseBranch: string;\n  /** Github repo name */\n  githubRepo: string;\n  /** Root directory */\n  rootDirectory: string;\n  /** A glob that excludes files */\n  excludedGlob?: string[];\n  /** Block list of files to not process */\n  fileBlockList?: string[];\n}): {\n  /** The list of files that changed */\n  changedFiles: string[];\n  /** Github repo name */\n  repoName: string;\n  /** Current commit */\n  commit: string;\n  /** File diffs */\n  fileDiffs: { [k in string]: string };\n} {\n  // Pull base branch\n  execSync(`git fetch origin ${baseBranch}`);\n\n  // Latest commit on base branch. If we are on the base branch, we take the prior commit\n  const latestBasedCommit = execSync(\n    `git ls-remote ${githubRepo} \"refs/heads/${baseBranch}\" | cut -f 1`,\n    { encoding: 'utf-8' },\n  ).split('\\n')[0];\n\n  // This commit\n  const latestThisCommit = execSync('git rev-parse HEAD', {\n    encoding: 'utf-8',\n  }).split('\\n')[0];\n\n  // Ensure commits are present\n  if (!latestBasedCommit || !latestThisCommit) {\n    throw new Error('FAILED TO FIND COMMIT RANGE');\n  }\n\n  // Get the diff between the given branch and base branch\n  const diff = execSync(\n    `git fetch && git diff --name-only \"${\n      baseBranch || latestBasedCommit\n    }...${latestThisCommit}\" -- ${rootDirectory}`,\n    { encoding: 'utf-8' },\n  );\n\n  // Filter out block list\n  const changedFiles = difference(\n    diff.split('\\n').filter((f) => f),\n    fileBlockList,\n  );\n\n  // Filter out globs\n  const filteredChanges =\n    excludedGlob.length > 0 ? fastGlob.sync(changedFiles, { ignore: excludedGlob }) : changedFiles;\n\n  // Get the contents of only the changed files\n  const fileDiffs: { [k in string]: string } = {};\n  filteredChanges.forEach((file) => {\n    const contents = execSync(`git show ${latestThisCommit}:${file}`, {\n      encoding: 'utf-8',\n    });\n    fileDiffs[file] = contents;\n  });\n\n  // Pull the github repo name\n  const repoName = githubRepo.split('/').pop()!.split('.')[0];\n\n  return {\n    changedFiles,\n    fileDiffs,\n    repoName,\n    commit: latestThisCommit,\n  };\n}\n"],"mappings":"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"}