{"version":3,"file":"run-0D4BZdd3.cjs","names":["isCIEnvironment","process","runtime","access","readFile","rm","process","path","homedir","hash","mkdir","writeFile","defaultStudioUrl","createSpinner","pairAgent","path","process","hostname","styleText","PairingCanceledError","KUBB_CONFIG_FILENAME","isCIEnvironment","canUseTTY","prompts","getConfigs","runConnection","setupReporters","logLevelMap","cliReporter","plainLogger","createFileStorage","sendTelemetry","buildTelemetryEvent","toError"],"sources":["../../../internals/utils/src/openInBrowser.ts","../../../internals/utils/src/fs.ts","../src/runners/studio/credentials.ts","../src/runners/studio/run.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\nimport process from 'node:process'\nimport { isCIEnvironment } from './isCIEnvironment.ts'\n\n/**\n * Opens a URL or local file in the default browser, unless running in CI.\n */\nexport function openInBrowser(target: string): void {\n  if (isCIEnvironment()) return\n\n  if (process.platform === 'win32') {\n    execFile('cmd', ['/c', 'start', '', target], () => {})\n  } else {\n    execFile(process.platform === 'darwin' ? 'open' : 'xdg-open', [target], () => {})\n  }\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n *   const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n  if (runtime.isBun) {\n    return Bun.file(path).exists()\n  }\n  return access(path).then(\n    () => true,\n    () => false,\n  )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n  if (runtime.isBun) {\n    return Bun.file(path).text()\n  }\n  return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n  /**\n   * Previously read content, or `null` when the file does not exist.\n   * Omitting this value reads the file before writing.\n   */\n  stored?: string | null\n  /**\n   * When `true`, re-reads the file immediately after writing and throws if the\n   * content does not match — useful for catching write failures on unreliable file systems.\n   */\n  sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n  return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source)         // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source)         // null — file unchanged\n * await write('./src/Pet.ts', '  ')           // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n  const trimmed = data.trim()\n  if (trimmed === '') return null\n\n  const content = `${trimmed}\\n`\n  const resolved = resolve(path)\n  let stored = options.stored\n\n  if (stored === undefined) {\n    if (runtime.isBun) {\n      const file = Bun.file(resolved)\n      stored = (await file.exists()) ? await file.text() : null\n    } else {\n      try {\n        stored = await readFile(resolved, { encoding: 'utf-8' })\n      } catch {\n        /* file doesn't exist yet */\n        stored = null\n      }\n    }\n  }\n  if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n  if (runtime.isBun) {\n    await Bun.write(resolved, content)\n    return content\n  }\n\n  // Creating the directory up front costs a syscall per file, and every file after the first in a\n  // directory pays it for nothing. Write first and only fall back when the directory is missing,\n  // which also stays correct when something removed it mid-run.\n  try {\n    await writeFile(resolved, content, { encoding: 'utf-8' })\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n    await mkdir(dirname(resolved), { recursive: true })\n    await writeFile(resolved, content, { encoding: 'utf-8' })\n  }\n\n  if (options.sanity) {\n    const savedData = await readFile(resolved, { encoding: 'utf-8' })\n    if (savedData !== content) {\n      throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n    }\n    return savedData\n  }\n\n  return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n  return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n  return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts')             // 'petStore'\n * trimExtName('/src/models/pet.ts')      // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts')  // '/project.v2/gen/pet'\n * trimExtName('noExtension')             // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n  const dotIndex = text.lastIndexOf('.')\n  if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n    return text.slice(0, dotIndex)\n  }\n  return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.')   // true  — nested inside the root\n * isPathInside('.', '.')           // true  — the same directory counts as inside\n * isPathInside('.', './src/gen')   // false — the root is not inside its own output\n * isPathInside('../other', '.')    // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n  const resolvedPath = resolve(path)\n  const resolvedParent = resolve(parent)\n  if (resolvedPath === resolvedParent) {\n    return true\n  }\n\n  const rel = relative(resolvedParent, resolvedPath)\n\n  return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { hash } from 'node:crypto'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { clean, read } from '@internals/utils'\n\n/**\n * Root for everything the CLI persists between runs: the paired credential, the machine secret,\n * the last Studio config, and the live-session registry. `KUBB_HOME` relocates all of it.\n */\nfunction getKubbHome(): string {\n  return process.env.KUBB_HOME ?? path.join(homedir(), '.kubb')\n}\n\n/**\n * Root for the credential and machine identity of the current project.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n  const projectId = hash('sha256', project)\n  return path.join(getKubbHome(), 'projects', projectId)\n}\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n  return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n  /**\n   * The Studio instance this machine paired with. A different `--url` means re-pairing.\n   */\n  studioUrl: string\n  /**\n   * Bearer token for this machine. Never logged, never printed.\n   */\n  token: string\n  agentId: string\n  agentSlug: string\n  /**\n   * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n   * once per project instead of on every run.\n   */\n  projects?: Record<string, { allowRead?: boolean; allowWrite?: boolean; allowConfigEdit?: boolean; allowExec?: boolean }>\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n  const raw = await read(getCredentialsPath()).catch(() => null)\n  if (!raw) {\n    return null\n  }\n\n  try {\n    return JSON.parse(raw) as Credentials\n  } catch {\n    return null\n  }\n}\n\n/**\n * Writes the credential with owner-only permissions. The file holds a bearer token, so the mode\n * is set at creation time rather than left to the process umask or fixed up afterward, which\n * would leave the token briefly readable under the umask's default mode.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n  const file = getCredentialsPath()\n\n  await mkdir(path.dirname(file), { recursive: true, mode: 0o700 })\n  await writeFile(file, JSON.stringify(credentials, null, 2), { mode: 0o600 })\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n  await clean(getCredentialsPath())\n}\n","import { hostname } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport * as prompts from '@clack/prompts'\nimport { KUBB_CONFIG_FILENAME } from '@internals/shared'\nimport { isCIEnvironment, openInBrowser, toError } from '@internals/utils'\nimport type { CLIOptions, Config } from '@kubb/core'\nimport { cliReporter, logLevel as logLevelMap } from '@kubb/core'\nimport {\n  createFileStorage,\n  type ClientOptions,\n  defaultStudioUrl,\n  type InvalidAgentTokenError,\n  pairAgent,\n  PairingCanceledError,\n  runConnection,\n  setStorage,\n} from '@kubb/studio'\nimport { buildTelemetryEvent, sendTelemetry } from '../../Telemetry.ts'\nimport { plainLogger } from '../../loggers/plainLogger.ts'\nimport setupReporters from '../../loggers/utils.ts'\nimport { createSpinner, logBlock, logIntro, logOutro, logTip } from '../../loggers/output.ts'\nimport { canUseTTY } from '../../utils/env.ts'\nimport { getConfigs } from '../generate/utils.ts'\nimport { clearCredentials, type Credentials, getCredentialsPath, getProjectKubbHome, readCredentials, writeCredentials } from './credentials.ts'\nimport { version } from '../../../package.json'\n\ntype Permission = 'allowRead' | 'allowWrite' | 'allowConfigEdit' | 'allowExec'\n\nexport type StudioOptions = {\n  /**\n   * Current `@kubb/cli` version, reported to Studio and used for the telemetry payload.\n   */\n  version: string\n  configPath?: string\n  /**\n   * Base URL of the Studio instance, for a self-hosted deployment. Resolved before it reaches here,\n   * since stored credentials are bound to it.\n   */\n  studioUrl: string\n  /**\n   * What Studio may do in this project. Each flag grants outright; the rest are asked once per\n   * project through {@link resolvePermissions}.\n   */\n  permission: Record<Permission, boolean>\n  /**\n   * Whether to open the approval page in a browser during pairing.\n   */\n  autoOpen: boolean\n  logLevel?: CLIOptions['logLevel']\n}\n\nexport type SnapshotOptions = StudioOptions & {\n  /**\n   * Organization CI API key. Falls back to `KUBB_TOKEN`.\n   */\n  token?: string\n  /**\n   * Stable identity for the CI agent. Falls back to CI auto-detection.\n   */\n  id?: string\n  /** Identity of another CI agent to also compare with. Falls back to CI auto-detection. */\n  baseId?: string\n  /**\n   * Package name for the generated tarball. Falls back to the nearest package.json.\n   */\n  name?: string\n  /**\n   * Package version for the generated tarball. Falls back to the nearest package.json.\n   */\n  packageVersion?: string\n  /**\n   * Seconds to wait for the job to finish.\n   */\n  timeout?: number\n  /**\n   * Print the result as one JSON object instead of a summary.\n   */\n  json?: boolean\n}\n\ntype StudioValues = {\n  config?: string\n  url?: string\n  allowRead: boolean\n  allowWrite: boolean\n  allowConfigEdit: boolean\n  allowExec: boolean\n  open?: boolean\n  logLevel?: CLIOptions['logLevel']\n}\n\nexport function createStudioOptions(values: StudioValues): StudioOptions {\n  return {\n    version,\n    configPath: values.config,\n    studioUrl: values.url ?? defaultStudioUrl,\n    permission: {\n      allowRead: values.allowRead,\n      allowWrite: values.allowWrite,\n      allowConfigEdit: values.allowConfigEdit,\n      allowExec: values.allowExec,\n    },\n    autoOpen: values.open ?? true,\n    logLevel: values.logLevel,\n  }\n}\n\ntype LoginOptions = {\n  /**\n   * Aborting this cancels an in-flight pairing request or poll and rejects with\n   * {@link PairingCanceledError}. Wired to `kubb studio`'s shutdown signal, so Ctrl+C during\n   * pairing cancels it instead of leaving the poll running.\n   */\n  signal?: AbortSignal\n  /**\n   * Credentials from before this login. When the fresh pairing resolves to the same agent on the\n   * same Studio, its saved project permissions carry forward instead of being asked again.\n   * Left out for an explicitly requested `kubb studio login`, which always starts clean.\n   */\n  previousCredentials?: Credentials | null\n}\n\n/**\n * Pairs this machine with Studio and stores the resulting token.\n *\n * The CLI holds a code and the browser approves it, rather than the user copying a token out of\n * the UI. The token comes back over the CLI's own HTTPS POST, so it never lands in a URL, a\n * server log, or a `Referer` header.\n */\nexport async function login({ studioUrl, autoOpen }: StudioOptions, { signal, previousCredentials }: LoginOptions = {}): Promise<Credentials> {\n  const spinner = createSpinner()\n  // The spinner only starts once a code is shown.\n  let waiting = false\n\n  try {\n    const { token, agent } = await pairAgent({\n      studioUrl,\n      type: 'cli',\n      name: path.basename(process.cwd()),\n      hostname: hostname(),\n      signal,\n      onCode: (session) => {\n        console.log(`\\nOpen ${styleText('cyan', session.verification_uri)} and approve the code ${styleText('bold', session.user_code)}`)\n\n        if (autoOpen) {\n          openInBrowser(session.verification_uri_complete)\n        }\n\n        spinner.start('Waiting for approval')\n        waiting = true\n      },\n      onRetry: (error) => spinner.message(`Could not reach Kubb Studio, retrying: ${error.message}`),\n    })\n    spinner.stop(`Paired as ${agent.name}`)\n\n    const keepsIdentity = previousCredentials?.studioUrl === studioUrl && previousCredentials.agentId === agent.id\n    const credentials: Credentials = {\n      studioUrl,\n      token,\n      agentId: agent.id,\n      agentSlug: agent.slug,\n      ...(keepsIdentity && previousCredentials?.projects ? { projects: previousCredentials.projects } : {}),\n    }\n    await writeCredentials(credentials)\n\n    console.log(`Credentials stored in ${getCredentialsPath()}`)\n\n    return credentials\n  } catch (error) {\n    if (waiting) {\n      spinner.stop(error instanceof PairingCanceledError ? 'Pairing canceled' : 'Pairing failed')\n    }\n    throw error\n  }\n}\n\n/**\n * What each permission is asked as, in the order the questions appear.\n */\nconst PERMISSIONS: ReadonlyArray<{\n  key: Permission\n  /**\n   * Short label for status output and the connect summary.\n   */\n  label: string\n  question: (project: string, configPath: string) => string\n}> = [\n  {\n    key: 'allowRead',\n    label: 'read generated files',\n    question: () => 'Let Kubb Studio read the files a generation produced?',\n  },\n  { key: 'allowWrite', label: 'write generated files', question: (project) => `Let Kubb Studio write generated files into ${project}?` },\n  {\n    key: 'allowConfigEdit',\n    label: 'edit kubb.config.ts',\n    question: (_project, configPath) => `Let Kubb Studio change plugin options in ${configPath}?`,\n  },\n  {\n    key: 'allowExec',\n    label: 'run formatter, linter, postGenerate',\n    question: () => 'Let Kubb Studio run the formatter, the linter, and output.postGenerate?',\n  },\n]\n\n/**\n * One row per permission, for the connect banner and `kubb studio status`. A list rather than a\n * joined line: four labels this long read as one run-on sentence side by side.\n */\nexport function formatPermissionRows(granted: Record<Permission, boolean>): Array<string> {\n  return PERMISSIONS.map(({ key, label }) => `${granted[key] ? styleText('green', '✔') : styleText('red', '✘')} ${label}`)\n}\n\n/**\n * Resolves Studio permissions for the current project.\n * Flags win, saved answers are reused, and new answers are stored unless `persist` is false.\n */\nexport async function resolvePermissions(\n  options: StudioOptions,\n  credentials: Credentials,\n  configPath: string = KUBB_CONFIG_FILENAME,\n  /**\n   * Stores new answers in `~/.kubb/credentials.json`.\n   * Pass `false` for `KUBB_AGENT_TOKEN` sessions so a temporary token is not written back to disk.\n   */\n  persist = true,\n): Promise<Record<Permission, boolean>> {\n  const project = process.cwd()\n  const remembered = credentials.projects?.[project]\n  const granted: Record<Permission, boolean> = { allowRead: false, allowWrite: false, allowConfigEdit: false, allowExec: false }\n  const answers: Partial<Record<Permission, boolean>> = {}\n  prompts.updateSettings({ withGuide: true })\n\n  for (const { key, question } of PERMISSIONS) {\n    if (options.permission[key] || typeof remembered?.[key] === 'boolean') {\n      granted[key] = options.permission[key] || remembered?.[key] === true\n      continue\n    }\n\n    if (isCIEnvironment() || !canUseTTY()) {\n      granted[key] = false\n      continue\n    }\n\n    granted[key] = (await prompts.confirm({ message: question(project, configPath), initialValue: false })) === true\n    answers[key] = granted[key]\n  }\n  if (persist && Object.keys(answers).length) {\n    await writeCredentials({\n      ...credentials,\n      projects: { ...credentials.projects, [project]: { ...remembered, ...answers } },\n    })\n  }\n\n  return granted\n}\n\n/**\n * Loads the project's Kubb config the same way `kubb generate` does.\n * Returns the first config.\n */\nexport async function loadConfigs(options: StudioOptions): Promise<{ configPath: string; config: Config }> {\n  const { configPath, configs } = await getConfigs({ configPath: options.configPath, logLevel: options.logLevel })\n  const [config] = configs\n\n  if (!config) {\n    throw new Error('Config not defined, create a kubb.config.ts or pass it with --config')\n  }\n\n  // `getConfigs` resolves this to an absolute path. Relativized here, once, so the permission\n  // prompt and the path Studio receives over the wire both show the project-relative form instead\n  // of leaking the local filesystem layout.\n  return { configPath: path.relative(process.cwd(), configPath) || configPath, config }\n}\n\n/**\n * Why a rejected token could not be recovered automatically: it came from `KUBB_AGENT_TOKEN`, no\n * browser is available to re-pair with, or the token this run already re-paired for was rejected\n * again.\n */\ntype RejectedTokenReason = 'envToken' | 'nonInteractive' | 'reauthExhausted'\n\n/**\n * Explains a rejected token to the operator. Builds the error and nothing else, so each caller\n * decides what happens to the credentials.\n */\nfunction explainRejectedToken(error: InvalidAgentTokenError, reason: RejectedTokenReason): Error {\n  if (reason === 'envToken') {\n    return new Error(`${error.message} Pair again and update KUBB_AGENT_TOKEN.`)\n  }\n\n  if (reason === 'reauthExhausted') {\n    return new Error(`${error.message} Studio rejected the newly approved token too. Run \\`kubb studio login\\` and try again.`)\n  }\n\n  return new Error(`${error.message} Run \\`kubb studio login\\` to pair again.`)\n}\n\nexport type PreparedConnection = {\n  configPath: string\n  credentials: Credentials\n  permissions: StudioOptions['permission']\n}\n\n/** Validates the project and resolves pairing and permissions before starting its worker. */\nexport async function prepareConnection(options: StudioOptions, signal?: AbortSignal): Promise<PreparedConnection> {\n  const { configPath } = await loadConfigs(options)\n  const envToken = process.env.KUBB_AGENT_TOKEN\n  const stored = envToken ? null : await readCredentials()\n  const credentials = await (async () => {\n    if (envToken) return { studioUrl: options.studioUrl, token: envToken, agentId: '', agentSlug: '' }\n    if (stored?.studioUrl === options.studioUrl) return stored\n    if (isCIEnvironment()) {\n      throw new Error(`Not paired with ${options.studioUrl}. Set KUBB_AGENT_TOKEN, or run \\`kubb studio login\\` on a machine with a browser.`)\n    }\n    return login(options, { signal })\n  })()\n  const permissions = await resolvePermissions(options, credentials, configPath, !envToken)\n  return { configPath, credentials, permissions }\n}\n\nexport type WorkerState = 'starting' | 'connected' | 'reconnecting' | 'authentication required' | 'stopped'\n\n/** Runs the same project connection in the foreground or in a background worker. */\nexport async function connect(\n  options: StudioOptions,\n  context: {\n    prepared?: PreparedConnection\n    signal?: AbortSignal\n    onState?: (state: WorkerState) => void | Promise<void>\n  } = {},\n): Promise<void> {\n  const shutdown = new AbortController()\n  const signal = context.signal ?? shutdown.signal\n  const stop = () => shutdown.abort()\n  const events = process as unknown as NodeJS.EventEmitter\n  if (!context.signal) {\n    events.once('SIGINT', stop)\n    events.once('SIGTERM', stop)\n  }\n  let hinted = false\n  let reauthenticated = false\n  try {\n    if (!context.prepared) {\n      const { getWorkerStatus } = await import('./background.ts')\n      const worker = await getWorkerStatus()\n      if (worker.running) throw new Error('This project is running in the background. Run `kubb studio stop` first.')\n    }\n    const prepared = context.prepared ?? (await prepareConnection(options, signal))\n    let { credentials, permissions } = prepared\n    const { configPath } = prepared\n    await runConnection({\n      credentials,\n      signal,\n      clientOptions: (): Omit<ClientOptions, 'token' | 'onAuthRequired'> => ({\n        studioUrl: options.studioUrl,\n        configPath,\n        version: options.version,\n        root: process.cwd(),\n        permissions,\n        loadConfig: async () => (await loadConfigs(options)).config,\n        installLogger: async (hooks) => {\n          // The background worker writes to a log file, so it gets the plain logger rather than the\n          // animated one. The logger also covers the `studio:*` events and each generation run.\n          await setupReporters(hooks, {\n            logLevel: logLevelMap[options.logLevel ?? 'info'],\n            reporters: [cliReporter],\n            ...(context.onState ? { logger: plainLogger } : {}),\n          })\n          hooks.hook('studio:connected', () => {\n            if (!hinted && !context.onState && options.logLevel !== 'silent') logBlock(styleText('dim', 'Press Ctrl+C to disconnect'))\n            hinted = true\n          })\n          hooks.hook('studio:ready', async () => {\n            await context.onState?.('connected')\n            if (!context.onState) logTip()\n          })\n          hooks.hook('studio:reconnecting', () => context.onState?.('reconnecting'))\n        },\n      }),\n      onTokenRejected: async ({ error, live }) => {\n        await context.onState?.('authentication required')\n        if (process.env.KUBB_AGENT_TOKEN) throw explainRejectedToken(error, 'envToken')\n        if (!live) await clearCredentials()\n        if (reauthenticated) throw explainRejectedToken(error, 'reauthExhausted')\n        if (context.onState || isCIEnvironment() || !canUseTTY()) throw explainRejectedToken(error, 'nonInteractive')\n        console.log(styleText('yellow', live ? `${error.message} Studio needs you to approve access again.` : `${error.message} Pairing again...`))\n        if (live) await clearCredentials()\n        try {\n          credentials = await login(options, { signal, previousCredentials: credentials })\n        } catch (error) {\n          if (error instanceof PairingCanceledError) {\n            if (live && options.logLevel !== 'silent') logOutro('Disconnected')\n            return null\n          }\n          throw error\n        }\n        reauthenticated = true\n        permissions = await resolvePermissions(options, credentials, configPath, !process.env.KUBB_AGENT_TOKEN)\n        return credentials\n      },\n    })\n    if (!context.onState && signal.aborted && options.logLevel !== 'silent') logOutro('Disconnected')\n  } catch (error) {\n    if (!(error instanceof PairingCanceledError)) throw error\n  } finally {\n    events.off('SIGINT', stop)\n    events.off('SIGTERM', stop)\n  }\n}\n\n/**\n * Reports the paired agent and any saved permissions for the current project.\n */\nexport async function status(options: StudioOptions): Promise<void> {\n  const credentials = await readCredentials()\n\n  if (!credentials) {\n    console.log('Not paired. Run `kubb studio login`.')\n\n    return\n  }\n\n  console.log(`Paired with ${credentials.studioUrl} as ${styleText('cyan', credentials.agentSlug || credentials.agentId)}`)\n\n  if (credentials.studioUrl !== options.studioUrl) {\n    console.log(styleText('yellow', `Connecting to ${options.studioUrl} needs pairing again.`))\n  }\n\n  const remembered = credentials.projects?.[process.cwd()]\n\n  if (!remembered) {\n    console.log(styleText('dim', 'No saved permissions for this project. Run `kubb studio` to connect and choose.'))\n\n    return\n  }\n\n  console.log(styleText('dim', 'Saved permissions'))\n\n  for (const row of formatPermissionRows({\n    allowRead: remembered.allowRead === true,\n    allowWrite: remembered.allowWrite === true,\n    allowConfigEdit: remembered.allowConfigEdit === true,\n    allowExec: remembered.allowExec === true,\n  })) {\n    console.log(row)\n  }\n}\n\n/**\n * Runs a Studio command with shared setup and telemetry reporting.\n */\nexport async function run(options: StudioOptions, action: () => Promise<unknown>, { block = false, json = false } = {}): Promise<void> {\n  // The machine secret lives here and pairing binds it, so storage is installed before anything\n  // reads `getMachineToken()`, which `startPairing` does, before any client exists.\n  setStorage(createFileStorage(getProjectKubbHome()))\n\n  const hrStart = process.hrtime()\n  const report = (status: 'success' | 'failed') => sendTelemetry(buildTelemetryEvent({ command: 'studio', kubbVersion: options.version, hrStart, status }))\n\n  try {\n    if (options.logLevel !== 'silent' && !json) {\n      logIntro({\n        warning: styleText('yellow', 'This feature is still under development, use with caution'),\n        block,\n      })\n    }\n\n    await action()\n\n    await report('success')\n  } catch (error) {\n    await report('failed')\n    console.error(toError(error).message)\n    process.exitCode = 1\n  }\n}\n"],"mappings":"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"}