@kubb/studio 5.5.6 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{configFile-BTVeov27.cjs → configFile-dJtYeqX1.cjs} +27 -45
- package/dist/configFile-dJtYeqX1.cjs.map +1 -0
- package/dist/{configFile-BymXUi9P.js → configFile-zhp5DQIe.js} +25 -43
- package/dist/configFile-zhp5DQIe.js.map +1 -0
- package/dist/index.cjs +290 -669
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +54 -62
- package/dist/index.js +292 -670
- package/dist/index.js.map +1 -1
- package/dist/{resolveConfig-D7I3Nj60.cjs → resolveConfig-CjF6j-3y.cjs} +49 -10
- package/dist/resolveConfig-CjF6j-3y.cjs.map +1 -0
- package/dist/{resolveConfig-DMIG_MH-.js → resolveConfig-DOUv_Tid.js} +38 -11
- package/dist/resolveConfig-DOUv_Tid.js.map +1 -0
- package/dist/{snapshotPackage-BwM3LFmZ.cjs → snapshotPackage-BFjiRhGd.cjs} +4 -3
- package/dist/snapshotPackage-BFjiRhGd.cjs.map +1 -0
- package/dist/{snapshotPackage-I8bt8KTR.js → snapshotPackage-BwZ8IJX-.js} +4 -3
- package/dist/snapshotPackage-BwZ8IJX-.js.map +1 -0
- package/dist/url-DC87bYiU.cjs +19 -0
- package/dist/url-DC87bYiU.cjs.map +1 -0
- package/dist/url-DhFfJpxp.js +14 -0
- package/dist/url-DhFfJpxp.js.map +1 -0
- package/package.json +5 -8
- package/dist/configFile-BTVeov27.cjs.map +0 -1
- package/dist/configFile-BymXUi9P.js.map +0 -1
- package/dist/resolveConfig-D7I3Nj60.cjs.map +0 -1
- package/dist/resolveConfig-DMIG_MH-.js.map +0 -1
- package/dist/snapshotPackage-BwM3LFmZ.cjs.map +0 -1
- package/dist/snapshotPackage-I8bt8KTR.js.map +0 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["spawn","detectTool","createStorage","fsDriver","styleText","randomBytes","hash","process","FetchError","delay","MB","createHash","resolve","relative","sep","fsStorage","require","createRequire","isAbsolute","relative","resolve","toPackageName","readFile","Diagnostics","ws","WebSocket","RpcTarget","AGENT_INSTANCE_HEADER","newWebSocketRpcSession","x","detectUncachedTool","path","process","hash","createKubb","Diagnostics","styleText","mkdtemp","path","tmpdir","rm","resolveCacheDir","RpcTarget","process","randomUUID","AgentCloseCode","Hookable","memoryStorage","cacheStorage","kubbVersion","read","version","toPackageName","mergePlugins","mergeAdapter","fsStorage","GENERATION_GONE_MESSAGE","MAX_PAGE_LINES","randomUUID","delay","delay"],"sources":["../../../internals/utils/src/errors.ts","../../../internals/utils/src/promise.ts","../../../internals/utils/src/tools.ts","../../../internals/utils/src/time.ts","../src/operations/machine.ts","../src/operations/api.ts","../src/operations/constants.ts","../package.json","../src/operations/generations.ts","../src/operations/generationEvents.ts","../src/operations/websocket.ts","../src/operations/rpc.ts","../src/operations/hooks.ts","../src/operations/generate.ts","../src/runtime/StudioSession.ts","../src/runtime/runConnection.ts","../src/runtime/client.ts","../src/operations/pair.ts"],"sourcesContent":["/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new Error('Build failed', { cause: toError(err) })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","import { spawn } from 'node:child_process'\n\n/**\n * How one formatter or linter is invoked: the executable, the argv it takes for an output\n * directory, and what to report when it is not installed.\n */\nexport type ToolCommand = {\n command: string\n args: (outputPath: string) => Array<string>\n errorMessage: string\n}\n\n/**\n * CLI command descriptors for each supported code formatter.\n */\nexport const formatters = {\n prettier: {\n command: 'prettier',\n args: (outputPath: string) => ['--ignore-unknown', '--write', outputPath],\n errorMessage: 'Prettier not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['format', '--write', outputPath],\n errorMessage: 'Biome not found',\n },\n oxfmt: {\n command: 'oxfmt',\n args: (outputPath: string) => [outputPath],\n errorMessage: 'Oxfmt not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * CLI command descriptors for each supported linter.\n */\nexport const linters = {\n eslint: {\n command: 'eslint',\n args: (outputPath: string) => [outputPath, '--fix'],\n errorMessage: 'Eslint not found',\n },\n biome: {\n command: 'biome',\n args: (outputPath: string) => ['lint', '--fix', outputPath],\n errorMessage: 'Biome not found',\n },\n oxlint: {\n command: 'oxlint',\n // --no-ignore so oxlint lints the folder even when it's gitignored (generated output dirs usually are).\n args: (outputPath: string) => ['--fix', '--no-ignore', outputPath],\n errorMessage: 'Oxlint not found',\n },\n} as const satisfies Record<string, ToolCommand>\n\n/**\n * Preference order for `format: 'auto'`, most-preferred first. Spelled out rather than taken from\n * the table's key order, which is arbitrary and would silently change what `auto` picks.\n */\nexport const FORMATTER_PREFERENCE = ['oxfmt', 'biome', 'prettier'] as const\n\n/**\n * Preference order for `lint: 'auto'`, most-preferred first.\n */\nexport const LINTER_PREFERENCE = ['oxlint', 'biome', 'eslint'] as const\n\n/**\n * Whether `name` is on PATH and answers `--version` with a zero exit.\n */\nexport function isToolAvailable(name: string): Promise<boolean> {\n return new Promise((resolve) => {\n const child = spawn(name, ['--version'], { stdio: 'ignore' })\n child.on('close', (code) => resolve(code === 0))\n child.on('error', () => resolve(false))\n })\n}\n\n/**\n * Returns the first installed executable from `candidates`, or `null` when none are found.\n *\n * Not memoized: a long-running host that probes repeatedly should cache the result itself, and a\n * `--watch` build should keep noticing a tool installed mid-session.\n */\nexport async function detectTool<TName extends string>(candidates: ReadonlyArray<TName>): Promise<TName | null> {\n for (const candidate of candidates) {\n if (await isToolAvailable(candidate)) {\n return candidate\n }\n }\n\n return null\n}\n\n/**\n * Tokenizes a shell command string, respecting single and double quotes.\n *\n * @example\n * ```ts\n * tokenize('git commit -m \"initial commit\"')\n * // → ['git', 'commit', '-m', 'initial commit']\n * ```\n */\nexport function tokenize(command: string): Array<string> {\n return (command.match(/[^\\s\"']+|\"([^\"]*)\"|'([^']*)'/g) ?? []).map((token) => token.replace(/^[\"']|[\"']$/g, ''))\n}\n","/**\n * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.\n * Rounds to 2 decimal places for sub-millisecond precision without noise.\n *\n * @example\n * ```ts\n * const start = process.hrtime()\n * doWork()\n * getElapsedMs(start) // 42.35\n * ```\n */\nexport function getElapsedMs(hrStart: [number, number]): number {\n const [seconds, nanoseconds] = process.hrtime(hrStart)\n const ms = seconds * 1000 + nanoseconds / 1e6\n return Math.round(ms * 100) / 100\n}\n\n/**\n * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).\n *\n * @example\n * ```ts\n * formatMs(250) // '250ms'\n * formatMs(1500) // '1.50s'\n * formatMs(90000) // '1m 30.0s'\n * ```\n */\nexport function formatMs(ms: number): string {\n if (ms >= 60000) {\n const mins = Math.floor(ms / 60000)\n const secs = ((ms % 60000) / 1000).toFixed(1)\n return `${mins}m ${secs}s`\n }\n\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(2)}s`\n }\n return `${Math.round(ms)}ms`\n}\n","import { hash, randomBytes } from 'node:crypto'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { createStorage, type Storage } from 'unstorage'\nimport fsDriver from 'unstorage/drivers/fs'\n\n/**\n * Key-value storage the runtime uses for its machine secret and the last Studio config.\n *\n * One storage per process, since one process serves one config file. Hosts install their own\n * driver on startup: Nitro passes its `kubb` mount, the CLI an fs driver under `~/.kubb/cache`.\n * The in-memory default keeps the runtime usable without a host, at the cost of a machine\n * identity that changes on every restart.\n */\nlet storage: Storage = createStorage()\nlet hasInstalledStorage = false\n\n/**\n * Installs the storage driver the runtime persists to. Call once, before connecting.\n */\nexport function setStorage(next: Storage): void {\n storage = next\n hasInstalledStorage = true\n}\n\n/**\n * A storage backed by files under `base`, so the machine secret and the last Studio config\n * survive a restart. Repeated pairings of one machine depend on that secret staying put.\n */\nexport function createFileStorage(base: string): Storage {\n return createStorage({ driver: fsDriver({ base }) })\n}\n\nlet fallbackSecretPromise: Promise<string> | null = null\n\n/**\n * Loads the fallback machine secret from the runtime storage.\n * On first use it generates a secret and persists it, so the machine identity stays\n * stable across restarts. An identity that changes on every boot breaks session\n * creation with Studio whenever the startup registration call fails.\n */\nasync function loadOrCreateFallbackSecret(): Promise<string> {\n // The secret is memoized for the life of the process, so a host that reads the machine token\n // before installing its storage is bound to the throwaway in-memory default. Nothing else\n // surfaces that: the write succeeds, and the identity silently changes on every restart, which\n // Studio rejects with a 403 on the next session create.\n if (!hasInstalledStorage) {\n console.warn(\n styleText('yellow', 'Deriving the machine token before a storage driver was installed'),\n 'call setStorage() first, or set KUBB_AGENT_SECRET, to keep a stable machine identity across restarts',\n )\n }\n\n const stored = await storage.getItem('machine-secret').catch(() => null)\n\n if (typeof stored === 'string' && stored) {\n return stored\n }\n\n const secret = randomBytes(32).toString('hex')\n\n await storage.setItem('machine-secret', secret).catch(() => {\n console.warn(\n styleText('yellow', 'Could not persist the generated machine secret'),\n 'set KUBB_AGENT_SECRET to keep a stable machine identity across restarts',\n )\n })\n\n return secret\n}\n\n/**\n * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its\n * own identity without duplicating `getMachineToken`'s SHA-256 step.\n */\nexport function machineTokenFrom(secret: string): string {\n return hash('sha256', secret)\n}\n\n/**\n * Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.\n * Falls back to a generated secret persisted in the runtime storage if the env var is not set.\n * The token is hashed with SHA-256.\n */\nexport async function getMachineToken(): Promise<string> {\n if (process.env.KUBB_AGENT_SECRET) {\n return machineTokenFrom(process.env.KUBB_AGENT_SECRET)\n }\n\n fallbackSecretPromise ??= loadOrCreateFallbackSecret()\n\n return machineTokenFrom(await fallbackSecretPromise)\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { getErrorMessage } from '@internals/utils'\nimport { FetchError, ofetch } from 'ofetch'\nimport type { AgentCapacity, AgentRegisterInput, AgentRegisterResponse } from '../protocol/index.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * Reads a human-readable message from a Studio JSON error body, when it has one. `FetchError`'s own\n * message stops at the status line, so the detail Studio sends with a failure (an agent limit, a\n * revoked token) would otherwise never reach the user.\n */\nfunction responseMessage(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') {\n return undefined\n }\n\n const body = data as { error_description?: unknown; message?: unknown; error?: unknown }\n for (const value of [body.error_description, body.message, body.error]) {\n if (typeof value === 'string' && value) {\n return value\n }\n }\n\n return undefined\n}\n\n/**\n * Thrown when Studio rejects the agent token itself (401). Retrying cannot help: the token was\n * revoked, or the agent it belonged to was deleted in the Studio UI. Hosts catch this to forget\n * the stored credential and pair again.\n */\nexport class InvalidAgentTokenError extends Error {\n constructor(studioUrl: string, options?: ErrorOptions) {\n super(`Kubb Studio rejected this agent's token. It was revoked or the agent was deleted in ${studioUrl}.`, options)\n this.name = 'InvalidAgentTokenError'\n }\n}\n\n/**\n * Thrown when Studio refuses this agent's protocol version (426). Retrying cannot help until the\n * agent is upgraded, so hosts stop instead of reconnecting.\n */\nexport class IncompatibleAgentError extends Error {\n constructor(studioUrl: string, detail?: string, options?: ErrorOptions) {\n super(`Kubb Studio at ${studioUrl} requires a newer agent${detail ? `: ${detail}` : ''}. Upgrade @kubb/studio or the Kubb agent image.`, options)\n this.name = 'IncompatibleAgentError'\n }\n}\n\n/**\n * Whether a thrown value carries `statusCode`. Not narrowed to `FetchError`: a host wrapper can\n * throw its own error shape with the same field.\n */\nfunction rejectedWith(error: unknown, statusCode: number): boolean {\n return (error as { statusCode?: number } | undefined)?.statusCode === statusCode\n}\n\nfunction registrationError(cause: unknown): Error {\n const detail = (cause instanceof FetchError ? responseMessage(cause.data) : undefined) ?? getErrorMessage(cause)\n return new Error(detail ? `Failed to register with Kubb Studio: ${detail}` : 'Failed to register with Kubb Studio', { cause })\n}\n\ntype RegisterProps = {\n studioUrl: string\n token: string\n /** Names this agent process, so Studio tells two processes sharing one token apart. */\n instanceId: string\n capacity: AgentCapacity\n signal?: AbortSignal\n}\n\n/**\n * Registers this agent process with Kubb Studio (`POST /api/agent/connect`): binds the machine\n * identity to the token, reports what the process can take on, and gets back the URL of the one\n * socket it keeps open.\n *\n * Makes one bounded attempt; `runConnection` owns retries. A rejected token (401) throws\n * {@link InvalidAgentTokenError} and an unsupported agent version (426) throws\n * {@link IncompatibleAgentError}, since retrying either cannot help.\n */\nexport async function registerAgent({ token, studioUrl, instanceId, capacity, signal }: RegisterProps): Promise<AgentRegisterResponse> {\n const body: AgentRegisterInput = { machineToken: await getMachineToken(), instanceId, capacity }\n\n try {\n return await ofetch<AgentRegisterResponse>(`${studioUrl}/api/agent/connect`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body,\n retry: 0,\n timeout: 10_000,\n signal,\n })\n } catch (error) {\n if (rejectedWith(error, 401)) throw new InvalidAgentTokenError(studioUrl, { cause: error })\n if (rejectedWith(error, 426))\n throw new IncompatibleAgentError(studioUrl, error instanceof FetchError ? responseMessage(error.data) : undefined, { cause: error })\n throw registrationError(error)\n }\n}\n\n/**\n * Status values returned by Studio's jobs API.\n */\nexport type StudioJobStatus = 'queued' | 'running' | 'success' | 'failed' | 'canceled'\n\n/** How a snapshot's files differ from an earlier snapshot of the same package, relative to `output.path`. */\nexport type StudioSnapshotChanges = {\n /** The snapshot these changes are measured against, `null` when there is none to compare with. */\n base: {\n id: string\n version: string | null\n /** The commit the base snapshot was built from, when reported. */\n commit?: string\n createdAt: string\n } | null\n added: Array<string>\n changed: Array<string>\n removed: Array<string>\n}\n\n/**\n * `StudioSnapshotChanges` against a pull request's base branch. `baseFound` tells apart the two\n * reasons `base` can be `null`: `false` when no CI agent is registered for the branch yet (the\n * workflow has never run there), `true` when that agent exists but has no snapshot of this package\n * to compare with.\n */\nexport type StudioBranchSnapshotChanges = StudioSnapshotChanges & { baseFound: boolean }\n\n/**\n * Package view returned on a successful snapshot job from Studio.\n */\nexport type StudioSnapshot = {\n /**\n * Immutable snapshot id.\n */\n id: string\n /**\n * npm package name, or `null` when Studio stored none.\n */\n name: string | null\n /**\n * npm package version, or `null` when Studio stored none.\n */\n version: string | null\n /**\n * Subresource integrity hash for the tarball, or `null` when unavailable.\n */\n integrity: string | null\n /**\n * Preferable download path, often the readable `/packages/{agentSlug}/{name}.tgz` form.\n */\n url: string\n /**\n * Stable download path keyed by snapshot id.\n */\n snapshotIdUrl: string\n /**\n * ISO timestamp after which Studio may delete the tarball.\n */\n expiresAt: string\n /** What changed since the previous snapshot on the same agent. Absent when Studio or the agent predates it. */\n changes?: StudioSnapshotChanges\n /** What differs from the latest snapshot of the CI agent `baseId` names. Absent without a base. */\n branchChanges?: StudioBranchSnapshotChanges\n}\n\n/**\n * Job record from `POST /api/jobs` and `GET /api/jobs/{id}`.\n */\nexport type StudioJob = {\n /**\n * Job id returned by Studio when the job was queued.\n */\n id: string\n /**\n * Current status. Poll until `success`, `failed`, or `canceled`.\n */\n status: StudioJobStatus\n /**\n * Failure message when `status` is `failed`.\n */\n error?: string\n /**\n * Package view when a snapshot job finished successfully.\n */\n snapshot?: StudioSnapshot\n}\n\n/**\n * First wait before `createJob` retries a busy or queue-full response, absent a `Retry-After` hint.\n */\nconst CREATE_JOB_INITIAL_DELAY_MS = 1_000\n\n/**\n * Slowest `createJob` backs off to between retries.\n */\nconst CREATE_JOB_MAX_INTERVAL_MS = 10_000\n\n/**\n * Statuses worth retrying: the agent has no free connection yet (409, a stale conflict a moment\n * later resolves), its queue is momentarily full (429), or it has no live connection at all yet\n * (503, an agent process that is mid-reconnect). Anything else (404 agent not found, 401/403 auth)\n * is thrown straight away, since retrying cannot change the outcome.\n */\nconst CREATE_JOB_RETRYABLE_STATUSES = new Set([409, 429, 503])\n\n/**\n * Reads Studio's `Retry-After` header (seconds) off a thrown `ofetch` error, when present.\n */\nfunction retryAfterMs(error: unknown): number | undefined {\n const seconds = Number((error as { response?: Response }).response?.headers.get('retry-after'))\n return Number.isFinite(seconds) && seconds > 0 ? seconds * 1_000 : undefined\n}\n\n/**\n * Adds up to 30% jitter, so every CI run queued behind the same busy agent does not retry in\n * lockstep.\n */\nfunction withJitter(ms: number): number {\n return ms + Math.random() * ms * 0.3\n}\n\n/**\n * Queues a generation or snapshot job on Studio (`POST /api/jobs`).\n *\n * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.\n * Authenticates with the organization CI API key via `x-api-key`.\n *\n * A busy agent, a full queue, or a momentary lack of a live connection (409, 429, 503) retries with\n * exponential backoff and jitter, honoring Studio's `Retry-After` header when it sends one, up to\n * `timeoutMs`. Every other failure, including a missing agent (404), throws immediately.\n *\n * @example Snapshot job\n * ```ts\n * const job = await createJob({\n * studioUrl: 'https://kubb.studio',\n * token: process.env.KUBB_TOKEN!,\n * type: 'snapshot',\n * agentId: agent.id,\n * name: '@kubb/demo',\n * version: '1.0.0',\n * })\n * const finished = await waitForJob({ studioUrl, token, id: job.id })\n * ```\n */\nexport async function createJob({\n studioUrl,\n token,\n type,\n agentId,\n name,\n version,\n commit,\n baseId,\n config,\n instanceId,\n timeoutMs = 60_000,\n signal,\n}: {\n studioUrl: string\n token: string\n type: 'generation' | 'snapshot'\n agentId: string\n name?: string\n version?: string\n /** The commit this snapshot is built from, so the next one can diff against it. */\n commit?: string\n /** The `id` another CI agent's runs register under, such as the base branch's; this snapshot is also compared with its latest one. */\n baseId?: string\n config?: Record<string, unknown>\n /**\n * The agent process to run the job on, the `instanceId` its client connected with. A CI run passes\n * its own, so an overlapping pipeline under the same CI agent never builds its checkout.\n */\n instanceId?: string\n /**\n * How long to keep retrying a busy or queue-full response before giving up, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n signal?: AbortSignal\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = CREATE_JOB_INITIAL_DELAY_MS\n\n for (;;) {\n signal?.throwIfAborted()\n\n try {\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { type, agentId, name, version, commit, baseId, config, instanceId },\n retry: false,\n timeout: Math.max(deadline - Date.now(), 1),\n signal,\n })\n\n return job\n } catch (error) {\n signal?.throwIfAborted()\n\n const status = (error as { response?: { status?: number } }).response?.status\n if (!status || !CREATE_JOB_RETRYABLE_STATUSES.has(status) || Date.now() >= deadline) throw error\n\n const wait = Math.min(retryAfterMs(error) ?? withJitter(interval), Math.max(deadline - Date.now(), 0))\n if (signal) {\n await delay(wait, undefined, { signal })\n } else {\n await new Promise((resolve) => setTimeout(resolve, wait))\n }\n if (Date.now() >= deadline) throw error\n interval = Math.min(interval * 2, CREATE_JOB_MAX_INTERVAL_MS)\n }\n }\n}\n\n/**\n * A job runs a generation and packs a tarball, so it is never done the instant it is queued.\n */\nconst INITIAL_POLL_DELAY_MS = 2_000\n\n/**\n * Slowest the poll backs off to. Requests per run are roughly `timeoutMs` divided by this, and\n * every concurrent run on the same organization key draws on one budget.\n */\nconst MAX_POLL_INTERVAL_MS = 30_000\n\n/**\n * Polls `GET /api/jobs/{id}` until the job reaches a terminal status, waiting\n * {@link INITIAL_POLL_DELAY_MS} first and doubling up to {@link MAX_POLL_INTERVAL_MS} so a long\n * job stays inside the API key's rate limit.\n *\n * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the\n * deadline passes before Studio finishes.\n */\nexport async function waitForJob({\n studioUrl,\n token,\n id,\n timeoutMs = 60_000,\n signal,\n}: {\n studioUrl: string\n token: string\n id: string\n /**\n * How long to keep polling before throwing, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n signal?: AbortSignal\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = INITIAL_POLL_DELAY_MS\n\n for (;;) {\n signal?.throwIfAborted()\n\n const wait = Math.max(Math.min(interval, deadline - Date.now()), 0)\n if (signal) {\n await delay(wait, undefined, { signal })\n } else {\n await new Promise((resolve) => setTimeout(resolve, wait))\n }\n\n if (Date.now() >= deadline) throw new Error('Timed out waiting for the Studio job')\n\n interval = Math.min(interval * 2, MAX_POLL_INTERVAL_MS)\n\n try {\n // ofetch retries a 429 immediately, which spends the rate limit faster than not retrying.\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs/${id}`, {\n headers: { 'x-api-key': token },\n retry: false,\n timeout: Math.max(deadline - Date.now(), 1),\n signal,\n })\n\n if (job.status === 'success' || job.status === 'failed' || job.status === 'canceled') return job\n } catch (error) {\n signal?.throwIfAborted()\n\n const response = (error as { response?: { status?: number; _data?: { data?: { tryAgainIn?: unknown } } } }).response\n\n if (response?.status !== 429) throw error\n\n const retryAfter = response._data?.data?.tryAgainIn\n const usable = typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter > 0\n\n // Studio's wait may exceed the ceiling, and a refusal must never shorten the next poll.\n interval = Math.max(interval, usable ? retryAfter : MAX_POLL_INTERVAL_MS)\n }\n }\n}\n\n/**\n * CI agent returned by {@link createAgent}. The token is issued only once, at creation or reuse.\n */\nexport type StudioAgent = {\n /**\n * Agent id, passed to {@link createJob} as `agentId`.\n */\n id: string\n /**\n * Human-readable slug, used to build the readable snapshot URL and the agent's Studio page.\n */\n slug: string\n /**\n * Agent display name.\n */\n name: string\n /**\n * Bearer token for the WebSocket agent session. Mask it before logging.\n */\n token: string\n}\n\n/**\n * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.\n * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of\n * consuming a new one from the organization's agent limit.\n */\nexport async function createAgent({\n studioUrl,\n token,\n name,\n machineToken,\n}: {\n studioUrl: string\n token: string\n name: string\n machineToken: string\n}): Promise<StudioAgent> {\n try {\n return await ofetch<StudioAgent>(`${studioUrl}/api/agents`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { name, machineToken },\n })\n } catch (error: unknown) {\n if (error instanceof FetchError) {\n const upgradeUrl = (error.data as { data?: { upgradeUrl?: string } } | undefined)?.data?.upgradeUrl\n const detail = responseMessage(error.data) ?? getErrorMessage(error)\n const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : ''\n throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error })\n }\n\n throw error\n }\n}\n","import type { AgentCapacity } from '../protocol/index.ts'\n\n/**\n * Hosted Kubb Studio URL. Exported so credential stores can bind tokens to the resolved instance,\n * not whatever default the client would pick on its own.\n */\nexport const defaultStudioUrl = 'https://kubb.studio'\n\n/**\n * Defaults the Studio client uses when a host passes nothing.\n * Config path is left out on purpose: each host discovers that itself.\n */\nexport const agentDefaults = {\n studioUrl: defaultStudioUrl,\n retryIntervalMs: 30_000,\n heartbeatIntervalMs: 30_000,\n /**\n * Slowest heartbeat a host may ask for. Studio drops an agent from the active list once its\n * stored ping is older than its liveness window, and it stores a ping at most once a minute, so\n * a slower cadence would make a healthy agent look dead after a single missed ping.\n */\n maxHeartbeatIntervalMs: 60_000,\n /** How long a heartbeat ping may take before the session is treated as dead. */\n heartbeatTimeoutMs: 10_000,\n maxConcurrent: 1,\n maxGenerations: 8,\n maxGenerationsMb: 100,\n maxSnapshotMb: 50,\n} as const\n\nfunction positiveNumber(value: string | undefined): number | undefined {\n const parsed = Number(value)\n return value && Number.isFinite(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/**\n * How many generations an agent keeps and how large they may get, read from\n * `KUBB_AGENT_MAX_GENERATIONS`, `KUBB_AGENT_MAX_GENERATIONS_MB` and `KUBB_AGENT_MAX_SNAPSHOT_MB`.\n * An unset or invalid value keeps the default.\n */\nexport function resolveGenerationLimits(env: NodeJS.ProcessEnv = process.env): { maxCount: number; maxMb: number; maxSnapshotMb: number } {\n return {\n maxCount: Math.max(1, Math.floor(positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS) ?? agentDefaults.maxGenerations)),\n maxMb: positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS_MB) ?? agentDefaults.maxGenerationsMb,\n maxSnapshotMb: positiveNumber(env.KUBB_AGENT_MAX_SNAPSHOT_MB) ?? agentDefaults.maxSnapshotMb,\n }\n}\n\n/**\n * An agent's capacity read from `KUBB_AGENT_MAX_CONCURRENT`. An unset or invalid value keeps the\n * default: one job at a time.\n */\nexport function resolveAgentCapacity(env: NodeJS.ProcessEnv = process.env): AgentCapacity {\n return {\n maxConcurrent: Math.max(1, Math.floor(positiveNumber(env.KUBB_AGENT_MAX_CONCURRENT) ?? agentDefaults.maxConcurrent)),\n }\n}\n","","import { createHash } from 'node:crypto'\nimport { relative, resolve, sep } from 'node:path'\nimport { inParallel } from '@internals/utils'\nimport { fsStorage, type Storage } from '@kubb/core'\nimport type { FilePage, ReadFilesResult } from '../protocol/index.ts'\n\nconst READ_CONCURRENCY = 50\nconst MB = 1024 * 1024\nconst INDEX_KEY = 'studio/generations.json'\n\n/**\n * Files a run produced, or the output directory held, keyed by path relative to `root`.\n */\nexport type SourceFiles = { storage: Storage; root: string; paths: Set<string> }\n\n/**\n * Which set of a generation to read: what the run produced, or the output directory before it ran.\n */\nexport type GenerationSource = 'output' | 'disk'\n\n/**\n * A set as kept in the store.\n */\ntype KeptSet = {\n /**\n * The paths a read is checked against. Empty when the set was too large to keep its content.\n */\n paths: Array<string>\n hashes: Record<string, string>\n bytes: number\n}\n\nexport type KeptGeneration = {\n jobId: string\n /** When the store took it, in epoch milliseconds. Absent on an index written before it existed. */\n keptAt?: number\n output: KeptSet\n disk?: KeptSet\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nconst hashOf = (content: string) => createHash('sha1').update(content).digest('hex').slice(0, 16)\n\n/**\n * What the output directory holds on disk before a run. `undefined` when `outputPath` is not a real\n * subdirectory of `root` (listing the root would take in every source file) or holds more than `maxFiles`.\n */\nexport async function listDisk({ root, outputPath, maxFiles }: { root: string; outputPath: string; maxFiles: number }): Promise<SourceFiles | undefined> {\n const outputDir = resolve(root, outputPath)\n const fromRoot = relative(resolve(root), outputDir)\n if (!fromRoot || fromRoot.startsWith('..') || fromRoot.startsWith(sep)) return undefined\n\n const storage = fsStorage()\n const keys = await storage.readKeys(outputDir)\n if (keys.length > maxFiles) return undefined\n\n const paths = keys\n .filter((key) => !key.split('/').includes('node_modules'))\n .map((key) => relative(resolve(root), resolve(outputDir, key)).replaceAll('\\\\', '/'))\n return { storage, root, paths: new Set(paths) }\n}\n\n/**\n * Keeps recent generations by job id in `storage`, with an index next to them so a store on disk\n * survives a restart. A generation is only ever looked up by its job id, so on a pooled sandbox one\n * tenant never reaches another's output. The oldest go past `maxCount` or `maxMb`, but the newest\n * always stays.\n */\nexport function createGenerationStore({\n storage,\n maxCount,\n maxMb,\n ttlMs,\n now = Date.now,\n}: {\n storage: Storage\n maxCount: number\n maxMb: number\n /** How long a generation stays readable. Unset, it stays until count or size pushes it out. */\n ttlMs?: number\n now?: () => number\n}) {\n let index: Array<KeptGeneration> | undefined\n\n // An entry from before `keptAt` existed never expires, so upgrading drops nothing.\n const isLive = (generation: KeptGeneration) => ttlMs === undefined || generation.keptAt === undefined || now() - generation.keptAt < ttlMs\n\n // Hashed so a job id from the wire can never point outside the store.\n const dirOf = (jobId: string) => `studio/generations/${hashOf(jobId)}/`\n\n async function load(): Promise<Array<KeptGeneration>> {\n if (index) return index\n const stored = await storage.readItem(INDEX_KEY).catch(() => null)\n try {\n index = stored ? (JSON.parse(stored) as Array<KeptGeneration>) : []\n } catch {\n index = []\n }\n return index\n }\n\n /**\n * Copies `files` into the store as one set of `jobId`. Above `maxSetMb` only the hashes are kept.\n */\n async function keep({ jobId, source, files, maxSetMb }: { jobId: string; source: GenerationSource; files: SourceFiles; maxSetMb: number }): Promise<KeptSet> {\n const maxSetBytes = maxSetMb * MB\n const hashes: Record<string, string> = {}\n let bytes = 0\n await inParallel({\n items: [...files.paths],\n limit: READ_CONCURRENCY,\n run: async (path) => {\n // Output outside the root would land outside the store too.\n if (path.split('/').includes('..')) return\n const content = await files.storage.readItem(resolve(files.root, path))\n if (content === null) return\n hashes[path] = hashOf(content)\n bytes += Buffer.byteLength(content)\n if (bytes <= maxSetBytes) await storage.writeItem(`${dirOf(jobId)}${source}/${path}`, content)\n },\n })\n if (bytes > maxSetBytes) {\n await storage.empty(`${dirOf(jobId)}${source}/`)\n return { paths: [], hashes, bytes: 0 }\n }\n return { paths: Object.keys(hashes), hashes, bytes }\n }\n\n async function drop(jobId: string): Promise<void> {\n await storage.empty(dirOf(jobId))\n }\n\n return {\n keep,\n drop,\n get: async (jobId: string) => (await load()).find((generation) => generation.jobId === jobId && isLive(generation)),\n latest: async () => (await load()).filter(isLive).at(-1),\n /** Total bytes of every set the store holds, expired ones too until the next add drops them. */\n bytes: async () => (await load()).reduce((sum, { output, disk }) => sum + output.bytes + (disk?.bytes ?? 0), 0),\n async add(generation: KeptGeneration): Promise<void> {\n const current = await load()\n for (const expired of current.filter((entry) => !isLive(entry))) await drop(expired.jobId)\n const entries = current.filter((entry) => entry.jobId !== generation.jobId && isLive(entry))\n entries.push({ ...generation, keptAt: now() })\n const weight = () => entries.reduce((sum, { output, disk }) => sum + output.bytes + (disk?.bytes ?? 0), 0)\n while (entries.length > 1 && (entries.length > maxCount || weight() > maxMb * MB)) {\n await drop(entries.shift()!.jobId)\n }\n index = entries\n await storage.writeItem(INDEX_KEY, JSON.stringify(entries))\n },\n /**\n * Reads the requested paths the set holds, skipping any it does not, one page of lines when `limit` is given.\n */\n async read({\n generation,\n source,\n paths,\n cursor = 0,\n limit,\n }: {\n generation: KeptGeneration\n source: GenerationSource\n paths: Array<string>\n cursor?: number\n limit?: number\n }): Promise<ReadFilesResult> {\n const kept = new Set(generation[source]?.paths)\n const files: Record<string, string> = {}\n const pages: Record<string, FilePage> = {}\n await inParallel({\n items: paths.filter((path) => kept.has(path)),\n limit: READ_CONCURRENCY,\n run: async (path) => {\n const content = await storage.readItem(`${dirOf(generation.jobId)}${source}/${path}`)\n if (content === null) return\n if (limit === undefined) {\n files[path] = content\n return\n }\n const lines = content.split(/\\r\\n|\\r|\\n/)\n const end = Math.min(cursor + limit, lines.length)\n files[path] = lines.slice(cursor, end).join('\\n')\n pages[path] = { nextCursor: end < lines.length ? end : null, totalLines: lines.length }\n },\n })\n return limit === undefined ? { files } : { files, pages }\n },\n }\n}\n\nexport type GenerationStore = ReturnType<typeof createGenerationStore>\n","import { readFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { isAbsolute, relative, resolve } from 'node:path'\nimport { getElapsedMs } from '@internals/utils'\nimport { Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport type { GenerationEvent, GenerationEventPayloads, GenerationEventType } from '../protocol/index.ts'\nimport type { SourceFiles } from './generations.ts'\nimport { toPackageName } from './resolveConfig.ts'\n\nconst require = createRequire(import.meta.url)\n\nfunction relativeStoragePath(root: string, filePath: string): string {\n return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll('\\\\', '/')\n}\n\ntype PackageJSON = {\n version?: string\n}\n\nasync function resolvePeerDependencies(names: Array<string>): Promise<{\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}> {\n const uniqueNames = [...new Set(names.map(toPackageName))]\n const peerDependencies: Record<string, string> = {}\n const missingDependencies: Array<string> = []\n\n const versions = await Promise.all(\n uniqueNames.map(async (name) => {\n try {\n const path = require.resolve(`${name}/package.json`)\n const packageJSON = JSON.parse(await readFile(path, 'utf8')) as PackageJSON\n return packageJSON.version\n } catch {\n return undefined\n }\n }),\n )\n\n for (const [index, name] of uniqueNames.entries()) {\n const version = versions[index]\n if (version) {\n peerDependencies[name] = version\n continue\n }\n missingDependencies.push(name)\n }\n\n return { peerDependencies, missingDependencies }\n}\n\n/**\n * What `kubb:generation:end` reports: the files the run produced, still in its own storage.\n */\nexport type GenerationEnd = {\n output: SourceFiles\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nexport type GenerationStreamOptions = {\n onGenerationEnd?: (result: GenerationEnd) => void\n}\n\nconst MAX_QUEUED_EVENTS = 1_024\nconst RESERVED_EVENTS = 64\nconst isDiscardable = (event: GenerationEvent) => event.type === 'kubb:files:processing:update' || event.type === 'kubb:info' || event.type === 'kubb:success'\n\n/** Forwards selected Kubb lifecycle events to a native Cap'n Web stream. */\nexport function createGenerationStream(\n hooks: Hookable<KubbHooks>,\n jobId: string,\n options: GenerationStreamOptions = {},\n): { stream: ReadableStream<GenerationEvent>; close: () => Promise<void>; dispose: () => void; fail: (error: unknown) => void } {\n const unhooks: Array<() => void> = []\n let root = ''\n let controller!: ReadableStreamDefaultController<GenerationEvent>\n let closed = false\n const stream = new ReadableStream<GenerationEvent>(\n {\n start: (value) => {\n controller = value\n },\n cancel: () => {\n closed = true\n detach()\n },\n },\n { highWaterMark: MAX_QUEUED_EVENTS },\n )\n\n /**\n * Registers a listener and keeps its remover, so one generation's listeners come off the session\n * emitter again when that generation ends.\n */\n function on<TName extends keyof KubbHooks & string>(name: TName, handler: (...args: KubbHooks[TName]) => unknown): void {\n unhooks.push(hooks.hook(name, handler))\n }\n\n function emitEvent<Type extends GenerationEventType>(type: Type, data: GenerationEventPayloads[Type]): void {\n if (closed) return\n const event = { jobId, type, data, version: 1 as const, timestamp: Date.now() } as unknown as GenerationEvent\n if (controller.desiredSize! <= RESERVED_EVENTS && isDiscardable(event)) return\n if (controller.desiredSize! <= 0) {\n fail(new Error(`Generation event stream exceeded ${MAX_QUEUED_EVENTS} queued events`))\n return\n }\n controller.enqueue(event)\n }\n\n on('kubb:plugin:start', (ctx) => {\n emitEvent('kubb:plugin:start', [{ plugin: { name: ctx.plugin.name } }])\n })\n\n on('kubb:plugin:end', (ctx) => {\n emitEvent('kubb:plugin:end', [{ plugin: { name: ctx.plugin.name }, duration: ctx.duration, success: ctx.success }])\n })\n\n on('kubb:build:start', ({ config, adapter }) => {\n root = config.root\n emitEvent('kubb:build:start', [{ config: { name: config.name }, adapter: { name: adapter.name } }])\n })\n\n on('kubb:build:end', ({ files, config, outputDir }) => {\n emitEvent('kubb:build:end', [{ files: files.map((file) => ({ path: relativeStoragePath(config.root, file.path), name: file.name })), outputDir }])\n })\n\n on('kubb:files:processing:start', ({ files }) => {\n emitEvent('kubb:files:processing:start', [{ total: files.length }])\n })\n\n on('kubb:files:processing:update', ({ files }) => {\n emitEvent('kubb:files:processing:update', [\n {\n files: files.map(({ file, processed, total, percentage }) => ({\n file: relativeStoragePath(root, file.path),\n processed,\n total,\n percentage,\n })),\n },\n ])\n })\n\n on('kubb:files:processing:end', ({ files }) => {\n emitEvent('kubb:files:processing:end', [{ total: files.length }])\n })\n\n // The three log levels differ only in their event name.\n for (const type of ['kubb:info', 'kubb:success', 'kubb:warn'] as const) {\n on(type, ({ message, info }) => {\n emitEvent(type, [{ message, info }])\n })\n }\n\n on('kubb:generation:start', ({ config }) => {\n emitEvent('kubb:generation:start', [\n {\n name: config.name,\n plugins: config.plugins.length,\n },\n ])\n })\n\n on('kubb:generation:end', async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {\n const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name))\n const keys = await storage.readKeys()\n const paths = new Set(keys.map((key) => relativeStoragePath(config.root, key)))\n options.onGenerationEnd?.({ output: { storage, root: config.root, paths }, peerDependencies, missingDependencies })\n\n emitEvent('kubb:generation:end', [])\n\n if (!hrStart) {\n return\n }\n\n const duration = Math.round(getElapsedMs(hrStart))\n\n emitEvent('kubb:generation:summary', [\n { duration, fileCount: filesCreated ?? 0, failedPlugins: Diagnostics.failedPlugins(diagnostics).length, status: status ?? 'success' },\n ])\n })\n\n on('kubb:error', ({ error }) => {\n emitEvent('kubb:error', [\n {\n message: error.message,\n stack: error.stack,\n },\n ])\n })\n\n on('kubb:diagnostic', ({ diagnostic }) => {\n const cause = 'cause' in diagnostic ? diagnostic.cause : undefined\n emitEvent('kubb:diagnostic', [\n {\n code: diagnostic.code,\n message: diagnostic.message,\n severity: diagnostic.severity,\n location: 'location' in diagnostic ? diagnostic.location : undefined,\n help: 'help' in diagnostic ? diagnostic.help : undefined,\n plugin: 'plugin' in diagnostic ? diagnostic.plugin : undefined,\n stack: cause?.stack,\n },\n ])\n })\n\n // Bracketing events carry no context, so they forward identically.\n for (const type of [\n 'kubb:lifecycle:start',\n 'kubb:lifecycle:end',\n 'kubb:format:start',\n 'kubb:format:end',\n 'kubb:lint:start',\n 'kubb:lint:end',\n 'kubb:hooks:start',\n 'kubb:hooks:end',\n ] as const) {\n on(type, () => {\n emitEvent(type, [])\n })\n }\n\n on('kubb:hook:start', ({ id, command, args }) => {\n emitEvent('kubb:hook:start', [{ id, command, args: args ? [...args] : undefined }])\n })\n\n on('kubb:hook:line', ({ id, line }) => {\n emitEvent('kubb:hook:line', [{ id, line }])\n })\n\n on('kubb:hook:end', ({ id, command, args, success, error }) => {\n emitEvent('kubb:hook:end', [\n {\n id,\n command,\n args: args ? [...args] : undefined,\n success,\n error: error ? { message: error.message, stack: error.stack } : undefined,\n },\n ])\n })\n\n /**\n * Takes this generation's listeners off the session emitter. Safe to call twice.\n */\n function detach(): void {\n for (const unhook of unhooks) unhook()\n unhooks.length = 0\n }\n\n async function close(): Promise<void> {\n if (closed) {\n return\n }\n closed = true\n detach()\n controller.close()\n }\n\n function fail(error?: unknown): void {\n detach()\n if (closed) {\n return\n }\n closed = true\n controller.error(error)\n }\n\n return { stream, close, dispose: () => fail(), fail }\n}\n","import WebSocket from 'ws'\n\ntype WebSocketOptions = WebSocket.ClientOptions\n\nconst CONNECT_TIMEOUT_MS = 5_000\n\n/**\n * Opens a Studio WebSocket connection and closes it when the initial handshake exceeds the configured timeout.\n */\nexport function createWebsocket(url: string, options: WebSocketOptions): WebSocket {\n const ws = new WebSocket(url, options)\n\n const timer = setTimeout(() => {\n if (ws.readyState === WebSocket.CONNECTING) {\n ws.close(3008, 'Connection timeout')\n }\n }, CONNECT_TIMEOUT_MS)\n\n // Once the handshake settles the timer has nothing left to check, and leaving it pending holds\n // the socket for the rest of the window.\n ws.once('open', () => clearTimeout(timer))\n ws.once('close', () => clearTimeout(timer))\n\n return ws\n}\n","import { newWebSocketRpcSession, RpcTarget } from 'capnweb'\nimport type {\n AgentApi,\n GenerateInput,\n PublishSnapshotInput,\n ReadFilesInput,\n RpcClose,\n RpcConnection,\n RpcConnector,\n SaveConfigInput,\n StudioApi,\n} from '../protocol/index.ts'\nimport { AGENT_INSTANCE_HEADER } from '../protocol/index.ts'\nimport { createWebsocket } from './websocket.ts'\n\n/**\n * The only methods Studio may call on an agent. A `StudioSession` carries far more than\n * {@link AgentApi}, so it is wrapped rather than exposed: what Cap'n Web can reach is exactly what\n * this class re-declares.\n */\nclass AgentRpcTarget extends RpcTarget implements AgentApi {\n constructor(private readonly api: AgentApi) {\n super()\n }\n\n connect() {\n return this.api.connect()\n }\n startGeneration(input: GenerateInput) {\n return this.api.startGeneration(input)\n }\n saveConfig(input: SaveConfigInput) {\n return this.api.saveConfig(input)\n }\n publishSnapshot(input: PublishSnapshotInput) {\n return this.api.publishSnapshot(input)\n }\n readFiles(input: ReadFilesInput) {\n return this.api.readFiles(input)\n }\n cancel(jobId: string) {\n return this.api.cancel(jobId)\n }\n}\n\n/**\n * Opens an authenticated Cap'n Web session to Studio over a WebSocket. Rejects an unencrypted URL\n * before opening the socket, so a bearer token never reaches a plaintext host.\n *\n * @example\n * ```ts\n * const rpc = await connectWebSocketRpc({ url: 'wss://studio.kubb.dev/s/1', token, local: session })\n * await rpc.studio.ping()\n * ```\n */\nexport const connectWebSocketRpc: RpcConnector = async ({ url, token, instanceId, local }): Promise<RpcConnection> => {\n const { protocol, hostname, host } = new URL(url)\n // `URL` keeps the brackets on an IPv6 hostname, so `::1` arrives as `[::1]`.\n const isLoopback = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]'\n if (protocol !== 'wss:' && !(protocol === 'ws:' && isLoopback)) {\n throw new Error(`Refusing unencrypted WebSocket to ${host}`)\n }\n\n const socket = createWebsocket(url, { headers: { Authorization: `Bearer ${token}`, [AGENT_INSTANCE_HEADER]: instanceId } })\n const closed = new Promise<RpcClose>((resolve) => socket.once('close', (code: number, reason: Buffer) => resolve({ code, reason: reason.toString() })))\n // `ws` implements the browser WebSocket surface capnweb uses, but declares its own nominal type.\n const studio = newWebSocketRpcSession<StudioApi>(socket as unknown as globalThis.WebSocket, new AgentRpcTarget(local))\n studio.onRpcBroken(() => socket.close())\n\n return {\n studio,\n closed,\n close: () => studio[Symbol.dispose](),\n }\n}\n","import type { Hookable, KubbHooks } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport type { AgentPermissions } from '../protocol/index.ts'\n\n/**\n * Events a host emits about its Kubb Studio session, as opposed to a generation. `kubb:` stays\n * reserved for generation lifecycle.\n */\nexport type StudioConnectingContext = {\n /**\n * The Studio instance this session is opening against.\n */\n url: string\n}\n\nexport type StudioConnectedContext = {\n /**\n * The Studio instance this session attached to.\n */\n url: string\n /**\n * Both sides of the connection, so a host can print them and make a mismatch visible.\n */\n versions: {\n /**\n * The Studio instance's own version, when it sent one.\n */\n studio?: string\n /**\n * The version of the runtime that connected.\n */\n kubb: string\n /**\n * The version of the host itself, such as the `kubb` CLI or the agent image.\n */\n agent: string\n }\n /**\n * This agent's slug, refreshed on every connect so a rename in Studio shows up without a\n * re-pair. Absent when Studio predates the field.\n */\n agentSlug?: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none, or when\n * Studio predates the field.\n */\n organizationSlug?: string\n}\n\n/**\n * Fired once Studio confirms the `agent:connect` handshake was received and the session is fully\n * registered. Distinct from `studio:connected`, which only means the socket is open.\n */\nexport type StudioReadyContext = Record<string, never>\n\nexport type StudioDisconnectedContext = {\n /**\n * Why Studio ended the session.\n */\n reason: string\n}\n\nexport type StudioReconnectingContext = {\n /** Milliseconds until the next connection attempt. */\n delayMs: number\n}\n\nexport type StudioCommandStartContext = {\n /**\n * The command Studio sent, without its `studio:` prefix: `generate`, `connect` or `save`.\n */\n command: string\n}\n\nexport type StudioCommandEndContext = {\n /**\n * The command that finished, without its `studio:` prefix.\n */\n command: string\n /**\n * What the command did, when there is something to report: `applied 2/3 edits to kubb.config.ts`.\n */\n info?: string\n}\n\nexport type StudioWarnContext = {\n /**\n * What was refused or ignored.\n */\n message: string\n /** The missing permission, if that is why, so the host can append its own remedy. */\n permission?: keyof AgentPermissions\n}\n\nexport type StudioErrorContext = {\n /**\n * The failure, for the host's own output. One Studio needs to hear about goes over the socket\n * through the `kubb:error` generation hook instead.\n */\n error: Error\n}\n\ndeclare global {\n namespace Kubb {\n interface KubbHooksRegistry {\n 'studio:connecting': [ctx: StudioConnectingContext]\n 'studio:connected': [ctx: StudioConnectedContext]\n 'studio:ready': [ctx: StudioReadyContext]\n 'studio:disconnected': [ctx: StudioDisconnectedContext]\n 'studio:reconnecting': [ctx: StudioReconnectingContext]\n 'studio:command:start': [ctx: StudioCommandStartContext]\n 'studio:command:end': [ctx: StudioCommandEndContext]\n 'studio:warn': [ctx: StudioWarnContext]\n 'studio:error': [ctx: StudioErrorContext]\n }\n }\n}\n\n/**\n * Register a `kubb:hook:start` listener that spawns the requested command via tinyexec,\n * streams each stdout line as a `kubb:hook:line` event, and calls `kubb:hook:end` with the result.\n * A failure travels on `kubb:hook:end` only: the caller waiting on it reports `kubb:error` once.\n * Streaming the output lets Kubb Studio render live hook progress over the WebSocket connection.\n *\n * Returns a remover, so a session that runs one generation after another on the same emitter does\n * not stack a listener per run.\n */\nexport function setupHookListener(hooks: Hookable<KubbHooks>, root: string, signal?: AbortSignal): () => void {\n return hooks.hook('kubb:hook:start', async (ctx) => {\n const { id, command, args } = ctx\n // No id means nothing is waiting on the result (benchmarks, tests).\n if (!id) {\n return\n }\n\n const commandWithArgs = args?.length ? `${command} ${args.join(' ')}` : command\n\n try {\n const proc = x(command, [...(args ?? [])], {\n signal,\n nodeOptions: { cwd: root, detached: true },\n })\n\n for await (const line of proc) {\n await hooks.callHook('kubb:hook:line', { id, line })\n }\n\n const { exitCode } = await proc\n\n if (exitCode !== 0) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n\n return\n }\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: true, error: null })\n } catch (caughtError) {\n const error = new Error(`Hook execute failed: ${commandWithArgs}`)\n error.cause = caughtError\n\n await hooks.callHook('kubb:hook:end', { id, command, args, success: false, error })\n }\n })\n}\n\n/**\n * Waits for the `kubb:hook:end` matching `hookId`. Register this before calling `kubb:hook:start`:\n * `callHook` awaits its listeners, and {@link setupHookListener} calls `kubb:hook:end` from inside\n * that same listener, so a handler added afterward would already have missed it.\n */\nexport function waitForHookEnd(hooks: Hookable<KubbHooks>, hookId: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const handleHookEnd = (ctx: { id?: string; success: boolean; error?: Error | null }) => {\n if (ctx.id !== hookId) return\n hooks.removeHook('kubb:hook:end', handleHookEnd)\n\n if (ctx.success) {\n resolve()\n return\n }\n reject(ctx.error)\n }\n\n hooks.hook('kubb:hook:end', handleHookEnd)\n })\n}\n","import { hash } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { type Config, createKubb, type Diagnostic, Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport {\n FORMATTER_PREFERENCE,\n LINTER_PREFERENCE,\n formatters,\n linters,\n memoize,\n toError,\n tokenize,\n type ToolCommand,\n detectTool as detectUncachedTool,\n} from '@internals/utils'\nimport { waitForHookEnd } from './hooks.ts'\nimport { setupHookListener } from './hooks.ts'\nimport { type GenerationStore, listDisk } from './generations.ts'\n\nconst DISK_SNAPSHOT_MAX_FILES = 10_000\n\n/**\n * `isToolAvailable` spawns a process, and a long-lived connection generates repeatedly, so each\n * executable is probed once per process. The CLI deliberately does not memoize: a `--watch` build\n * should keep noticing a tool installed mid-session.\n */\nconst detectTool = memoize(new Map<ReadonlyArray<string>, Promise<string | null>>(), detectUncachedTool)\n\n/**\n * The two post-build tool steps. Formatting and linting differ only in which tools they look for,\n * so they run through one loop rather than two near-identical blocks.\n *\n * `noun` and `verbing` are spelled out instead of built from `kind`. Concatenating `` `${kind}ter` ``\n * and `` `${kind}ting` `` works for `format`, but doubles the `t` in `lint`, giving \"lintter\" and\n * \"lintting\" instead of \"linter\" and \"linting\".\n */\nconst TOOL_STEPS: ReadonlyArray<{ kind: 'format' | 'lint'; noun: string; verbing: string; tools: Record<string, ToolCommand>; detect: ReadonlyArray<string> }> =\n [\n // `detect` is the preference order for `auto`, most-preferred first. Spelled out rather than\n // taken from the table's key order, which is arbitrary and silently changes what `auto` picks.\n { kind: 'format', noun: 'formatter', verbing: 'Formatting', tools: formatters, detect: FORMATTER_PREFERENCE },\n { kind: 'lint', noun: 'linter', verbing: 'Linting', tools: linters, detect: LINTER_PREFERENCE },\n ]\n\n/**\n * Absolute path of the directory the formatter and linter are pointed at.\n */\nfunction outputPath(config: Config): string {\n return path.isAbsolute(config.output.path) ? config.output.path : path.resolve(process.cwd(), config.root, config.output.path)\n}\n\ntype RunHookProps = {\n hooks: Hookable<KubbHooks>\n /**\n * Stable identity for the command, hashed so the `kubb:hook:*` events for concurrent commands\n * can be told apart.\n */\n id: string\n command: string\n args: ReadonlyArray<string>\n}\n\n/**\n * Emits `kubb:hook:start` and waits for the matching `kubb:hook:end`. The host spawns the process:\n * this only describes what to run and when it finished.\n *\n * @throws whatever the command failed with, so callers can report it their own way.\n */\nasync function runHook({ hooks, id, command, args }: RunHookProps): Promise<void> {\n const hookId = hash('sha256', id)\n // Registered before the start event, since `callHook` awaits its listeners and the host calls\n // `kubb:hook:end` from inside that same listener.\n const hookEnd = waitForHookEnd(hooks, hookId)\n\n await hooks.callHook('kubb:hook:start', { id: hookId, command, args: [...args] })\n await hookEnd\n}\n\ntype GenerateProps = {\n config: Config\n hooks: Hookable<KubbHooks>\n signal?: AbortSignal\n}\n\nfunction isProblemErrorDiagnostic(diagnostic: Diagnostic): diagnostic is Diagnostic & { plugin?: string; message: string } {\n return (diagnostic.kind ?? 'problem') === 'problem' && diagnostic.severity === 'error'\n}\n\n/**\n * Folds error-severity diagnostics into one thrown error so logs name the failing plugin.\n */\nfunction formatGenerationFailure(diagnostics: ReadonlyArray<Diagnostic>): Error {\n const reasons = diagnostics\n .filter(isProblemErrorDiagnostic)\n .map((diagnostic) => (diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message))\n\n if (!reasons.length) {\n return new Error('Generation failed')\n }\n\n return new Error(`Generation failed: ${reasons.length} error${reasons.length === 1 ? '' : 's'}: ${reasons.join('; ')}`)\n}\n\n/**\n * Runs a full Kubb code-generation cycle for the given config.\n *\n * Emits lifecycle events on the provided `hooks` emitter so callers (e.g. the WebSocket stream)\n * can forward progress to connected clients. After a successful build, auto-formatting and\n * linting are applied when configured, followed by any user-defined `hooks.done` commands.\n */\nexport async function generate({ config, hooks, signal }: GenerateProps): Promise<void> {\n signal?.throwIfAborted()\n const hrStart = process.hrtime()\n\n await hooks.callHook('kubb:generation:start', { config })\n\n await hooks.callHook('kubb:info', { message: config.name ? `Setup generation ${config.name}` : 'Setup generation' })\n\n const kubb = createKubb(config, { hooks, signal })\n await kubb.setup()\n\n await hooks.callHook('kubb:info', { message: config.name ? `Build generation ${config.name}` : 'Build generation' })\n\n const { files, diagnostics, storage } = await kubb.safeBuild()\n signal?.throwIfAborted()\n\n await hooks.callHook('kubb:info', { message: 'Load summary' })\n\n // Core captures build failures as `error`-severity diagnostics instead of throwing, so\n // surface each one as a `kubb:error` event for the client. Warnings and info reported\n // through `ctx.warn`/`ctx.info` reach the client directly via core's events on this shared emitter.\n for (const diagnostic of diagnostics.filter(isProblemErrorDiagnostic)) {\n await hooks.callHook('kubb:error', { error: new Error(diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message) })\n }\n\n const status = Diagnostics.hasError(diagnostics) ? 'failed' : 'success'\n\n await hooks.callHook('kubb:generation:end', {\n config,\n // Only the files Kubb generated. `fsStorage().readKeys()` lists the working directory, so\n // Studio's tree would show the host's own source and miss output landing outside it.\n storage: { ...storage, readKeys: async () => [...new Set(files.map((file) => file.path))] },\n diagnostics,\n status,\n hrStart,\n filesCreated: files.length,\n })\n\n if (status === 'failed') {\n throw formatGenerationFailure(diagnostics)\n }\n\n await hooks.callHook('kubb:success', { message: 'Generation successfully' })\n\n for (const step of TOOL_STEPS) {\n const setting = config.output[step.kind]\n if (!setting) {\n continue\n }\n\n await hooks.callHook(`kubb:${step.kind}:start`)\n\n // `auto` means \"whatever is installed\", so the tool is detected now rather than at config time.\n const tool = setting === 'auto' ? await detectTool(step.detect) : setting\n\n if (!tool) {\n await hooks.callHook('kubb:warn', { message: `No ${step.noun} found (${step.detect.join(', ')}). Skipping ${step.verbing.toLowerCase()}.` })\n }\n\n if (tool && setting === 'auto') {\n await hooks.callHook('kubb:info', { message: `Auto-detected ${step.noun}: ${styleText('dim', tool)}` })\n }\n\n const command = tool ? step.tools[tool] : undefined\n\n if (command) {\n try {\n await runHook({ hooks, id: [config.name, tool].filter(Boolean).join('-'), command: command.command, args: command.args(outputPath(config)) })\n\n await hooks.callHook('kubb:success', { message: `${step.verbing} with ${tool} successfully` })\n } catch (caughtError) {\n await hooks.callHook('kubb:error', { error: new Error(command.errorMessage, { cause: caughtError }) })\n // Format/lint failures are non-fatal, but an abort during those commands must still reject.\n signal?.throwIfAborted()\n }\n }\n\n await hooks.callHook(`kubb:${step.kind}:end`)\n }\n\n // `output.postGenerate` commands run in order, each one waiting for the previous to finish.\n if (config.output.postGenerate?.length) {\n await hooks.callHook('kubb:hooks:start')\n\n for (const entry of config.output.postGenerate) {\n const line = typeof entry === 'string' ? entry : entry.command\n const [cmd, ...args] = tokenize(line)\n\n if (!cmd) {\n continue\n }\n\n try {\n await runHook({ hooks, id: line, command: cmd, args })\n } catch (error) {\n await hooks.callHook('kubb:error', { error: toError(error) })\n throw error\n }\n await hooks.callHook('kubb:success', { message: `${line} successfully executed` })\n }\n\n await hooks.callHook('kubb:hooks:end')\n }\n}\n\nexport async function runGenerationOperation({\n config,\n hooks,\n signal,\n jobId,\n store,\n snapshotRoot,\n maxSnapshotMb,\n}: GenerateProps & {\n jobId: string\n store: GenerationStore\n snapshotRoot?: string\n maxSnapshotMb: number\n}): Promise<Awaited<ReturnType<GenerationStore['keep']>> | undefined> {\n const diskFiles = snapshotRoot ? await listDisk({ root: snapshotRoot, outputPath: config.output.path, maxFiles: DISK_SNAPSHOT_MAX_FILES }) : undefined\n const disk = diskFiles ? await store.keep({ jobId, source: 'disk', files: diskFiles, maxSetMb: maxSnapshotMb }) : undefined\n const removeHookListener = setupHookListener(hooks, config.root, signal)\n\n try {\n await generate({ config, hooks, signal })\n } catch (error) {\n await store.drop(jobId)\n throw error\n } finally {\n removeHookListener()\n }\n\n return disk\n}\n","import { randomUUID } from 'node:crypto'\nimport { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { getErrorMessage, read, toError } from '@internals/utils'\nimport { cacheStorage, type Config, fsStorage, Hookable, type KubbHooks, memoryStorage, resolveCacheDir } from '@kubb/core'\nimport { version as kubbVersion } from '../../package.json'\nimport {\n type AgentApi,\n type AgentCapacity,\n type AgentRegisterResponse,\n type AgentLoad,\n type AgentPermissions,\n type ConfigFileView,\n type ConnectMessagePayload,\n type GenerateInput,\n type GenerateResult,\n type GenerationEvent,\n type GenerationRun,\n GENERATION_GONE_MESSAGE,\n MAX_FILES_PER_REQUEST,\n MAX_PAGE_LINES,\n type ReadFilesInput,\n type ReadFilesResult,\n type SaveConfigInput,\n type SaveResult,\n type PublishSnapshotInput,\n type PublishSnapshotResult,\n AgentCloseCode,\n type RpcClose,\n type RpcConnector,\n type RpcConnection,\n} from '../protocol/index.ts'\nimport { registerAgent } from '../operations/api.ts'\nimport { agentDefaults, resolveAgentCapacity, resolveGenerationLimits } from '../operations/constants.ts'\nimport { mergeAdapter, mergePlugins, toPackageName } from '../operations/resolveConfig.ts'\nimport { RpcTarget } from 'capnweb'\nimport { createGenerationStore, type GenerationStore } from '../operations/generations.ts'\nimport { createGenerationStream, type GenerationEnd } from '../operations/generationEvents.ts'\nimport { connectWebSocketRpc } from '../operations/rpc.ts'\nimport { runGenerationOperation } from '../operations/generate.ts'\n\n/** A sandbox shares one in-memory generation store across tenants, so old generations expire. */\nconst SANDBOX_GENERATION_TTL_MS = 15 * 60_000\n\n/**\n * A fresh root for one sandbox job. Kubb keys its output manifest cache by root, so tenants that\n * shared the agent's own root would read each other's manifest.\n */\nfunction createJobRoot(): Promise<string> {\n return mkdtemp(path.join(tmpdir(), 'kubb-job-'))\n}\n\n/**\n * Removes a job root and the manifest cache Kubb derived from it. Best effort: a leftover temp\n * directory must not fail a job that already finished.\n */\nasync function removeJobRoot(jobRoot: string): Promise<void> {\n await Promise.all([rm(jobRoot, { recursive: true, force: true }), rm(resolveCacheDir(jobRoot), { recursive: true, force: true })]).catch(() => {})\n}\n\nclass GenerationRunTarget extends RpcTarget implements GenerationRun {\n constructor(\n private readonly generationStream: ReadableStream<GenerationEvent>,\n private readonly generationResult: Promise<GenerateResult>,\n private readonly cancelGeneration: () => Promise<void>,\n /** Stops the run. Cap'n Web calls this on explicit disposal and on a dropped session alike. */\n private readonly stopGeneration: () => void,\n ) {\n super()\n }\n\n async events() {\n return this.generationStream\n }\n result() {\n return this.generationResult\n }\n cancel() {\n return this.cancelGeneration()\n }\n [Symbol.dispose]() {\n this.stopGeneration()\n }\n}\n\nexport type StudioSessionOptions = {\n connector?: RpcConnector\n token: string\n studioUrl?: string\n configPath: string\n /**\n * Loads the on-disk Kubb config. Injected so each host resolves config its own way: the Docker\n * agent from an explicit `KUBB_AGENT_CONFIG` path, the CLI through the same discovery\n * `kubb generate` uses.\n */\n loadConfig: () => Promise<Config>\n /**\n * The runtime's own version, reported to Studio next to the `kubb` version.\n */\n version: string\n /**\n * What Studio may do in this project, off unless the host grants it. A sandbox session narrows\n * them further: it never writes to disk and never edits a config file, and it always generates\n * from the spec Studio sends.\n */\n permissions?: Partial<AgentPermissions>\n root?: string\n /**\n * Maximum reconnect backoff in milliseconds.\n */\n retryInterval?: number\n /**\n * Milliseconds between keep-alive pings, clamped to `agentDefaults.maxHeartbeatIntervalMs`.\n * Raise it to halve the traffic and database writes a long-lived agent costs, at the price of\n * Studio taking that much longer to notice the agent has gone. Lower it in development to see\n * connection state move immediately.\n */\n heartbeatInterval?: number\n /**\n * What this agent process can take on, reported to Studio at registration. Unset fields come from\n * `KUBB_AGENT_MAX_CONCURRENT`.\n */\n capacity?: Partial<AgentCapacity>\n /**\n * Names this agent process to Studio, sent at registration and as {@link AGENT_INSTANCE_HEADER}\n * on the socket. `runConnection` sets one per process, so a reconnect is the same instance and a\n * restart is a new one. Not meant to be set directly by a host.\n */\n instanceId?: string\n /**\n * Aborting this disconnects the session and stops the reconnect loop. Hosts wire it to their own\n * shutdown signal, usually `SIGINT` or `SIGTERM`.\n */\n signal?: AbortSignal\n /**\n * Installs listeners on the session's event emitter, which carries both the session events and\n * the generations it runs. Left out, the runtime prints nothing, which is what a library should\n * default to.\n */\n installLogger?: (hooks: Hookable<KubbHooks>) => void | Promise<void>\n}\n\n/**\n * A session's options with every default filled in, so nothing downstream repeats a fallback.\n */\ntype ResolvedOptions = StudioSessionOptions & {\n studioUrl: string\n root: string\n permissions: AgentPermissions\n retryInterval: number\n heartbeatInterval: number\n capacity: AgentCapacity\n instanceId: string\n /**\n * Absolute path to the config file, for reading and patching it. `configPath` keeps the form the\n * host gave, which is what Studio shows.\n */\n configFile: string\n}\n\n/**\n * Fills in a host's options: the hosted Studio URL, the current working directory, and every\n * permission off unless granted. Idempotent, so a reconnect can pass an already-resolved bag\n * back in.\n */\nfunction applyStudioDefaults(options: StudioSessionOptions): ResolvedOptions {\n const root = options.root ?? process.cwd()\n\n return {\n ...options,\n studioUrl: options.studioUrl ?? agentDefaults.studioUrl,\n root,\n // `configPath` is relative to the agent's root unless it is already absolute, which is what\n // `resolve` does on its own.\n configFile: path.resolve(root, options.configPath),\n permissions: { allowWrite: false, allowConfigEdit: false, allowExec: false, allowRead: false, ...options.permissions },\n retryInterval: options.retryInterval ?? agentDefaults.retryIntervalMs,\n // Studio counts an agent offline once its last ping is older than its liveness window, so a\n // slower cadence would make a healthy agent invisible. Clamped here rather than in a host's\n // env parsing, so every host is held to the contract.\n heartbeatInterval: Math.min(options.heartbeatInterval ?? agentDefaults.heartbeatIntervalMs, agentDefaults.maxHeartbeatIntervalMs),\n capacity: { ...resolveAgentCapacity(), ...options.capacity },\n instanceId: options.instanceId ?? randomUUID(),\n }\n}\n\n/**\n * Jobs one agent process can run at once today. Two runs would share this session's hook emitter,\n * and with it each other's events, so the connection keeps this limit.\n */\nconst RUNTIME_MAX_CONCURRENT = 1\n\nconst MB = 1024 * 1024\n\nfunction rssMb(): number {\n return process.memoryUsage().rss / MB\n}\n\n/**\n * How a session ends: what the host is told, and whether it reconnects.\n */\nexport type SessionEnd = {\n reason: string\n retry: boolean\n /** Reported through `studio:error` when the end needs the user to act. */\n error?: Error\n}\n\n/**\n * Reads what Studio meant by closing the connection. A code Studio did not send on purpose is an\n * ordinary drop, and the agent reconnects as it always has.\n */\nfunction planEnd(close: RpcClose | void): SessionEnd {\n const code = close?.code\n // Every connection attempt registers first, so reconnecting is how the agent registers again.\n if (code === AgentCloseCode.REAUTHENTICATE) return { reason: 'Kubb Studio asked the agent to register again', retry: true }\n if (code === AgentCloseCode.SUPERSEDED) return { reason: 'another instance of this agent took over', retry: false }\n if (code === AgentCloseCode.INCOMPATIBLE) {\n return {\n reason: 'this agent is too old for Kubb Studio, or was deleted',\n retry: false,\n error: new Error('Kubb Studio closed the connection: this agent is too old for it, or was deleted. Upgrade the agent, or pair it again.'),\n }\n }\n return { reason: 'connection closed', retry: true }\n}\n\nfunction isValidPage({ cursor, limit }: Pick<ReadFilesInput, 'cursor' | 'limit'>): boolean {\n if (limit === undefined) return cursor === undefined\n if (cursor !== undefined && (!Number.isSafeInteger(cursor) || cursor < 0)) return false\n return Number.isSafeInteger(limit) && limit >= 1 && limit <= MAX_PAGE_LINES\n}\n\n/**\n * One agent-to-Studio RPC transport: opening it, keeping it alive, and serving remote methods.\n * `runConnection` owns the lifetime and retry of each attempt.\n */\nexport class StudioSession implements AgentApi {\n readonly #options: ResolvedOptions\n // Each session gets its own isolated event emitter so generation events from one session do not\n // bleed into another session's WebSocket stream.\n readonly #hooks = new Hookable<KubbHooks>()\n /**\n * Removers for every listener this session added (socket, shutdown signal, hooks) so `dispose`\n * detaches them in one pass. Listeners a host attached itself through `installLogger` survive.\n */\n readonly #unhooks: Array<() => void> = []\n\n /**\n * What registration handed back, and the marker for whether the agent registered at all.\n * Before it resolves there is no sandbox flag to read.\n */\n #registration: AgentRegisterResponse | undefined\n #rpc: RpcConnection | undefined\n // Returned with the session, so both sides can be named from the first RPC connection.\n #studioVersion: string | undefined\n\n // Whether the session is over: guards the close event from tearing down twice, and a shutdown\n // from being turned into a reconnect.\n #disposed = false\n // Guards against a second `generate` command starting while one is already running. Without\n // this, two concurrent `generate()` calls share this socket via `setupEventsStream`, and their\n // events interleave with no way for Studio to tell the two runs apart.\n #isGenerating = false\n #activeJob: { jobId: string; cancel: () => Promise<void> } | undefined\n #heartbeatTimer: ReturnType<typeof setTimeout> | undefined\n // Set by `kubb:generation:end`, filed into `#generations` once the job finishes.\n #lastGeneration: GenerationEnd | undefined\n #store: GenerationStore | undefined\n readonly #limits = resolveGenerationLimits()\n /**\n * Resolves when Studio calls {@link StudioSession.connect}. `studio:ready` waits on this so the\n * host does not queue jobs before the agent session is registered.\n */\n readonly #connectAck = Promise.withResolvers<void>()\n readonly #ended = Promise.withResolvers<SessionEnd>()\n readonly closed = this.#ended.promise\n #generationResult: Promise<GenerateResult> | undefined\n\n constructor(options: StudioSessionOptions) {\n this.#options = applyStudioDefaults(options)\n // dispose() may reject this before start() awaits it\n void this.#connectAck.promise.catch(() => {})\n }\n\n /**\n * A sandbox agent runs on Studio's own infrastructure, so it has no user project to touch.\n */\n get #isSandbox(): boolean {\n return this.#registration?.isSandbox === true\n }\n\n /**\n * Kept in the project's cache directory, so it survives a restart, except on a sandbox: its pool\n * sessions run every tenant's jobs, so it keeps them in memory. Looked up by job id only.\n */\n get #generations(): GenerationStore {\n this.#store ??= createGenerationStore({\n storage: this.#isSandbox ? memoryStorage() : cacheStorage({ root: this.#options.root }),\n maxCount: this.#limits.maxCount,\n maxMb: this.#limits.maxMb,\n ttlMs: this.#isSandbox ? SANDBOX_GENERATION_TTL_MS : undefined,\n })\n return this.#store\n }\n\n get #canWrite(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowWrite\n }\n\n get #canEditConfig(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowConfigEdit\n }\n\n /**\n * A sandbox agent always allows reading its output back; a local agent only when opted in.\n */\n get #canRead(): boolean {\n return this.#isSandbox || this.#options.permissions.allowRead\n }\n\n async start(): Promise<void> {\n const { token, studioUrl, signal, heartbeatInterval, installLogger, instanceId, capacity } = this.#options\n\n await installLogger?.(this.#hooks)\n signal?.addEventListener('abort', this.#onAbort, { once: true })\n this.#unhooks.push(() => signal?.removeEventListener('abort', this.#onAbort))\n\n try {\n signal?.throwIfAborted()\n // Before registering, so a host can cover the wait: registration is a round trip and the\n // socket after it opens without being awaited.\n await this.#hooks.callHook('studio:connecting', { url: studioUrl })\n\n if (capacity.maxConcurrent > RUNTIME_MAX_CONCURRENT) {\n await this.#warn(\n `Running ${RUNTIME_MAX_CONCURRENT} job at a time: KUBB_AGENT_MAX_CONCURRENT=${capacity.maxConcurrent} needs per-job workers, which this agent does not have yet`,\n )\n }\n\n const registration = await registerAgent({\n signal,\n token,\n studioUrl,\n instanceId,\n capacity: { ...capacity, maxConcurrent: Math.min(capacity.maxConcurrent, RUNTIME_MAX_CONCURRENT) },\n })\n\n signal?.throwIfAborted()\n this.#registration = registration\n this.#studioVersion = registration.version\n\n const rpc = await (this.#options.connector ?? connectWebSocketRpc)({ url: registration.socketUrl, token, instanceId, local: this })\n this.#rpc = rpc\n void rpc.closed.then(this.#onClose)\n\n if (signal?.aborted || this.#disposed) {\n rpc.close()\n throw new Error('Connection stopped during startup')\n }\n\n this.#scheduleHeartbeat(heartbeatInterval)\n await this.#hooks.callHook('studio:connected', {\n url: studioUrl,\n versions: { studio: this.#studioVersion, kubb: kubbVersion, agent: this.#options.version },\n agentSlug: registration.agentSlug,\n organizationSlug: registration.organizationSlug,\n })\n // Studio registers the agent by calling connect() over RPC. Ready means that handshake landed.\n await this.#connectAck.promise\n await this.#hooks.callHook('studio:ready', {})\n } catch (error) {\n await this.dispose()\n if (!signal?.aborted) await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n reportRetry(delayMs: number): Promise<void> | void {\n return this.#hooks.callHook('studio:reconnecting', { delayMs })\n }\n\n #warn(message: string, permission?: keyof AgentPermissions): Promise<void> | void {\n return this.#hooks.callHook('studio:warn', { message, permission })\n }\n\n /**\n * Declines a request: logs why locally, then tells Studio. The two wordings differ on purpose,\n * since the log names the request that was ignored and the error names what the caller can do.\n */\n async #refuse(reason: string, message: string, permission?: keyof AgentPermissions): Promise<never> {\n await this.#warn(reason, permission)\n throw new Error(message)\n }\n\n #scheduleHeartbeat(interval: number): void {\n const rpc = this.#rpc\n if (!rpc) {\n return\n }\n this.#heartbeatTimer = setTimeout(async () => {\n try {\n await this.#ping(rpc)\n } catch {\n if (this.#rpc === rpc) {\n rpc.close()\n }\n return\n }\n\n if (this.#rpc === rpc) {\n this.#scheduleHeartbeat(interval)\n }\n }, interval)\n }\n\n /**\n * Races `studio.ping()` against a deadline, so a half-open socket can't hang it forever.\n * */\n async #ping(rpc: RpcConnection): Promise<void> {\n const { promise: timedOut, reject: onTimeout } = Promise.withResolvers<never>()\n const timer = setTimeout(() => onTimeout(new Error('Heartbeat ping timed out')), agentDefaults.heartbeatTimeoutMs)\n\n try {\n // A load report that fails to build must not cost the heartbeat itself.\n const load = await Promise.race([this.#load().catch(() => undefined), timedOut])\n await Promise.race([rpc.studio.ping(load), timedOut])\n } finally {\n clearTimeout(timer)\n }\n }\n\n async #load(): Promise<AgentLoad> {\n return {\n running: this.#isGenerating ? 1 : 0,\n rssMb: Math.round(rssMb()),\n storeBytes: await this.#generations.bytes(),\n accepting: true,\n }\n }\n\n /**\n * Reads `kubb.config.ts` and reports which plugin options Studio may edit.\n *\n * Skipped when the host did not grant `allowConfigEdit`. Not cached: the user can edit the file\n * between two Studio actions.\n */\n async #readConfigFileView(source?: string): Promise<ConfigFileView | undefined> {\n if (!this.#canEditConfig) {\n return undefined\n }\n\n try {\n const { readConfig } = await import('../operations/configFile.ts')\n return readConfig(source ?? (await read(this.#options.configFile)))\n } catch (error) {\n await this.#warn(`Could not read ${this.#options.configFile}: ${getErrorMessage(error)}`)\n\n return undefined\n }\n }\n\n async connect(): Promise<ConnectMessagePayload> {\n const { configPath, root, version, loadConfig, permissions } = this.#options\n const [config, file] = await Promise.all([loadConfig(), this.#readConfigFileView()])\n\n const payload: ConnectMessagePayload = {\n versions: { kubb: kubbVersion, agent: version },\n root,\n config: {\n path: configPath,\n file,\n plugins: config.plugins.map((plugin) => ({\n name: toPackageName(plugin.name),\n options: plugin.options ?? {},\n })),\n },\n permissions: {\n ...permissions,\n allowWrite: this.#canWrite,\n allowConfigEdit: this.#canEditConfig,\n allowRead: this.#canRead,\n },\n }\n this.#connectAck.resolve()\n return payload\n }\n\n #onAbort = (): void => void this.#end({ reason: 'shutdown', retry: false }).catch(() => {})\n\n #onClose = (close: RpcClose | void): void => void this.#end(planEnd(close)).catch(() => {})\n\n /** Closes this attempt and waits for its generation to stop before another can start. */\n dispose(): Promise<void> {\n return this.#end({ reason: 'shutdown', retry: false })\n }\n\n async #end(plan: SessionEnd): Promise<void> {\n if (this.#disposed) {\n await this.closed\n return\n }\n this.#disposed = true\n clearTimeout(this.#heartbeatTimer)\n this.#rpc?.close()\n this.#rpc = undefined\n this.#connectAck.reject(new Error('Session ended before Studio called connect()'))\n for (const unhook of this.#unhooks) unhook()\n this.#unhooks.length = 0\n\n try {\n await this.#activeJob?.cancel()\n await this.#generationResult?.catch(() => {})\n await this.#hooks.callHook('studio:disconnected', { reason: plan.reason })\n if (plan.error) await this.#hooks.callHook('studio:error', { error: plan.error })\n } finally {\n this.#ended.resolve(plan)\n }\n }\n\n startGeneration(data: GenerateInput): GenerationRun {\n const generationStream = createGenerationStream(this.#hooks, data.jobId, {\n onGenerationEnd: (result) => {\n this.#lastGeneration = result\n },\n })\n const controller = new AbortController()\n const cancelRun = async () => {\n controller.abort(new Error('Generation canceled'))\n }\n const result = this.#runGeneration(data, controller, cancelRun)\n .then(async (value) => {\n await generationStream.close()\n return value\n })\n .catch((error) => {\n generationStream.fail(error)\n throw error\n })\n .finally(() => {\n if (this.#activeJob?.cancel === cancelRun) this.#activeJob = undefined\n })\n if (this.#activeJob?.cancel === cancelRun) this.#generationResult = result\n // A disconnect can reject this after the remote caller disappeared.\n void result.catch(() => {})\n\n return new GenerationRunTarget(generationStream.stream, result, cancelRun, () => {\n controller.abort(new Error('Generation canceled'))\n generationStream.dispose()\n })\n }\n\n /**\n * Cancels the currently running job when its id matches `jobId`.\n */\n async cancel(jobId: string): Promise<void> {\n if (this.#activeJob?.jobId === jobId) await this.#activeJob.cancel()\n }\n\n async #runGeneration(data: GenerateInput, controller: AbortController, cancelRun: () => Promise<void>): Promise<GenerateResult> {\n // Checked before the first `await`, so two calls in the same tick can't both pass.\n if (this.#disposed) throw new Error('Connection is closed')\n if (this.#isGenerating) {\n return this.#refuse('Ignored generate: a generation is already in progress', 'A generation is already in progress, please wait for it to finish')\n }\n this.#isGenerating = true\n this.#activeJob = { jobId: data.jobId, cancel: cancelRun }\n\n const command = 'generate'\n const { loadConfig, permissions } = this.#options\n let root = this.#options.root\n\n try {\n if (this.#isSandbox) root = await createJobRoot()\n await this.#hooks.callHook('studio:command:start', { command })\n const config = await loadConfig()\n const patch = data.config\n const plugins = await mergePlugins(config.plugins, patch?.plugins)\n const adapter = await mergeAdapter(config.adapter, patch?.adapter)\n\n // A sandbox agent always uses the inline spec (empty string included, since it has no disk\n // file); a non-sandbox agent always reads its spec from disk.\n const inputOverride = this.#isSandbox ? (patch?.input ?? '') : undefined\n\n if (permissions.allowWrite && this.#isSandbox) {\n await this.#warn('Running in a sandbox, so writing files is disabled')\n }\n\n if (patch?.input && !this.#isSandbox) {\n await this.#warn('Ignored the spec from Studio: generating from a Studio spec is only available to a sandbox agent')\n }\n\n const resolvedPlugins = plugins ?? config.plugins\n\n // The session's own emitter carries the run: the host's logger is already on it from\n // `connect`, and these two come off again below, so one run's listeners never see the next.\n // Cleared up front, filled the moment `kubb:generation:end` fires.\n this.#lastGeneration = undefined\n const disk = await runGenerationOperation({\n config: {\n ...config,\n root,\n input: inputOverride ?? config.input,\n storage: this.#canWrite ? fsStorage() : memoryStorage(),\n output: permissions.allowExec ? { ...config.output } : { ...config.output, format: false, lint: false, postGenerate: [] },\n plugins: resolvedPlugins,\n adapter,\n },\n hooks: this.#hooks,\n signal: controller.signal,\n jobId: data.jobId,\n store: this.#generations,\n snapshotRoot: this.#hasProjectOnDisk ? root : undefined,\n maxSnapshotMb: this.#limits.maxSnapshotMb,\n })\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `${resolvedPlugins.length} plugin${resolvedPlugins.length === 1 ? '' : 's'}, ${this.#canWrite ? 'written to disk' : 'in memory'}${inputOverride !== undefined ? ', from a Studio spec' : ''}`,\n })\n\n // The generate call above reassigns the field, but control flow analysis still sees the\n // `= undefined` from this method and narrows it to `never`.\n const generation = this.#lastGeneration as GenerationEnd | undefined\n const output = generation\n ? await this.#generations.keep({ jobId: data.jobId, source: 'output', files: generation.output, maxSetMb: this.#limits.maxMb })\n : undefined\n if (generation && output) {\n if (!output.paths.length && Object.keys(output.hashes).length) {\n await this.#warn('Kept only the hashes of this generation: its output is too large to keep')\n }\n const { peerDependencies, missingDependencies } = generation\n await this.#generations.add({ jobId: data.jobId, output, disk, peerDependencies, missingDependencies })\n }\n const files = [...(generation?.output.paths ?? [])]\n return {\n status: 'success',\n files,\n fileCount: files.length,\n hashes: output?.hashes ?? {},\n disk: disk ? { hashes: disk.hashes } : undefined,\n }\n } finally {\n // The store holds its own copy by now. Without `allowWrite` this output is the in-memory\n // storage with every generated file, so holding it until the next run wastes memory.\n this.#lastGeneration = undefined\n if (root !== this.#options.root) await removeJobRoot(root)\n this.#isGenerating = false\n }\n }\n\n async saveConfig(data: SaveConfigInput): Promise<SaveResult> {\n const command = 'saveConfig'\n await this.#hooks.callHook('studio:command:start', { command })\n const { configPath, configFile } = this.#options\n\n // Every RPC call gets one result. `edits` is checked before it is walked because values cross\n // the agent trust boundary.\n if (!Array.isArray(data.edits)) {\n await this.#warn('Ignored save: the message carried no edits')\n\n return { outcomes: [], changed: false }\n }\n\n const edits = data.edits\n const refuse = (reason: string): SaveResult => ({ outcomes: edits.map((edit) => ({ edit, applied: false, reason })), changed: false })\n\n if (!this.#canEditConfig) {\n await this.#warn('Ignored save: editing kubb.config.ts was not granted')\n\n return refuse('the agent was not granted permission to edit kubb.config.ts')\n }\n\n // A generation reloads the config while it runs, so rewriting the file underneath it would\n // leave that run working from half the change.\n if (this.#isGenerating) {\n return refuse('a generation is in progress')\n }\n\n try {\n const { writeConfigEdits } = await import('../operations/configFile.ts')\n const { source: patched, outcomes, changed } = await writeConfigEdits({ filePath: configFile, edits })\n\n const applied = outcomes.filter((outcome) => outcome.applied).length\n await this.#hooks.callHook('studio:command:end', { command, info: `applied ${applied}/${outcomes.length} edits to ${configPath}` })\n return { outcomes, changed, file: changed ? await this.#readConfigFileView(patched) : undefined }\n } catch (error) {\n // An unreadable config, a read-only filesystem. Reported as a refusal of every edit so\n // Studio hears back rather than waiting on a reply that never comes.\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n return refuse(getErrorMessage(error))\n }\n }\n\n async publishSnapshot(data: PublishSnapshotInput): Promise<PublishSnapshotResult> {\n const command = 'snapshot'\n await this.#hooks.callHook('studio:command:start', { command })\n\n if (this.#isSandbox) {\n return this.#refuse('Ignored snapshot: a sandbox agent has no project to build a package from', 'A sandbox agent has no project to build a package from')\n }\n\n const { name, version, bundledDependencies, uploadPath } = data\n\n if (!name || !version || !uploadPath) {\n return this.#refuse('Ignored snapshot: the message was missing required fields', 'The request was missing required fields')\n }\n\n const generation = await this.#generations.latest()\n\n if (!generation) {\n return this.#refuse('Ignored snapshot: no prior generation to pack', 'No prior generation exists to pack, run a generation first')\n }\n\n const bundled = new Set(bundledDependencies ?? [])\n const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency))\n\n if (missing.length) {\n return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(', ')}`, `Missing dependencies: ${missing.join(', ')}`)\n }\n\n try {\n const { createSnapshotPackage, uploadSnapshot } = await import('../operations/snapshotPackage.ts')\n const { files } = await this.#generations.read({ generation, source: 'output', paths: generation.output.paths })\n\n const { bytes, integrity } = await createSnapshotPackage(files, { name, version, peerDependencies: generation.peerDependencies })\n\n const { token, studioUrl, signal } = this.#options\n await uploadSnapshot({ bytes, uploadPath, studioUrl, token, shutdown: signal })\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `packed ${Object.keys(files).length} file${Object.keys(files).length === 1 ? '' : 's'}`,\n })\n return { integrity, peerDependencies: generation.peerDependencies }\n } catch (error) {\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n /** A sandbox has no project directory to snapshot. */\n get #hasProjectOnDisk(): boolean {\n return !this.#isSandbox && this.#canRead\n }\n\n async readFiles(data: ReadFilesInput): Promise<ReadFilesResult> {\n const command = 'readFiles'\n await this.#hooks.callHook('studio:command:start', { command })\n\n if (!this.#canRead) {\n return this.#refuse('Ignored files: reading generated files was not granted', 'The agent was not granted permission to read generated files', 'allowRead')\n }\n\n // `paths` came off the wire, so check its shape before walking it.\n if (!Array.isArray(data.paths)) {\n return this.#refuse('Ignored files: the message carried no paths', 'The request carried no paths')\n }\n\n const { paths } = data\n\n if (paths.length > MAX_FILES_PER_REQUEST) {\n return this.#refuse(\n `Ignored files: requested ${paths.length} paths, more than the ${MAX_FILES_PER_REQUEST} allowed per request`,\n `At most ${MAX_FILES_PER_REQUEST} paths may be requested at once`,\n )\n }\n\n if (typeof data.jobId !== 'string' || !data.jobId) {\n return this.#refuse('Ignored files: the message named no job', 'The request named no generation job')\n }\n\n const generation = await this.#generations.get(data.jobId)\n\n if (!generation) {\n return this.#refuse(`Ignored files: job ${data.jobId} is not kept on this agent`, GENERATION_GONE_MESSAGE)\n }\n\n if (!isValidPage(data)) {\n return this.#refuse(\n 'Ignored files: the message carried an invalid page',\n `The cursor must be a whole line from 0 and come with a limit of 1 to ${MAX_PAGE_LINES} lines`,\n )\n }\n\n const source = data.source === 'disk' ? 'disk' : 'output'\n\n if (!generation[source]) {\n return this.#refuse('Ignored files: that job has no snapshot of the files on disk', 'This agent kept no snapshot of the files on disk for that job')\n }\n\n // Only paths the set holds are read, never an arbitrary path.\n const result = await this.#generations.read({ generation, source, paths, cursor: data.cursor, limit: data.limit })\n\n await this.#hooks.callHook('studio:command:end', {\n command,\n info: `read ${Object.keys(result.files).length}/${paths.length} requested file${paths.length === 1 ? '' : 's'}`,\n })\n return result\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { IncompatibleAgentError, InvalidAgentTokenError } from '../operations/api.ts'\nimport { agentDefaults } from '../operations/constants.ts'\nimport { StudioSession, type StudioSessionOptions } from './StudioSession.ts'\n\nexport type ConnectionOutcome = 'shutdown' | 'stopped'\n\nexport type TokenRejection<TCredentials> = {\n error: InvalidAgentTokenError\n credentials: TCredentials\n /** Whether this credential already served a ready session. */\n live: boolean\n}\n\nexport type ConnectionOptions<TCredentials extends { token: string }> = {\n credentials: TCredentials\n clientOptions: (credentials: TCredentials) => Omit<StudioSessionOptions, 'token' | 'signal'>\n onTokenRejected: (rejection: TokenRejection<TCredentials>) => Promise<TCredentials | null>\n signal?: AbortSignal\n /** Used by the client facade once startup succeeds or schedules its first retry. */\n onStarted?: () => void\n}\n\n/** Runs the process's one connection loop, including retries and credential replacement. */\nexport async function runConnection<TCredentials extends { token: string }>({\n credentials,\n clientOptions,\n onTokenRejected,\n signal,\n onStarted,\n}: ConnectionOptions<TCredentials>): Promise<ConnectionOutcome> {\n let current = credentials\n const initialOptions = clientOptions(current)\n const instanceId = initialOptions.instanceId ?? randomUUID()\n let options = initialOptions\n let attempt = 0\n let live = false\n\n while (!signal?.aborted) {\n const session = new StudioSession({ ...options, instanceId, token: current.token, signal })\n try {\n await session.start()\n live = true\n attempt = 0\n onStarted?.()\n const end = await session.closed\n if (!end.retry) return signal?.aborted ? 'shutdown' : 'stopped'\n } catch (error) {\n if (signal?.aborted) return 'shutdown'\n if (error instanceof IncompatibleAgentError) throw error\n await session.dispose()\n const end = await session.closed\n // A terminal socket close during the handshake must not become a network retry.\n if (!end.retry && end.reason !== 'shutdown') return 'stopped'\n if (error instanceof InvalidAgentTokenError) {\n const next = await onTokenRejected({ error, credentials: current, live })\n if (!next) return 'stopped'\n current = next\n options = clientOptions(current)\n live = false\n attempt = 0\n continue\n }\n } finally {\n await session.dispose()\n }\n\n onStarted?.()\n const cap = Math.min(1_000 * 2 ** Math.min(attempt++, 16), options.retryInterval ?? agentDefaults.retryIntervalMs)\n const delayMs = Math.random() * cap\n await session.reportRetry(delayMs)\n try {\n await delay(delayMs, undefined, { signal })\n } catch (error) {\n if (!signal?.aborted) throw error\n }\n }\n return 'shutdown'\n}\n","import type { InvalidAgentTokenError } from '../operations/api.ts'\nimport type { StudioSessionOptions } from './StudioSession.ts'\nimport { runConnection } from './runConnection.ts'\n\nexport type ClientOptions = Omit<StudioSessionOptions, 'signal'> & {\n /** Called once when a background reconnect rejects the credential. */\n onAuthRequired?: (error: InvalidAgentTokenError) => void\n}\n\nexport type Client = {\n connect: () => Promise<void>\n disconnect: () => void\n}\n\n/** Compatibility facade over the shared connection loop. */\nexport function createClient({ onAuthRequired, ...options }: ClientOptions): Client {\n const shutdown = new AbortController()\n const started = Promise.withResolvers<void>()\n let resolved = false\n let running: Promise<unknown> | undefined\n\n return {\n connect() {\n running ??= runConnection({\n credentials: { token: options.token },\n clientOptions: () => options,\n signal: shutdown.signal,\n onStarted: () => {\n resolved = true\n started.resolve()\n },\n onTokenRejected: async ({ error }) => {\n if (!resolved) throw error\n onAuthRequired?.(error)\n return null\n },\n }).then(\n () => started.resolve(),\n (error: unknown) => started.reject(error),\n )\n return started.promise\n },\n disconnect() {\n shutdown.abort()\n },\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { toError } from '@internals/utils'\nimport { ofetch } from 'ofetch'\nimport { agentDefaults } from './constants.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * RFC 8628 device-authorization response from Studio's `/api/auth/device/code` endpoint.\n * Field names match the RFC; the CLI polls with `device_code` and shows `user_code` to the user.\n */\nexport type PairingSession = {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\nexport type PairingResult = {\n /**\n * Bearer token for this machine. Write credentials with mode 0600 and never log the value.\n */\n token: string\n agent: {\n /**\n * Stable agent id in Studio.\n */\n id: string\n /**\n * Short slug used in logs and the UI (for example `brave-otter`).\n */\n slug: string\n /**\n * Display name chosen at pairing time.\n */\n name: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none.\n */\n organizationSlug?: string\n }\n}\n\n/**\n * What this machine registers as. Any member may approve `cli` (`kubb studio`); only an admin may\n * approve `user` and `sandbox` (the Docker image). A `ci` agent never pairs.\n */\nexport type PairingAgentType = 'cli' | 'user' | 'sandbox'\n\n/** Labels, not secrets: a person approving the code in the browser is what authorizes a pairing. */\nconst CLIENT_IDS = { cli: 'kubb-cli', agent: 'kubb-agent' } as const\n\n/**\n * Thrown when a caller aborts `startPairing` or `pollForPairingToken` through their `signal`, such\n * as a `kubb studio` shutdown mid-pairing. Distinct from a denial or an expired code, so a host can\n * exit quietly instead of reporting a pairing failure.\n */\nexport class PairingCanceledError extends Error {\n constructor() {\n super('Pairing was canceled')\n this.name = 'PairingCanceledError'\n }\n}\n\n/** Thrown when the code expired unapproved. A fresh code can still succeed. */\nexport class PairingExpiredError extends Error {\n constructor(message = 'The pairing code expired, pair again') {\n super(message)\n this.name = 'PairingExpiredError'\n }\n}\n\n/** Thrown when the pairing was denied in the browser, including by the organization's agent limit. */\nexport class PairingDeniedError extends Error {\n constructor(message = 'Pairing was denied in the browser') {\n super(message)\n this.name = 'PairingDeniedError'\n }\n}\n\ntype StartPairingOptions = {\n studioUrl?: string\n type: PairingAgentType\n /**\n * Display name for the agent, usually the project or machine name.\n */\n name: string\n hostname: string\n /**\n * Aborting this cancels the request in flight and rejects with {@link PairingCanceledError}.\n */\n signal?: AbortSignal\n}\n\n/**\n * Asks Studio for a pairing code. The machine token travels with the request and is stored against\n * the code, so approval knows which machine it is pairing: the same machine pairing twice rotates\n * one agent's token instead of creating a second agent.\n */\nexport async function startPairing({ studioUrl = agentDefaults.studioUrl, type, name, hostname, signal }: StartPairingOptions): Promise<PairingSession> {\n try {\n return await ofetch<PairingSession>(`${studioUrl}/api/auth/device/code`, {\n method: 'POST',\n body: {\n client_id: type === 'cli' ? CLIENT_IDS.cli : CLIENT_IDS.agent,\n name,\n hostname,\n machine_token: await getMachineToken(),\n agent_kind: type === 'cli' ? undefined : type,\n },\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n throw error\n }\n}\n\ntype PollOptions = {\n studioUrl?: string\n session: PairingSession\n /**\n * Aborting this stops polling and rejects with {@link PairingCanceledError}, whether the abort\n * lands between polls or during the wait for the next one.\n */\n signal?: AbortSignal\n /** Called when a poll could not reach Studio. Polling carries on. */\n onRetry?: (error: Error) => void\n}\n\ntype PollError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied' | 'invalid_grant'\n\ntype PollResponse =\n | PairingResult\n | {\n error: PollError | string\n /**\n * Why, when Studio has something more useful to say than the RFC code. An approval that hits\n * the organization's agent limit comes back as `access_denied` with the limit spelled out.\n */\n error_description?: string\n }\n\nfunction isPairingResult(response: PollResponse | undefined): response is PairingResult {\n return !!response && typeof response === 'object' && 'token' in response && typeof response.token === 'string'\n}\n\n/**\n * Polls until the user approves or denies, honoring the server's `slow_down` back-off.\n *\n * Studio's own endpoint is used rather than the auth layer's `/device/token`, because an approved\n * Kubb pairing is worth an agent bearer token, not a user session.\n *\n * @throws {PairingExpiredError} when the code expires before anyone approves it.\n * @throws {PairingDeniedError} when the pairing is denied in the browser.\n * @throws {PairingCanceledError} when `signal` aborts.\n */\nexport async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, session, signal, onRetry }: PollOptions): Promise<PairingResult> {\n // Both fields cross the network, so neither is trusted as-is: a missing or zero `interval` would\n // spin the poll loop, and a missing or zero `expires_in` would expire the code before the first\n // poll. `> 0` is also false for `NaN` and for a missing field, so it doubles as the type guard.\n const deadline = Date.now() + (session.expires_in > 0 ? session.expires_in : 600) * 1000\n let intervalMs = (session.interval > 0 ? session.interval : 5) * 1000\n\n while (Date.now() < deadline) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n try {\n await delay(intervalMs, undefined, { signal })\n } catch {\n throw new PairingCanceledError()\n }\n\n let response: PollResponse | undefined\n try {\n response = await ofetch<PollResponse | undefined>(`${studioUrl}/api/agent/token`, {\n method: 'POST',\n body: { device_code: session.device_code },\n // A denial, an expiry, and \"not yet\" all come back as 4xx with a body the caller needs to\n // read, so let every response through and switch on `error` instead of catching.\n ignoreResponseError: true,\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n // Studio can be briefly unreachable (a deploy) while the code is still valid, so retry.\n onRetry?.(toError(error))\n continue\n }\n\n if (isPairingResult(response)) {\n return response\n }\n\n if (!response || typeof response !== 'object' || !('error' in response) || typeof response.error !== 'string') {\n throw new Error('Kubb Studio returned an empty pairing response, pair again')\n }\n\n if (response.error === 'authorization_pending') {\n continue\n }\n\n if (response.error === 'slow_down') {\n intervalMs += 5_000\n continue\n }\n\n if (response.error === 'access_denied') {\n throw new PairingDeniedError(response.error_description)\n }\n\n if (response.error === 'expired_token' || response.error === 'invalid_grant') {\n throw new PairingExpiredError(response.error_description)\n }\n\n throw new Error(response.error_description ?? `Pairing failed (${response.error})`)\n }\n\n throw new PairingExpiredError()\n}\n\ntype PairAgentOptions = StartPairingOptions & {\n /** Shows the code to whoever approves it. Called again for each fresh code. */\n onCode: (session: PairingSession, attempt: number) => void | Promise<void>\n /** Called when a poll could not reach Studio. Polling carries on. */\n onRetry?: (error: Error) => void\n /**\n * Codes to ask for in total when one expires unapproved. A denial or abort ends it at once.\n * @default 1\n */\n maxAttempts?: number\n}\n\n/**\n * Pairs this machine with Studio: asks for a code, hands it to the host to show, and waits for approval.\n */\nexport async function pairAgent({ onCode, onRetry, maxAttempts = 1, ...options }: PairAgentOptions): Promise<PairingResult> {\n for (let attempt = 1; ; attempt++) {\n const session = await startPairing(options)\n await onCode(session, attempt)\n\n try {\n return await pollForPairingToken({ studioUrl: options.studioUrl, session, signal: options.signal, onRetry })\n } catch (error) {\n if (!(error instanceof PairingExpiredError) || attempt >= maxAttempts) {\n throw error\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createStorage","fsDriver","styleText","randomBytes","hash","process","FetchError","delay","isAbsolute","relative","resolve","createHash","sep","fsStorage","stripVTControlCharacters","generationEventTypes","Diagnostics","RpcTarget","isLoopbackHost","AGENT_INSTANCE_HEADER","newWebSocketRpcSession","Diagnostics","mkdtemp","path","tmpdir","rm","resolveCacheDir","RpcTarget","process","randomUUID","AgentCloseCode","Hookable","memoryStorage","cacheStorage","kubbVersion","read","version","toPackageName","mergePlugins","mergeAdapter","fsStorage","createKubb","resolvePeerDependencies","GENERATION_GONE_MESSAGE","MAX_PAGE_LINES","randomUUID","delay","delay"],"sources":["../../../internals/utils/src/errors.ts","../../../internals/utils/src/promise.ts","../../../internals/utils/src/time.ts","../src/operations/machine.ts","../src/operations/api.ts","../src/operations/constants.ts","../package.json","../src/operations/generations.ts","../src/operations/generationEvents.ts","../src/operations/rpc.ts","../src/runtime/StudioSession.ts","../src/runtime/runConnection.ts","../src/runtime/client.ts","../src/operations/pair.ts"],"sourcesContent":["/**\n * Coerces an unknown thrown value to an `Error` instance.\n * Returns the value as-is when it is already an `Error`; otherwise wraps it with `String(value)`.\n *\n * @example\n * ```ts\n * try { ... } catch(err) {\n * throw new Error('Build failed', { cause: toError(err) })\n * }\n * ```\n */\nexport function toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value))\n}\n\n/**\n * Extracts a human-readable message from any thrown value.\n *\n * @example\n * ```ts\n * getErrorMessage(new Error('oops')) // 'oops'\n * getErrorMessage('plain string') // 'plain string'\n * ```\n */\nexport function getErrorMessage(value: unknown): string {\n return value instanceof Error ? value.message : String(value)\n}\n","/** A value that may already be resolved or still pending.\n *\n * @example\n * ```ts\n * function load(id: string): PossiblePromise<string> {\n * return cache.get(id) ?? fetchRemote(id)\n * }\n * ```\n */\nexport type PossiblePromise<T> = Promise<T> | T\n\n/** Returns `true` when `result` is a thenable `Promise`.\n *\n * @example\n * ```ts\n * isPromise(Promise.resolve(1)) // true\n * isPromise(42) // false\n * ```\n */\nexport function isPromise<T>(result: PossiblePromise<T>): result is Promise<T> {\n return result !== null && result !== undefined && typeof (result as Record<string, unknown>)['then'] === 'function'\n}\n\ntype Store<TKey, TValue> = {\n has(key: TKey): boolean\n get(key: TKey): TValue | undefined\n set(key: TKey, value: TValue): unknown\n}\n\n/**\n * Wraps `factory` with a keyed cache backed by the provided store.\n *\n * Pass a `WeakMap` for object keys (results are GC-eligible when the key is\n * collected) or a `Map` for primitive keys. For multi-argument functions,\n * nest two `memoize` calls — the outer keyed by the first argument, the\n * inner (created once per outer miss) keyed by the second.\n *\n * Because the cache is owned by the caller, it can be shared, inspected, or\n * cleared independently of the memoized function.\n *\n * @example Single WeakMap key\n * ```ts\n * const cache = new WeakMap<SchemaNode, Set<string>>()\n * const getRefs = memoize(cache, (node) => collectRefs(node))\n * ```\n *\n * @example Single Map key (primitive)\n * ```ts\n * const cache = new Map<string, Resolver>()\n * const getResolver = memoize(cache, (name) => buildResolver(name))\n * ```\n *\n * @example Two-level (object + primitive)\n * ```ts\n * const outer = new WeakMap<Params[], Map<string, Params[]>>()\n * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))\n * fn(params)('camelcase')\n * ```\n */\nexport function memoize<TKey, TValue>(store: Store<TKey, TValue>, factory: (key: TKey) => TValue): (key: TKey) => TValue {\n return (key: TKey): TValue => {\n if (store.has(key)) return store.get(key)!\n const value = factory(key)\n store.set(key, value)\n return value\n }\n}\n\ntype ParallelOptions<TItem> = {\n /**\n * Items to work through, handed out in order.\n */\n items: ReadonlyArray<TItem>\n /**\n * How many items may be in flight at once.\n */\n limit: number\n /**\n * Runs once per item, with the item's position so a caller can report progress.\n */\n run(item: TItem, index: number): Promise<void>\n}\n\n/**\n * Runs `run` over every item with at most `limit` in flight. Workers share one iterator, so each\n * takes the next item the moment it frees up instead of waiting for a batch to drain.\n *\n * @example\n * ```ts\n * await inParallel({ items: files, limit: 50, run: (file) => storage.writeItem(file.path, file.source) })\n * ```\n */\nexport async function inParallel<TItem>({ items, limit, run }: ParallelOptions<TItem>): Promise<void> {\n const queue = items.entries()\n\n const worker = async (): Promise<void> => {\n for (const [index, item] of queue) await run(item, index)\n }\n\n await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => worker()))\n}\n","/**\n * Calculates elapsed time in milliseconds from a high-resolution `process.hrtime` start time.\n * Rounds to 2 decimal places for sub-millisecond precision without noise.\n *\n * @example\n * ```ts\n * const start = process.hrtime()\n * doWork()\n * getElapsedMs(start) // 42.35\n * ```\n */\nexport function getElapsedMs(hrStart: [number, number]): number {\n const [seconds, nanoseconds] = process.hrtime(hrStart)\n const ms = seconds * 1000 + nanoseconds / 1e6\n return Math.round(ms * 100) / 100\n}\n\n/**\n * Converts a millisecond duration into a human-readable string (`ms`, `s`, or `m s`).\n *\n * @example\n * ```ts\n * formatMs(250) // '250ms'\n * formatMs(1500) // '1.50s'\n * formatMs(90000) // '1m 30.0s'\n * ```\n */\nexport function formatMs(ms: number): string {\n if (ms >= 60000) {\n const mins = Math.floor(ms / 60000)\n const secs = ((ms % 60000) / 1000).toFixed(1)\n return `${mins}m ${secs}s`\n }\n\n if (ms >= 1000) {\n return `${(ms / 1000).toFixed(2)}s`\n }\n return `${Math.round(ms)}ms`\n}\n","import { hash, randomBytes } from 'node:crypto'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { createStorage, type Storage } from 'unstorage'\nimport fsDriver from 'unstorage/drivers/fs'\n\n/**\n * Key-value storage the runtime uses for its machine secret and the last Studio config.\n *\n * One storage per process, since one process serves one config file. Hosts install their own\n * driver on startup: Nitro passes its `kubb` mount, the CLI an fs driver under `~/.kubb/cache`.\n * The in-memory default keeps the runtime usable without a host, at the cost of a machine\n * identity that changes on every restart.\n */\nlet storage: Storage = createStorage()\nlet hasInstalledStorage = false\n\n/**\n * Installs the storage driver the runtime persists to. Call once, before connecting.\n */\nexport function setStorage(next: Storage): void {\n storage = next\n hasInstalledStorage = true\n}\n\n/**\n * A storage backed by files under `base`, so the machine secret and the last Studio config\n * survive a restart. Repeated pairings of one machine depend on that secret staying put.\n */\nexport function createFileStorage(base: string): Storage {\n return createStorage({ driver: fsDriver({ base }) })\n}\n\nlet fallbackSecretPromise: Promise<string> | null = null\n\n/**\n * Loads the fallback machine secret from the runtime storage.\n * On first use it generates a secret and persists it, so the machine identity stays\n * stable across restarts. An identity that changes on every boot breaks session\n * creation with Studio whenever the startup registration call fails.\n */\nasync function loadOrCreateFallbackSecret(): Promise<string> {\n // The secret is memoized for the life of the process, so a host that reads the machine token\n // before installing its storage is bound to the throwaway in-memory default. Nothing else\n // surfaces that: the write succeeds, and the identity silently changes on every restart, which\n // Studio rejects with a 403 on the next session create.\n if (!hasInstalledStorage) {\n console.warn(\n styleText('yellow', 'Deriving the machine token before a storage driver was installed'),\n 'call setStorage() first, or set KUBB_AGENT_SECRET, to keep a stable machine identity across restarts',\n )\n }\n\n const stored = await storage.getItem('machine-secret').catch(() => null)\n\n if (typeof stored === 'string' && stored) {\n return stored\n }\n\n const secret = randomBytes(32).toString('hex')\n\n await storage.setItem('machine-secret', secret).catch(() => {\n console.warn(\n styleText('yellow', 'Could not persist the generated machine secret'),\n 'set KUBB_AGENT_SECRET to keep a stable machine identity across restarts',\n )\n })\n\n return secret\n}\n\n/**\n * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its\n * own identity without duplicating `getMachineToken`'s SHA-256 step.\n */\nexport function machineTokenFrom(secret: string): string {\n return hash('sha256', secret)\n}\n\n/**\n * Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.\n * Falls back to a generated secret persisted in the runtime storage if the env var is not set.\n * The token is hashed with SHA-256.\n */\nexport async function getMachineToken(): Promise<string> {\n if (process.env.KUBB_AGENT_SECRET) {\n return machineTokenFrom(process.env.KUBB_AGENT_SECRET)\n }\n\n fallbackSecretPromise ??= loadOrCreateFallbackSecret()\n\n return machineTokenFrom(await fallbackSecretPromise)\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { getErrorMessage } from '@internals/utils'\nimport { FetchError, ofetch } from 'ofetch'\nimport type { AgentCapacity, AgentRegisterInput, AgentRegisterResponse } from '../protocol/index.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * Reads a human-readable message from a Studio JSON error body, when it has one. `FetchError`'s own\n * message stops at the status line, so the detail Studio sends with a failure (an agent limit, a\n * revoked token) would otherwise never reach the user.\n */\nfunction responseMessage(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') {\n return undefined\n }\n\n const body = data as { error_description?: unknown; message?: unknown; error?: unknown }\n for (const value of [body.error_description, body.message, body.error]) {\n if (typeof value === 'string' && value) {\n return value\n }\n }\n\n return undefined\n}\n\n/**\n * Thrown when Studio rejects the agent token itself (401). Retrying cannot help: the token was\n * revoked, or the agent it belonged to was deleted in the Studio UI. Hosts catch this to forget\n * the stored credential and pair again.\n */\nexport class InvalidAgentTokenError extends Error {\n constructor(studioUrl: string, options?: ErrorOptions) {\n super(`Kubb Studio rejected this agent's token. It was revoked or the agent was deleted in ${studioUrl}.`, options)\n this.name = 'InvalidAgentTokenError'\n }\n}\n\n/**\n * Thrown when Studio refuses this agent's protocol version (426). Retrying cannot help until the\n * agent is upgraded, so hosts stop instead of reconnecting.\n */\nexport class IncompatibleAgentError extends Error {\n constructor(studioUrl: string, detail?: string, options?: ErrorOptions) {\n super(`Kubb Studio at ${studioUrl} requires a newer agent${detail ? `: ${detail}` : ''}. Upgrade @kubb/studio or the Kubb agent image.`, options)\n this.name = 'IncompatibleAgentError'\n }\n}\n\n/**\n * Whether a thrown value carries `statusCode`. Not narrowed to `FetchError`: a host wrapper can\n * throw its own error shape with the same field.\n */\nfunction rejectedWith(error: unknown, statusCode: number): boolean {\n return (error as { statusCode?: number } | undefined)?.statusCode === statusCode\n}\n\nfunction registrationError(cause: unknown): Error {\n const detail = (cause instanceof FetchError ? responseMessage(cause.data) : undefined) ?? getErrorMessage(cause)\n return new Error(detail ? `Failed to register with Kubb Studio: ${detail}` : 'Failed to register with Kubb Studio', { cause })\n}\n\ntype RegisterProps = {\n studioUrl: string\n token: string\n /** Names this agent process, so Studio tells two processes sharing one token apart. */\n instanceId: string\n capacity: AgentCapacity\n signal?: AbortSignal\n}\n\n/**\n * Registers this agent process with Kubb Studio (`POST /api/agent/connect`): binds the machine\n * identity to the token, reports what the process can take on, and gets back the URL of the one\n * socket it keeps open.\n *\n * Makes one bounded attempt; `runConnection` owns retries. A rejected token (401) throws\n * {@link InvalidAgentTokenError} and an unsupported agent version (426) throws\n * {@link IncompatibleAgentError}, since retrying either cannot help.\n */\nexport async function registerAgent({ token, studioUrl, instanceId, capacity, signal }: RegisterProps): Promise<AgentRegisterResponse> {\n const body: AgentRegisterInput = { machineToken: await getMachineToken(), instanceId, capacity }\n\n try {\n return await ofetch<AgentRegisterResponse>(`${studioUrl}/api/agent/connect`, {\n method: 'POST',\n headers: { Authorization: `Bearer ${token}` },\n body,\n retry: 0,\n timeout: 10_000,\n signal,\n })\n } catch (error) {\n if (rejectedWith(error, 401)) throw new InvalidAgentTokenError(studioUrl, { cause: error })\n if (rejectedWith(error, 426))\n throw new IncompatibleAgentError(studioUrl, error instanceof FetchError ? responseMessage(error.data) : undefined, { cause: error })\n throw registrationError(error)\n }\n}\n\n/**\n * Status values returned by Studio's jobs API.\n */\nexport type StudioJobStatus = 'queued' | 'running' | 'success' | 'failed' | 'canceled'\n\n/** How a snapshot's files differ from an earlier snapshot of the same package, relative to `output.path`. */\nexport type StudioSnapshotChanges = {\n /** The snapshot these changes are measured against, `null` when there is none to compare with. */\n base: {\n id: string\n version: string | null\n /** The commit the base snapshot was built from, when reported. */\n commit?: string\n createdAt: string\n } | null\n added: Array<string>\n changed: Array<string>\n removed: Array<string>\n}\n\n/**\n * `StudioSnapshotChanges` against a pull request's base branch. `baseFound` tells apart the two\n * reasons `base` can be `null`: `false` when no CI agent is registered for the branch yet (the\n * workflow has never run there), `true` when that agent exists but has no snapshot of this package\n * to compare with.\n */\nexport type StudioBranchSnapshotChanges = StudioSnapshotChanges & { baseFound: boolean }\n\n/**\n * Package view returned on a successful snapshot job from Studio.\n */\nexport type StudioSnapshot = {\n /**\n * Immutable snapshot id.\n */\n id: string\n /**\n * npm package name, or `null` when Studio stored none.\n */\n name: string | null\n /**\n * npm package version, or `null` when Studio stored none.\n */\n version: string | null\n /**\n * Subresource integrity hash for the tarball, or `null` when unavailable.\n */\n integrity: string | null\n /**\n * Preferable download path, often the readable `/packages/{agentSlug}/{name}.tgz` form.\n */\n url: string\n /**\n * Stable download path keyed by snapshot id.\n */\n snapshotIdUrl: string\n /**\n * ISO timestamp after which Studio may delete the tarball.\n */\n expiresAt: string\n /** What changed since the previous snapshot on the same agent. Absent when Studio or the agent predates it. */\n changes?: StudioSnapshotChanges\n /** What differs from the latest snapshot of the CI agent `baseId` names. Absent without a base. */\n branchChanges?: StudioBranchSnapshotChanges\n}\n\n/**\n * Job record from `POST /api/jobs` and `GET /api/jobs/{id}`.\n */\nexport type StudioJob = {\n /**\n * Job id returned by Studio when the job was queued.\n */\n id: string\n /**\n * Current status. Poll until `success`, `failed`, or `canceled`.\n */\n status: StudioJobStatus\n /**\n * Failure message when `status` is `failed`.\n */\n error?: string\n /**\n * Package view when a snapshot job finished successfully.\n */\n snapshot?: StudioSnapshot\n}\n\n/**\n * First wait before `createJob` retries a busy or queue-full response, absent a `Retry-After` hint.\n */\nconst CREATE_JOB_INITIAL_DELAY_MS = 1_000\n\n/**\n * Slowest `createJob` backs off to between retries.\n */\nconst CREATE_JOB_MAX_INTERVAL_MS = 10_000\n\n/**\n * Statuses worth retrying: the agent has no free connection yet (409, a stale conflict a moment\n * later resolves), its queue is momentarily full (429), or it has no live connection at all yet\n * (503, an agent process that is mid-reconnect). Anything else (404 agent not found, 401/403 auth)\n * is thrown straight away, since retrying cannot change the outcome.\n */\nconst CREATE_JOB_RETRYABLE_STATUSES = new Set([409, 429, 503])\n\n/**\n * Reads Studio's `Retry-After` header (seconds) off a thrown `ofetch` error, when present.\n */\nfunction retryAfterMs(error: unknown): number | undefined {\n const seconds = Number((error as { response?: Response }).response?.headers.get('retry-after'))\n return Number.isFinite(seconds) && seconds > 0 ? seconds * 1_000 : undefined\n}\n\n/**\n * Adds up to 30% jitter, so every CI run queued behind the same busy agent does not retry in\n * lockstep.\n */\nfunction withJitter(ms: number): number {\n return ms + Math.random() * ms * 0.3\n}\n\n/**\n * Queues a generation or snapshot job on Studio (`POST /api/jobs`).\n *\n * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.\n * Authenticates with the organization CI API key via `x-api-key`.\n *\n * A busy agent, a full queue, or a momentary lack of a live connection (409, 429, 503) retries with\n * exponential backoff and jitter, honoring Studio's `Retry-After` header when it sends one, up to\n * `timeoutMs`. Every other failure, including a missing agent (404), throws immediately.\n *\n * @example Snapshot job\n * ```ts\n * const job = await createJob({\n * studioUrl: 'https://kubb.studio',\n * token: process.env.KUBB_TOKEN!,\n * type: 'snapshot',\n * agentId: agent.id,\n * name: '@kubb/demo',\n * version: '1.0.0',\n * })\n * const finished = await waitForJob({ studioUrl, token, id: job.id })\n * ```\n */\nexport async function createJob({\n studioUrl,\n token,\n type,\n agentId,\n name,\n version,\n commit,\n baseId,\n config,\n instanceId,\n timeoutMs = 60_000,\n signal,\n}: {\n studioUrl: string\n token: string\n type: 'generation' | 'snapshot'\n agentId: string\n name?: string\n version?: string\n /** The commit this snapshot is built from, so the next one can diff against it. */\n commit?: string\n /** The `id` another CI agent's runs register under, such as the base branch's; this snapshot is also compared with its latest one. */\n baseId?: string\n config?: Record<string, unknown>\n /**\n * The agent process to run the job on, the `instanceId` its client connected with. A CI run passes\n * its own, so an overlapping pipeline under the same CI agent never builds its checkout.\n */\n instanceId?: string\n /**\n * How long to keep retrying a busy or queue-full response before giving up, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n signal?: AbortSignal\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = CREATE_JOB_INITIAL_DELAY_MS\n\n for (;;) {\n signal?.throwIfAborted()\n\n try {\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { type, agentId, name, version, commit, baseId, config, instanceId },\n retry: false,\n timeout: Math.max(deadline - Date.now(), 1),\n signal,\n })\n\n return job\n } catch (error) {\n signal?.throwIfAborted()\n\n const status = (error as { response?: { status?: number } }).response?.status\n if (!status || !CREATE_JOB_RETRYABLE_STATUSES.has(status) || Date.now() >= deadline) throw error\n\n const wait = Math.min(retryAfterMs(error) ?? withJitter(interval), Math.max(deadline - Date.now(), 0))\n if (signal) {\n await delay(wait, undefined, { signal })\n } else {\n await new Promise((resolve) => setTimeout(resolve, wait))\n }\n if (Date.now() >= deadline) throw error\n interval = Math.min(interval * 2, CREATE_JOB_MAX_INTERVAL_MS)\n }\n }\n}\n\n/**\n * A job runs a generation and packs a tarball, so it is never done the instant it is queued.\n */\nconst INITIAL_POLL_DELAY_MS = 2_000\n\n/**\n * Slowest the poll backs off to. Requests per run are roughly `timeoutMs` divided by this, and\n * every concurrent run on the same organization key draws on one budget.\n */\nconst MAX_POLL_INTERVAL_MS = 30_000\n\n/**\n * Polls `GET /api/jobs/{id}` until the job reaches a terminal status, waiting\n * {@link INITIAL_POLL_DELAY_MS} first and doubling up to {@link MAX_POLL_INTERVAL_MS} so a long\n * job stays inside the API key's rate limit.\n *\n * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the\n * deadline passes before Studio finishes.\n */\nexport async function waitForJob({\n studioUrl,\n token,\n id,\n timeoutMs = 60_000,\n signal,\n}: {\n studioUrl: string\n token: string\n id: string\n /**\n * How long to keep polling before throwing, in milliseconds.\n *\n * @default 60000\n */\n timeoutMs?: number\n signal?: AbortSignal\n}): Promise<StudioJob> {\n const deadline = Date.now() + timeoutMs\n let interval = INITIAL_POLL_DELAY_MS\n\n for (;;) {\n signal?.throwIfAborted()\n\n const wait = Math.max(Math.min(interval, deadline - Date.now()), 0)\n if (signal) {\n await delay(wait, undefined, { signal })\n } else {\n await new Promise((resolve) => setTimeout(resolve, wait))\n }\n\n if (Date.now() >= deadline) throw new Error('Timed out waiting for the Studio job')\n\n interval = Math.min(interval * 2, MAX_POLL_INTERVAL_MS)\n\n try {\n // ofetch retries a 429 immediately, which spends the rate limit faster than not retrying.\n const { job } = await ofetch<{ job: StudioJob }>(`${studioUrl}/api/jobs/${id}`, {\n headers: { 'x-api-key': token },\n retry: false,\n timeout: Math.max(deadline - Date.now(), 1),\n signal,\n })\n\n if (job.status === 'success' || job.status === 'failed' || job.status === 'canceled') return job\n } catch (error) {\n signal?.throwIfAborted()\n\n const response = (error as { response?: { status?: number; _data?: { data?: { tryAgainIn?: unknown } } } }).response\n\n if (response?.status !== 429) throw error\n\n const retryAfter = response._data?.data?.tryAgainIn\n const usable = typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter > 0\n\n // Studio's wait may exceed the ceiling, and a refusal must never shorten the next poll.\n interval = Math.max(interval, usable ? retryAfter : MAX_POLL_INTERVAL_MS)\n }\n }\n}\n\n/**\n * CI agent returned by {@link createAgent}. The token is issued only once, at creation or reuse.\n */\nexport type StudioAgent = {\n /**\n * Agent id, passed to {@link createJob} as `agentId`.\n */\n id: string\n /**\n * Human-readable slug, used to build the readable snapshot URL and the agent's Studio page.\n */\n slug: string\n /**\n * Agent display name.\n */\n name: string\n /**\n * Bearer token for the WebSocket agent session. Mask it before logging.\n */\n token: string\n}\n\n/**\n * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.\n * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of\n * consuming a new one from the organization's agent limit.\n */\nexport async function createAgent({\n studioUrl,\n token,\n name,\n machineToken,\n}: {\n studioUrl: string\n token: string\n name: string\n machineToken: string\n}): Promise<StudioAgent> {\n try {\n return await ofetch<StudioAgent>(`${studioUrl}/api/agents`, {\n method: 'POST',\n headers: { 'x-api-key': token },\n body: { name, machineToken },\n })\n } catch (error: unknown) {\n if (error instanceof FetchError) {\n const upgradeUrl = (error.data as { data?: { upgradeUrl?: string } } | undefined)?.data?.upgradeUrl\n const detail = responseMessage(error.data) ?? getErrorMessage(error)\n const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : ''\n throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error })\n }\n\n throw error\n }\n}\n","import type { AgentCapacity } from '../protocol/index.ts'\n\n/**\n * Hosted Kubb Studio URL. Exported so credential stores can bind tokens to the resolved instance,\n * not whatever default the client would pick on its own.\n */\nexport const defaultStudioUrl = 'https://kubb.studio'\n\n/**\n * Defaults the Studio client uses when a host passes nothing.\n * Config path is left out on purpose: each host discovers that itself.\n */\nexport const agentDefaults = {\n studioUrl: defaultStudioUrl,\n retryIntervalMs: 30_000,\n heartbeatIntervalMs: 30_000,\n /**\n * Slowest heartbeat a host may ask for. Studio drops an agent from the active list once its\n * stored ping is older than its liveness window, and it persists a ping at most once an hour,\n * so a slower cadence would make a healthy agent look dead after a single missed ping.\n */\n maxHeartbeatIntervalMs: 60_000,\n /** How long a heartbeat ping may take before the session is treated as dead. */\n heartbeatTimeoutMs: 10_000,\n maxConcurrent: 1,\n maxGenerations: 8,\n maxGenerationsMb: 100,\n maxSnapshotMb: 50,\n} as const\n\nfunction positiveNumber(value: string | undefined): number | undefined {\n const parsed = Number(value)\n return value && Number.isFinite(parsed) && parsed > 0 ? parsed : undefined\n}\n\n/**\n * How many generations an agent keeps and how large they may get, read from\n * `KUBB_AGENT_MAX_GENERATIONS`, `KUBB_AGENT_MAX_GENERATIONS_MB` and `KUBB_AGENT_MAX_SNAPSHOT_MB`.\n * An unset or invalid value keeps the default.\n */\nexport function resolveGenerationLimits(env: NodeJS.ProcessEnv = process.env): { maxCount: number; maxMb: number; maxSnapshotMb: number } {\n return {\n maxCount: Math.max(1, Math.floor(positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS) ?? agentDefaults.maxGenerations)),\n maxMb: positiveNumber(env.KUBB_AGENT_MAX_GENERATIONS_MB) ?? agentDefaults.maxGenerationsMb,\n maxSnapshotMb: positiveNumber(env.KUBB_AGENT_MAX_SNAPSHOT_MB) ?? agentDefaults.maxSnapshotMb,\n }\n}\n\n/**\n * An agent's capacity read from `KUBB_AGENT_MAX_CONCURRENT`. An unset or invalid value keeps the\n * default: one job at a time.\n */\nexport function resolveAgentCapacity(env: NodeJS.ProcessEnv = process.env): AgentCapacity {\n return {\n maxConcurrent: Math.max(1, Math.floor(positiveNumber(env.KUBB_AGENT_MAX_CONCURRENT) ?? agentDefaults.maxConcurrent)),\n }\n}\n","","import { createHash } from 'node:crypto'\nimport { isAbsolute, relative, resolve, sep } from 'node:path'\nimport { inParallel } from '@internals/utils'\nimport { fsStorage, type Storage } from '@kubb/core'\nimport type { FilePage, FileSource, ReadFilesResult } from '../protocol/index.ts'\n\nconst READ_CONCURRENCY = 50\nconst MB = 1024 * 1024\nconst INDEX_KEY = 'studio/generations.json'\n\n/**\n * Files a run produced, or the output directory held, keyed by path relative to `root`.\n */\nexport type SourceFiles = { storage: Storage; root: string; paths: Set<string> }\n\n/**\n * The path form {@link SourceFiles} keys on: relative to `root`, with forward slashes.\n */\nexport function relativeStoragePath({ root, filePath }: { root: string; filePath: string }): string {\n return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll('\\\\', '/')\n}\n\n/**\n * A set as kept in the store.\n */\ntype KeptSet = {\n /**\n * The paths a read is checked against. Empty when the set was too large to keep its content.\n */\n paths: Array<string>\n hashes: Record<string, string>\n bytes: number\n}\n\ntype KeptGeneration = {\n jobId: string\n /** When the store took it, in epoch milliseconds. Absent on an index written before it existed. */\n keptAt?: number\n output: KeptSet\n disk?: KeptSet\n peerDependencies: Record<string, string>\n missingDependencies: Array<string>\n}\n\nconst hashOf = (content: string) => createHash('sha1').update(content).digest('hex').slice(0, 16)\n\nconst totalBytes = (entries: Array<KeptGeneration>) => entries.reduce((sum, { output, disk }) => sum + output.bytes + (disk?.bytes ?? 0), 0)\n\n/**\n * What the output directory holds on disk before a run. `undefined` when `outputPath` is not a real\n * subdirectory of `root` (listing the root would take in every source file) or holds more than `maxFiles`.\n */\nexport async function listDisk({ root, outputPath, maxFiles }: { root: string; outputPath: string; maxFiles: number }): Promise<SourceFiles | undefined> {\n const outputDir = resolve(root, outputPath)\n const fromRoot = relative(resolve(root), outputDir)\n if (!fromRoot || fromRoot.startsWith('..') || fromRoot.startsWith(sep)) return undefined\n\n const storage = fsStorage()\n const keys = await storage.readKeys(outputDir)\n if (keys.length > maxFiles) return undefined\n\n const paths = keys\n .filter((key) => !key.split('/').includes('node_modules'))\n .map((key) => relative(resolve(root), resolve(outputDir, key)).replaceAll('\\\\', '/'))\n return { storage, root, paths: new Set(paths) }\n}\n\n/**\n * Keeps recent generations by job id in `storage`, with an index next to them so a store on disk\n * survives a restart. A generation is only ever looked up by its job id, so on a pooled sandbox one\n * tenant never reaches another's output. The oldest go past `maxCount` or `maxMb`, but the newest\n * always stays.\n */\nexport function createGenerationStore({\n storage,\n maxCount,\n maxMb,\n ttlMs,\n now = Date.now,\n}: {\n storage: Storage\n maxCount: number\n maxMb: number\n /** How long a generation stays readable. Unset, it stays until count or size pushes it out. */\n ttlMs?: number\n now?: () => number\n}) {\n let index: Array<KeptGeneration> | undefined\n\n // An entry from before `keptAt` existed never expires, so upgrading drops nothing.\n const isLive = (generation: KeptGeneration) => ttlMs === undefined || generation.keptAt === undefined || now() - generation.keptAt < ttlMs\n\n // Hashed so a job id from the wire can never point outside the store.\n const dirOf = (jobId: string) => `studio/generations/${hashOf(jobId)}/`\n\n async function load(): Promise<Array<KeptGeneration>> {\n if (index) return index\n const stored = await storage.readItem(INDEX_KEY).catch(() => null)\n try {\n index = stored ? (JSON.parse(stored) as Array<KeptGeneration>) : []\n } catch {\n index = []\n }\n return index\n }\n\n /**\n * Copies `files` into the store as one set of `jobId`. Above `maxSetMb` only the hashes are kept.\n */\n async function keep({ jobId, source, files, maxSetMb }: { jobId: string; source: FileSource; files: SourceFiles; maxSetMb: number }): Promise<KeptSet> {\n const maxSetBytes = maxSetMb * MB\n const hashes: Record<string, string> = {}\n let bytes = 0\n await inParallel({\n items: [...files.paths],\n limit: READ_CONCURRENCY,\n run: async (path) => {\n // Output outside the root would land outside the store too.\n if (path.split('/').includes('..')) return\n const content = await files.storage.readItem(resolve(files.root, path))\n if (content === null) return\n hashes[path] = hashOf(content)\n bytes += Buffer.byteLength(content)\n if (bytes <= maxSetBytes) await storage.writeItem(`${dirOf(jobId)}${source}/${path}`, content)\n },\n })\n if (bytes > maxSetBytes) {\n await storage.empty(`${dirOf(jobId)}${source}/`)\n return { paths: [], hashes, bytes: 0 }\n }\n return { paths: Object.keys(hashes), hashes, bytes }\n }\n\n async function drop(jobId: string): Promise<void> {\n await storage.empty(dirOf(jobId))\n }\n\n return {\n keep,\n drop,\n get: async (jobId: string) => (await load()).find((generation) => generation.jobId === jobId && isLive(generation)),\n latest: async () => (await load()).filter(isLive).at(-1),\n /** Total bytes of every set the store holds, expired ones too until the next add drops them. */\n bytes: async () => totalBytes(await load()),\n async add(generation: KeptGeneration): Promise<void> {\n const current = await load()\n for (const expired of current.filter((entry) => !isLive(entry))) await drop(expired.jobId)\n const entries = current.filter((entry) => entry.jobId !== generation.jobId && isLive(entry))\n entries.push({ ...generation, keptAt: now() })\n while (entries.length > 1 && (entries.length > maxCount || totalBytes(entries) > maxMb * MB)) {\n await drop(entries.shift()!.jobId)\n }\n index = entries\n await storage.writeItem(INDEX_KEY, JSON.stringify(entries))\n },\n /**\n * Reads the requested paths the set holds, skipping any it does not, one page of lines when `limit` is given.\n */\n async read({\n generation,\n source,\n paths,\n cursor = 0,\n limit,\n }: {\n generation: KeptGeneration\n source: FileSource\n paths: Array<string>\n cursor?: number\n limit?: number\n }): Promise<ReadFilesResult> {\n const kept = new Set(generation[source]?.paths)\n const files: Record<string, string> = {}\n const pages: Record<string, FilePage> = {}\n await inParallel({\n items: paths.filter((path) => kept.has(path)),\n limit: READ_CONCURRENCY,\n run: async (path) => {\n const content = await storage.readItem(`${dirOf(generation.jobId)}${source}/${path}`)\n if (content === null) return\n if (limit === undefined) {\n files[path] = content\n return\n }\n const lines = content.split(/\\r\\n|\\r|\\n/)\n const end = Math.min(cursor + limit, lines.length)\n files[path] = lines.slice(cursor, end).join('\\n')\n pages[path] = { nextCursor: end < lines.length ? end : null, totalLines: lines.length }\n },\n })\n return limit === undefined ? { files } : { files, pages }\n },\n }\n}\n\nexport type GenerationStore = ReturnType<typeof createGenerationStore>\n","import { stripVTControlCharacters } from 'node:util'\nimport { getElapsedMs } from '@internals/utils'\nimport { Diagnostics, type Hookable, type KubbHooks } from '@kubb/core'\nimport { type GenerationEvent, type GenerationEventPayloads, type GenerationEventType, generationEventTypes } from '../protocol/index.ts'\nimport { relativeStoragePath } from './generations.ts'\n\n/** How each published hook's context becomes its wire payload; a hook without a projection fails to compile. */\ntype Projections = { [Type in GenerationEventType]: (...args: KubbHooks[Type]) => GenerationEventPayloads[Type] }\n\nconst MAX_QUEUED_EVENTS = 1_024\nconst RESERVED_EVENTS = 64\nconst isDiscardable = (event: GenerationEvent) => event.type === 'kubb:files:processing:update' || event.type === 'kubb:info' || event.type === 'kubb:success'\n\n/** Forwards selected Kubb lifecycle events to a native Cap'n Web stream. */\nexport function createGenerationStream(\n hooks: Hookable<KubbHooks>,\n jobId: string,\n): { stream: ReadableStream<GenerationEvent>; close: () => Promise<void>; dispose: () => void; fail: (error: unknown) => void } {\n const unhooks: Array<() => void> = []\n let root = ''\n let controller!: ReadableStreamDefaultController<GenerationEvent>\n let closed = false\n const stream = new ReadableStream<GenerationEvent>(\n {\n start: (value) => {\n controller = value\n },\n cancel: () => {\n closed = true\n detach()\n },\n },\n { highWaterMark: MAX_QUEUED_EVENTS },\n )\n\n /**\n * Registers a listener and keeps its remover, so one generation's listeners come off the session\n * emitter again when that generation ends.\n */\n function on<TName extends keyof KubbHooks & string>(name: TName, handler: (...args: KubbHooks[TName]) => unknown): void {\n unhooks.push(hooks.hook(name, handler))\n }\n\n function emitEvent<Type extends GenerationEventType>(type: Type, data: GenerationEventPayloads[Type]): void {\n if (closed) return\n const event = { jobId, type, data, version: 1 as const, timestamp: Date.now() } as unknown as GenerationEvent\n if (controller.desiredSize! <= RESERVED_EVENTS && isDiscardable(event)) return\n if (controller.desiredSize! <= 0) {\n fail(new Error(`Generation event stream exceeded ${MAX_QUEUED_EVENTS} queued events`))\n return\n }\n controller.enqueue(event)\n }\n\n const projections: Projections = {\n 'kubb:plugin:start': ({ plugin }) => [{ plugin: { name: plugin.name } }],\n 'kubb:plugin:end': ({ plugin, duration, success }) => [{ plugin: { name: plugin.name }, duration, success }],\n 'kubb:build:start': ({ config, adapter }) => [{ config: { name: config.name }, adapter: { name: adapter.name } }],\n 'kubb:build:end': ({ files, config, outputDir }) => [\n { files: files.map((file) => ({ path: relativeStoragePath({ root: config.root, filePath: file.path }), name: file.name })), outputDir },\n ],\n 'kubb:files:processing:start': ({ files }) => [{ total: files.length }],\n 'kubb:files:processing:update': ({ files }) => [\n {\n files: files.map(({ file, processed, total, percentage }) => ({\n file: relativeStoragePath({ root, filePath: file.path }),\n processed,\n total,\n percentage,\n })),\n },\n ],\n 'kubb:files:processing:end': ({ files }) => [{ total: files.length }],\n 'kubb:info': ({ message, info }) => [{ message: stripVTControlCharacters(message), info }],\n 'kubb:success': ({ message, info }) => [{ message: stripVTControlCharacters(message), info }],\n 'kubb:warn': ({ message, info }) => [{ message: stripVTControlCharacters(message), info }],\n 'kubb:error': ({ error }) => [{ message: error.message, stack: error.stack }],\n 'kubb:diagnostic': ({ diagnostic }) => [\n {\n code: diagnostic.code,\n message: diagnostic.message,\n severity: diagnostic.severity,\n location: 'location' in diagnostic ? diagnostic.location : undefined,\n help: 'help' in diagnostic ? diagnostic.help : undefined,\n plugin: 'plugin' in diagnostic ? diagnostic.plugin : undefined,\n stack: 'cause' in diagnostic ? diagnostic.cause?.stack : undefined,\n },\n ],\n 'kubb:generation:start': ({ config }) => [{ name: config.name, plugins: config.plugins.length }],\n 'kubb:generation:end': () => [],\n 'kubb:generation:summary': ({ duration, fileCount, failedPlugins, status }) => [{ duration, fileCount, failedPlugins, status }],\n 'kubb:lifecycle:start': () => [],\n 'kubb:lifecycle:end': () => [],\n 'kubb:format:start': () => [],\n 'kubb:format:end': () => [],\n 'kubb:lint:start': () => [],\n 'kubb:lint:end': () => [],\n 'kubb:hooks:start': () => [],\n 'kubb:hooks:end': () => [],\n 'kubb:hook:start': ({ id, command, args }) => [{ id, command, args: args ? [...args] : undefined }],\n 'kubb:hook:line': ({ id, line }) => [{ id, line }],\n 'kubb:hook:end': ({ id, command, args, success, error }) => [\n { id, command, args: args ? [...args] : undefined, success, error: error ? { message: error.message, stack: error.stack } : undefined },\n ],\n }\n\n function forward<Type extends GenerationEventType>(type: Type): void {\n on(type, (...args: KubbHooks[Type]) => emitEvent(type, projections[type](...args)))\n }\n\n // Paths in `kubb:files:processing:update` are made relative to the root the build announced.\n on('kubb:build:start', ({ config }) => {\n root = config.root\n })\n\n for (const type of generationEventTypes) forward(type)\n\n // Registered after the loop so the summary follows its `kubb:generation:end`; core never emits it.\n on('kubb:generation:end', ({ diagnostics = [], status, hrStart, filesCreated }) => {\n if (!hrStart) {\n return\n }\n\n const duration = Math.round(getElapsedMs(hrStart))\n\n emitEvent('kubb:generation:summary', [\n { duration, fileCount: filesCreated ?? 0, failedPlugins: Diagnostics.failedPlugins(diagnostics).length, status: status ?? 'success' },\n ])\n })\n\n /**\n * Takes this generation's listeners off the session emitter. Safe to call twice.\n */\n function detach(): void {\n for (const unhook of unhooks) unhook()\n unhooks.length = 0\n }\n\n async function close(): Promise<void> {\n if (closed) {\n return\n }\n closed = true\n detach()\n controller.close()\n }\n\n function fail(error?: unknown): void {\n detach()\n if (closed) {\n return\n }\n closed = true\n controller.error(error)\n }\n\n return { stream, close, dispose: () => fail(), fail }\n}\n","import { newWebSocketRpcSession, RpcTarget } from 'capnweb'\nimport type {\n AgentApi,\n GenerateInput,\n PublishSnapshotInput,\n ReadFilesInput,\n RpcClose,\n RpcConnection,\n RpcConnector,\n SaveConfigInput,\n StudioApi,\n} from '../protocol/index.ts'\nimport { AGENT_INSTANCE_HEADER } from '../protocol/index.ts'\nimport { isLoopbackHost } from './url.ts'\n\nconst CONNECT_TIMEOUT_MS = 5_000\n\n/**\n * The only methods Studio may call on an agent. A `StudioSession` carries far more than\n * {@link AgentApi}, so it is wrapped rather than exposed: what Cap'n Web can reach is exactly what\n * this class re-declares.\n */\nclass AgentRpcTarget extends RpcTarget implements AgentApi {\n constructor(private readonly api: AgentApi) {\n super()\n }\n\n connect() {\n return this.api.connect()\n }\n startGeneration(input: GenerateInput) {\n return this.api.startGeneration(input)\n }\n saveConfig(input: SaveConfigInput) {\n return this.api.saveConfig(input)\n }\n publishSnapshot(input: PublishSnapshotInput) {\n return this.api.publishSnapshot(input)\n }\n readFiles(input: ReadFilesInput) {\n return this.api.readFiles(input)\n }\n cancel(jobId: string) {\n return this.api.cancel(jobId)\n }\n}\n\n/** Node's `WebSocket` takes undici's `headers` option, which the DOM typing leaves out. */\ntype NodeWebSocket = new (url: string, init: { headers: Record<string, string> }) => WebSocket\n\n/**\n * Opens a Studio WebSocket, closing it when the handshake exceeds {@link CONNECT_TIMEOUT_MS}. Node\n * fires no `close` event when the handshake itself fails, only `error`, so `closed` settles as 1006 there.\n */\nfunction openSocket({ url, headers }: { url: string; headers: Record<string, string> }): { socket: WebSocket; closed: Promise<RpcClose> } {\n const socket = new (WebSocket as unknown as NodeWebSocket)(url, { headers })\n\n const closed = new Promise<RpcClose>((resolve) => {\n let opened = false\n const timer = setTimeout(() => {\n if (socket.readyState === WebSocket.CONNECTING) {\n socket.close(3008, 'Connection timeout')\n }\n }, CONNECT_TIMEOUT_MS)\n\n socket.addEventListener(\n 'open',\n () => {\n opened = true\n clearTimeout(timer)\n },\n { once: true },\n )\n socket.addEventListener(\n 'close',\n (event) => {\n clearTimeout(timer)\n resolve({ code: event.code, reason: event.reason })\n },\n { once: true },\n )\n socket.addEventListener('error', () => {\n if (opened) return\n clearTimeout(timer)\n resolve({ code: 1006, reason: '' })\n })\n })\n\n return { socket, closed }\n}\n\n/**\n * Opens an authenticated Cap'n Web session to Studio over a WebSocket. Rejects an unencrypted URL\n * before opening the socket, so a bearer token never reaches a plaintext host.\n *\n * @example\n * ```ts\n * const rpc = await connectWebSocketRpc({ url: 'wss://studio.kubb.dev/s/1', token, local: session })\n * await rpc.studio.ping()\n * ```\n */\nexport const connectWebSocketRpc: RpcConnector = async ({ url, token, instanceId, local }): Promise<RpcConnection> => {\n const { protocol, hostname, host } = new URL(url)\n if (protocol !== 'wss:' && !(protocol === 'ws:' && isLoopbackHost(hostname))) {\n throw new Error(`Refusing unencrypted WebSocket to ${host}`)\n }\n\n const { socket, closed } = openSocket({ url, headers: { Authorization: `Bearer ${token}`, [AGENT_INSTANCE_HEADER]: instanceId } })\n const studio = newWebSocketRpcSession<StudioApi>(socket, new AgentRpcTarget(local))\n studio.onRpcBroken(() => socket.close())\n\n return {\n studio,\n closed,\n close: () => studio[Symbol.dispose](),\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { getErrorMessage, read, toError } from '@internals/utils'\nimport {\n cacheStorage,\n type Config,\n createKubb,\n type Diagnostic,\n Diagnostics,\n fsStorage,\n Hookable,\n type KubbHooks,\n memoryStorage,\n resolveCacheDir,\n} from '@kubb/core'\nimport { version as kubbVersion } from '../../package.json'\nimport {\n type AgentApi,\n type AgentCapacity,\n type AgentRegisterResponse,\n type AgentLoad,\n type AgentPermissions,\n type ConfigFileView,\n type ConnectMessagePayload,\n type GenerateInput,\n type GenerateResult,\n type GenerationEvent,\n type GenerationRun,\n GENERATION_GONE_MESSAGE,\n MAX_FILES_PER_REQUEST,\n MAX_PAGE_LINES,\n type ReadFilesInput,\n type ReadFilesResult,\n type SaveConfigInput,\n type SaveResult,\n type PublishSnapshotInput,\n type PublishSnapshotResult,\n AgentCloseCode,\n type RpcClose,\n type RpcConnector,\n type RpcConnection,\n} from '../protocol/index.ts'\nimport { registerAgent } from '../operations/api.ts'\nimport { agentDefaults, resolveAgentCapacity, resolveGenerationLimits } from '../operations/constants.ts'\nimport { mergeAdapter, mergePlugins, resolvePeerDependencies, toPackageName } from '../operations/resolveConfig.ts'\nimport { RpcTarget } from 'capnweb'\nimport { createGenerationStore, type GenerationStore, listDisk, relativeStoragePath } from '../operations/generations.ts'\nimport { createGenerationStream } from '../operations/generationEvents.ts'\nimport { connectWebSocketRpc } from '../operations/rpc.ts'\n\nconst DISK_SNAPSHOT_MAX_FILES = 10_000\n\n/**\n * Folds error-severity diagnostics into one thrown error so logs name the failing plugin or pass.\n */\nfunction generationFailure(diagnostics: ReadonlyArray<Diagnostic>): Error {\n const reasons = diagnostics\n .filter(Diagnostics.isProblem)\n .filter((diagnostic) => diagnostic.severity === 'error')\n .map((diagnostic) => (diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message))\n\n if (!reasons.length) return new Error('Generation failed')\n\n return new Error(`Generation failed: ${reasons.length} error${reasons.length === 1 ? '' : 's'}: ${reasons.join('; ')}`)\n}\n\n/** A sandbox shares one in-memory generation store across tenants, so old generations expire. */\nconst SANDBOX_GENERATION_TTL_MS = 15 * 60_000\n\n/**\n * A fresh root for one sandbox job. Kubb keys its output manifest cache by root, so tenants that\n * shared the agent's own root would read each other's manifest.\n */\nfunction createJobRoot(): Promise<string> {\n return mkdtemp(path.join(tmpdir(), 'kubb-job-'))\n}\n\n/**\n * Removes a job root and the manifest cache Kubb derived from it. Best effort: a leftover temp\n * directory must not fail a job that already finished.\n */\nasync function removeJobRoot(jobRoot: string): Promise<void> {\n await Promise.all([rm(jobRoot, { recursive: true, force: true }), rm(resolveCacheDir(jobRoot), { recursive: true, force: true })]).catch(() => {})\n}\n\nclass GenerationRunTarget extends RpcTarget implements GenerationRun {\n constructor(\n private readonly generationStream: ReadableStream<GenerationEvent>,\n private readonly generationResult: Promise<GenerateResult>,\n private readonly cancelGeneration: () => Promise<void>,\n /** Stops the run. Cap'n Web calls this on explicit disposal and on a dropped session alike. */\n private readonly stopGeneration: () => void,\n ) {\n super()\n }\n\n async events() {\n return this.generationStream\n }\n result() {\n return this.generationResult\n }\n cancel() {\n return this.cancelGeneration()\n }\n // Cap'n Web also calls this when the peer session is torn down, so a dropped socket stops the run.\n [Symbol.dispose]() {\n this.stopGeneration()\n }\n}\n\nexport type StudioSessionOptions = {\n connector?: RpcConnector\n token: string\n studioUrl?: string\n configPath: string\n /**\n * Loads the on-disk Kubb config. Injected so each host resolves config its own way: the Docker\n * agent from an explicit `KUBB_AGENT_CONFIG` path, the CLI through the same discovery\n * `kubb generate` uses.\n */\n loadConfig: () => Promise<Config>\n /**\n * The runtime's own version, reported to Studio next to the `kubb` version.\n */\n version: string\n /**\n * What Studio may do in this project, off unless the host grants it. A sandbox session narrows\n * them further: it never writes to disk and never edits a config file, and it always generates\n * from the spec Studio sends.\n */\n permissions?: Partial<AgentPermissions>\n root?: string\n /**\n * Maximum reconnect backoff in milliseconds.\n */\n retryInterval?: number\n /**\n * Milliseconds between keep-alive pings, clamped to `agentDefaults.maxHeartbeatIntervalMs`.\n * Raise it to halve the traffic and database writes a long-lived agent costs, at the price of\n * Studio taking that much longer to notice the agent has gone. Lower it in development to see\n * connection state move immediately.\n */\n heartbeatInterval?: number\n /**\n * What this agent process can take on, reported to Studio at registration. Unset fields come from\n * `KUBB_AGENT_MAX_CONCURRENT`.\n */\n capacity?: Partial<AgentCapacity>\n /**\n * Names this agent process to Studio, sent at registration and as {@link AGENT_INSTANCE_HEADER}\n * on the socket. `runConnection` sets one per process, so a reconnect is the same instance and a\n * restart is a new one. Not meant to be set directly by a host.\n */\n instanceId?: string\n /**\n * Aborting this disconnects the session and stops the reconnect loop. Hosts wire it to their own\n * shutdown signal, usually `SIGINT` or `SIGTERM`.\n */\n signal?: AbortSignal\n /**\n * Installs listeners on the session's event emitter, which carries both the session events and\n * the generations it runs. Left out, the runtime prints nothing, which is what a library should\n * default to.\n */\n installLogger?: (hooks: Hookable<KubbHooks>) => void | Promise<void>\n}\n\n/**\n * A session's options with every default filled in, so nothing downstream repeats a fallback.\n */\ntype ResolvedOptions = StudioSessionOptions & {\n studioUrl: string\n root: string\n permissions: AgentPermissions\n heartbeatInterval: number\n capacity: AgentCapacity\n instanceId: string\n /**\n * Absolute path to the config file, for reading and patching it. `configPath` keeps the form the\n * host gave, which is what Studio shows.\n */\n configFile: string\n}\n\n/**\n * Fills in a host's options: the hosted Studio URL, the current working directory, and every\n * permission off unless granted. Idempotent, so a reconnect can pass an already-resolved bag\n * back in.\n */\nfunction applyStudioDefaults(options: StudioSessionOptions): ResolvedOptions {\n const root = options.root ?? process.cwd()\n\n return {\n ...options,\n studioUrl: options.studioUrl ?? agentDefaults.studioUrl,\n root,\n // `configPath` is relative to the agent's root unless it is already absolute, which is what\n // `resolve` does on its own.\n configFile: path.resolve(root, options.configPath),\n permissions: { allowWrite: false, allowConfigEdit: false, allowExec: false, allowRead: false, ...options.permissions },\n // Studio counts an agent offline once its last ping is older than its liveness window, so a\n // slower cadence would make a healthy agent invisible. Clamped here rather than in a host's\n // env parsing, so every host is held to the contract.\n heartbeatInterval: Math.min(options.heartbeatInterval ?? agentDefaults.heartbeatIntervalMs, agentDefaults.maxHeartbeatIntervalMs),\n capacity: { ...resolveAgentCapacity(), ...options.capacity },\n instanceId: options.instanceId ?? randomUUID(),\n }\n}\n\n/**\n * Jobs one agent process can run at once today. Two runs would share this session's hook emitter,\n * and with it each other's events, so the connection keeps this limit.\n */\nconst RUNTIME_MAX_CONCURRENT = 1\n\n/**\n * How a session ends: what the host is told, and whether it reconnects.\n */\ntype SessionEnd = {\n reason: string\n retry: boolean\n /** Reported through `studio:error` when the end needs the user to act. */\n error?: Error\n}\n\n/**\n * Reads what Studio meant by closing the connection. A code Studio did not send on purpose is an\n * ordinary drop, and the agent reconnects as it always has.\n */\nfunction planEnd(close: RpcClose | void): SessionEnd {\n const code = close?.code\n // Every connection attempt registers first, so reconnecting is how the agent registers again.\n if (code === AgentCloseCode.REAUTHENTICATE) return { reason: 'Kubb Studio asked the agent to register again', retry: true }\n if (code === AgentCloseCode.SUPERSEDED) return { reason: 'another instance of this agent took over', retry: false }\n if (code === AgentCloseCode.INCOMPATIBLE) {\n return {\n reason: 'this agent is too old for Kubb Studio, or was deleted',\n retry: false,\n error: new Error('Kubb Studio closed the connection: this agent is too old for it, or was deleted. Upgrade the agent, or pair it again.'),\n }\n }\n return { reason: 'connection closed', retry: true }\n}\n\nfunction isValidPage({ cursor, limit }: Pick<ReadFilesInput, 'cursor' | 'limit'>): boolean {\n if (limit === undefined) return cursor === undefined\n if (cursor !== undefined && (!Number.isSafeInteger(cursor) || cursor < 0)) return false\n return Number.isSafeInteger(limit) && limit >= 1 && limit <= MAX_PAGE_LINES\n}\n\n/**\n * One agent-to-Studio RPC transport: opening it, keeping it alive, and serving remote methods.\n * `runConnection` owns the lifetime and retry of each attempt.\n */\nexport class StudioSession implements AgentApi {\n readonly #options: ResolvedOptions\n // Each session gets its own isolated event emitter so generation events from one session do not\n // bleed into another session's WebSocket stream.\n readonly #hooks = new Hookable<KubbHooks>()\n /**\n * Removers for every listener this session added (socket, shutdown signal, hooks) so `dispose`\n * detaches them in one pass. Listeners a host attached itself through `installLogger` survive.\n */\n readonly #unhooks: Array<() => void> = []\n\n /**\n * What registration handed back, and the marker for whether the agent registered at all.\n * Before it resolves there is no sandbox flag to read.\n */\n #registration: AgentRegisterResponse | undefined\n #rpc: RpcConnection | undefined\n\n // Whether the session is over: guards the close event from tearing down twice, and a shutdown\n // from being turned into a reconnect.\n #disposed = false\n // Guards against a second `generate` command starting while one is already running. Without\n // this, two concurrent `generate()` calls share this socket via `setupEventsStream`, and their\n // events interleave with no way for Studio to tell the two runs apart.\n #isGenerating = false\n #activeJob: { jobId: string; cancel: () => Promise<void> } | undefined\n #heartbeatTimer: ReturnType<typeof setTimeout> | undefined\n #store: GenerationStore | undefined\n readonly #limits = resolveGenerationLimits()\n /**\n * Resolves when Studio calls {@link StudioSession.connect}. `studio:ready` waits on this so the\n * host does not queue jobs before the agent session is registered.\n */\n readonly #connectAck = Promise.withResolvers<void>()\n readonly #ended = Promise.withResolvers<SessionEnd>()\n readonly closed = this.#ended.promise\n #generationResult: Promise<GenerateResult> | undefined\n\n constructor(options: StudioSessionOptions) {\n this.#options = applyStudioDefaults(options)\n // dispose() may reject this before start() awaits it\n void this.#connectAck.promise.catch(() => {})\n }\n\n /**\n * A sandbox agent runs on Studio's own infrastructure, so it has no user project to touch.\n */\n get #isSandbox(): boolean {\n return this.#registration?.isSandbox === true\n }\n\n /**\n * Kept in the project's cache directory, so it survives a restart, except on a sandbox: its pool\n * sessions run every tenant's jobs, so it keeps them in memory. Looked up by job id only.\n */\n get #generations(): GenerationStore {\n this.#store ??= createGenerationStore({\n storage: this.#isSandbox ? memoryStorage() : cacheStorage({ root: this.#options.root }),\n maxCount: this.#limits.maxCount,\n maxMb: this.#limits.maxMb,\n ttlMs: this.#isSandbox ? SANDBOX_GENERATION_TTL_MS : undefined,\n })\n return this.#store\n }\n\n get #canWrite(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowWrite\n }\n\n get #canEditConfig(): boolean {\n return !this.#isSandbox && this.#options.permissions.allowConfigEdit\n }\n\n /**\n * A sandbox agent always allows reading its output back; a local agent only when opted in.\n */\n get #canRead(): boolean {\n return this.#isSandbox || this.#options.permissions.allowRead\n }\n\n async start(): Promise<void> {\n const { token, studioUrl, signal, heartbeatInterval, installLogger, instanceId, capacity } = this.#options\n\n await installLogger?.(this.#hooks)\n signal?.addEventListener('abort', this.#onAbort, { once: true })\n this.#unhooks.push(() => signal?.removeEventListener('abort', this.#onAbort))\n\n try {\n signal?.throwIfAborted()\n // Before registering, so a host can cover the wait: registration is a round trip and the\n // socket after it opens without being awaited.\n await this.#hooks.callHook('studio:connecting', { url: studioUrl })\n\n if (capacity.maxConcurrent > RUNTIME_MAX_CONCURRENT) {\n await this.#warn(\n `Running ${RUNTIME_MAX_CONCURRENT} job at a time: KUBB_AGENT_MAX_CONCURRENT=${capacity.maxConcurrent} needs per-job workers, which this agent does not have yet`,\n )\n }\n\n const registration = await registerAgent({\n signal,\n token,\n studioUrl,\n instanceId,\n capacity: { ...capacity, maxConcurrent: Math.min(capacity.maxConcurrent, RUNTIME_MAX_CONCURRENT) },\n })\n\n signal?.throwIfAborted()\n this.#registration = registration\n\n const rpc = await (this.#options.connector ?? connectWebSocketRpc)({ url: registration.socketUrl, token, instanceId, local: this })\n this.#rpc = rpc\n void rpc.closed.then(this.#onClose)\n\n if (signal?.aborted || this.#disposed) {\n rpc.close()\n throw new Error('Connection stopped during startup')\n }\n\n this.#scheduleHeartbeat(heartbeatInterval)\n await this.#hooks.callHook('studio:connected', {\n url: studioUrl,\n versions: { studio: registration.version, kubb: kubbVersion, agent: this.#options.version },\n agentSlug: registration.agentSlug,\n organizationSlug: registration.organizationSlug,\n })\n // Studio registers the agent by calling connect() over RPC. Ready means that handshake landed.\n await this.#connectAck.promise\n await this.#hooks.callHook('studio:ready', {})\n } catch (error) {\n await this.dispose()\n if (!signal?.aborted) await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n reportRetry(delayMs: number): Promise<void> | void {\n return this.#hooks.callHook('studio:reconnecting', { delayMs })\n }\n\n #warn(message: string, permission?: keyof AgentPermissions): Promise<void> | void {\n return this.#hooks.callHook('studio:warn', { message, permission })\n }\n\n /**\n * Declines a request: logs why locally, then tells Studio. The two wordings differ on purpose,\n * since the log names the request that was ignored and the error names what the caller can do.\n */\n async #refuse(reason: string, message: string, permission?: keyof AgentPermissions): Promise<never> {\n await this.#warn(reason, permission)\n throw new Error(message)\n }\n\n /**\n * Brackets one Studio command with `studio:command:start` and `studio:command:end`. A command\n * that declined its request reports no `info`, and only the start is heard.\n */\n async #command<T>({ command, run }: { command: string; run: () => Promise<{ value: T; info?: string }> }): Promise<T> {\n await this.#hooks.callHook('studio:command:start', { command })\n const { value, info } = await run()\n if (info !== undefined) await this.#hooks.callHook('studio:command:end', { command, info })\n return value\n }\n\n #scheduleHeartbeat(interval: number): void {\n const rpc = this.#rpc\n if (!rpc) {\n return\n }\n this.#heartbeatTimer = setTimeout(async () => {\n try {\n await this.#ping(rpc)\n } catch {\n if (this.#rpc === rpc) {\n rpc.close()\n }\n return\n }\n\n if (this.#rpc === rpc) {\n this.#scheduleHeartbeat(interval)\n }\n }, interval)\n }\n\n /**\n * Races `studio.ping()` against a deadline, so a half-open socket can't hang it forever.\n * */\n async #ping(rpc: RpcConnection): Promise<void> {\n const { promise: timedOut, reject: onTimeout } = Promise.withResolvers<never>()\n const timer = setTimeout(() => onTimeout(new Error('Heartbeat ping timed out')), agentDefaults.heartbeatTimeoutMs)\n\n try {\n // A load report that fails to build must not cost the heartbeat itself.\n const load = await Promise.race([this.#load().catch(() => undefined), timedOut])\n await Promise.race([rpc.studio.ping(load), timedOut])\n } finally {\n clearTimeout(timer)\n }\n }\n\n async #load(): Promise<AgentLoad> {\n return {\n running: this.#isGenerating ? 1 : 0,\n rssMb: Math.round(process.memoryUsage().rss / (1024 * 1024)),\n storeBytes: await this.#generations.bytes(),\n accepting: true,\n }\n }\n\n /**\n * Reads `kubb.config.ts` and reports which plugin options Studio may edit.\n *\n * Skipped when the host did not grant `allowConfigEdit`. Not cached: the user can edit the file\n * between two Studio actions.\n */\n async #readConfigFileView(source?: string): Promise<ConfigFileView | undefined> {\n if (!this.#canEditConfig) {\n return undefined\n }\n\n try {\n const { readConfig } = await import('../operations/configFile.ts')\n return readConfig(source ?? (await read(this.#options.configFile)))\n } catch (error) {\n await this.#warn(`Could not read ${this.#options.configFile}: ${getErrorMessage(error)}`)\n\n return undefined\n }\n }\n\n async connect(): Promise<ConnectMessagePayload> {\n const { configPath, root, version, loadConfig, permissions } = this.#options\n const [config, file] = await Promise.all([loadConfig(), this.#readConfigFileView()])\n\n const payload: ConnectMessagePayload = {\n versions: { kubb: kubbVersion, agent: version },\n root,\n config: {\n path: configPath,\n file,\n plugins: config.plugins.map((plugin) => ({\n name: toPackageName(plugin.name),\n options: plugin.options ?? {},\n })),\n },\n permissions: {\n ...permissions,\n allowWrite: this.#canWrite,\n allowConfigEdit: this.#canEditConfig,\n allowRead: this.#canRead,\n },\n }\n this.#connectAck.resolve()\n return payload\n }\n\n #onAbort = (): void => void this.#end({ reason: 'shutdown', retry: false }).catch(() => {})\n\n #onClose = (close: RpcClose | void): void => void this.#end(planEnd(close)).catch(() => {})\n\n /** Closes this attempt and waits for its generation to stop before another can start. */\n dispose(): Promise<void> {\n return this.#end({ reason: 'shutdown', retry: false })\n }\n\n async #end(plan: SessionEnd): Promise<void> {\n if (this.#disposed) {\n await this.closed\n return\n }\n this.#disposed = true\n clearTimeout(this.#heartbeatTimer)\n this.#rpc?.close()\n this.#rpc = undefined\n this.#connectAck.reject(new Error('Session ended before Studio called connect()'))\n for (const unhook of this.#unhooks) unhook()\n this.#unhooks.length = 0\n\n try {\n await this.#activeJob?.cancel()\n await this.#generationResult?.catch(() => {})\n await this.#hooks.callHook('studio:disconnected', { reason: plan.reason })\n if (plan.error) await this.#hooks.callHook('studio:error', { error: plan.error })\n } finally {\n this.#ended.resolve(plan)\n }\n }\n\n startGeneration(data: GenerateInput): GenerationRun {\n const generationStream = createGenerationStream(this.#hooks, data.jobId)\n const controller = new AbortController()\n const cancelRun = async () => {\n controller.abort(new Error('Generation canceled'))\n }\n const result = this.#runGeneration(data, controller, cancelRun)\n .then(async (value) => {\n await generationStream.close()\n return value\n })\n .catch((error) => {\n generationStream.fail(error)\n throw error\n })\n .finally(() => {\n if (this.#activeJob?.cancel === cancelRun) this.#activeJob = undefined\n })\n if (this.#activeJob?.cancel === cancelRun) this.#generationResult = result\n // A disconnect can reject this after the remote caller disappeared.\n void result.catch(() => {})\n\n return new GenerationRunTarget(generationStream.stream, result, cancelRun, () => {\n controller.abort(new Error('Generation canceled'))\n generationStream.dispose()\n })\n }\n\n /**\n * Cancels the currently running job when its id matches `jobId`.\n */\n async cancel(jobId: string): Promise<void> {\n if (this.#activeJob?.jobId === jobId) await this.#activeJob.cancel()\n }\n\n async #runGeneration(data: GenerateInput, controller: AbortController, cancelRun: () => Promise<void>): Promise<GenerateResult> {\n // Checked before the first `await`, so two calls in the same tick can't both pass.\n if (this.#disposed) throw new Error('Connection is closed')\n if (this.#isGenerating) {\n return this.#refuse('Ignored generate: a generation is already in progress', 'A generation is already in progress, please wait for it to finish')\n }\n this.#isGenerating = true\n this.#activeJob = { jobId: data.jobId, cancel: cancelRun }\n\n let root = this.#options.root\n\n try {\n if (this.#isSandbox) root = await createJobRoot()\n return await this.#command<GenerateResult>({ command: 'generate', run: () => this.#generate({ data, root, signal: controller.signal }) })\n } finally {\n if (root !== this.#options.root) await removeJobRoot(root)\n this.#isGenerating = false\n }\n }\n\n async #generate({ data, root, signal }: { data: GenerateInput; root: string; signal: AbortSignal }): Promise<{ value: GenerateResult; info: string }> {\n const { loadConfig, permissions } = this.#options\n const config = await loadConfig()\n const patch = data.config\n const plugins = await mergePlugins(config.plugins, patch?.plugins)\n const adapter = await mergeAdapter(config.adapter, patch?.adapter)\n\n // A sandbox agent always uses the inline spec (empty string included, since it has no disk\n // file); a non-sandbox agent always reads its spec from disk.\n const inputOverride = this.#isSandbox ? (patch?.input ?? '') : undefined\n\n if (permissions.allowWrite && this.#isSandbox) {\n await this.#warn('Running in a sandbox, so writing files is disabled')\n }\n\n if (patch?.input && !this.#isSandbox) {\n await this.#warn('Ignored the spec from Studio: generating from a Studio spec is only available to a sandbox agent')\n }\n\n const resolvedPlugins = plugins ?? config.plugins\n\n const storage = this.#canWrite ? fsStorage() : memoryStorage()\n const diskFiles = this.#hasProjectOnDisk ? await listDisk({ root, outputPath: config.output.path, maxFiles: DISK_SNAPSHOT_MAX_FILES }) : undefined\n const disk = diskFiles\n ? await this.#generations.keep({ jobId: data.jobId, source: 'disk', files: diskFiles, maxSetMb: this.#limits.maxSnapshotMb })\n : undefined\n\n // The session's own emitter carries the run: the host's logger is already on it from\n // `connect`, and the stream's listeners come off again once the run ends, so one run's\n // listeners never see the next.\n const kubb = createKubb(\n {\n ...config,\n root,\n input: inputOverride ?? config.input,\n storage,\n output: permissions.allowExec ? { ...config.output } : { ...config.output, format: false, lint: false, postGenerate: [] },\n plugins: resolvedPlugins,\n adapter,\n },\n { hooks: this.#hooks, signal },\n )\n const result = await kubb.generate().catch(async (error: unknown) => {\n await this.#generations.drop(data.jobId)\n throw error\n })\n if (!result.success) {\n await this.#generations.drop(data.jobId)\n throw generationFailure(result.diagnostics)\n }\n\n const paths = new Set(result.files.map((file) => relativeStoragePath({ root, filePath: file.path })))\n const output = await this.#generations.keep({ jobId: data.jobId, source: 'output', files: { storage, root, paths }, maxSetMb: this.#limits.maxMb })\n if (!output.paths.length && Object.keys(output.hashes).length) {\n await this.#warn('Kept only the hashes of this generation: its output is too large to keep')\n }\n const { peerDependencies, missingDependencies } = await resolvePeerDependencies(resolvedPlugins.map(({ name }) => name))\n await this.#generations.add({ jobId: data.jobId, output, disk, peerDependencies, missingDependencies })\n return {\n value: {\n status: 'success',\n files: [...paths],\n fileCount: paths.size,\n hashes: output.hashes,\n disk: disk ? { hashes: disk.hashes } : undefined,\n },\n info: `${resolvedPlugins.length} plugin${resolvedPlugins.length === 1 ? '' : 's'}, ${this.#canWrite ? 'written to disk' : 'in memory'}${inputOverride !== undefined ? ', from a Studio spec' : ''}`,\n }\n }\n\n saveConfig(data: SaveConfigInput): Promise<SaveResult> {\n return this.#command<SaveResult>({ command: 'saveConfig', run: () => this.#saveConfig(data) })\n }\n\n async #saveConfig(data: SaveConfigInput): Promise<{ value: SaveResult; info?: string }> {\n const { configPath, configFile } = this.#options\n\n // Every RPC call gets one result. `edits` is checked before it is walked because values cross\n // the agent trust boundary.\n if (!Array.isArray(data.edits)) {\n await this.#warn('Ignored save: the message carried no edits')\n\n return { value: { outcomes: [], changed: false } }\n }\n\n const edits = data.edits\n const refuse = (reason: string): SaveResult => ({ outcomes: edits.map((edit) => ({ edit, applied: false, reason })), changed: false })\n\n if (!this.#canEditConfig) {\n await this.#warn('Ignored save: editing kubb.config.ts was not granted')\n\n return { value: refuse('the agent was not granted permission to edit kubb.config.ts') }\n }\n\n // A generation reloads the config while it runs, so rewriting the file underneath it would\n // leave that run working from half the change.\n if (this.#isGenerating) {\n return { value: refuse('a generation is in progress') }\n }\n\n try {\n const { writeConfigEdits } = await import('../operations/configFile.ts')\n const { source: patched, outcomes, changed } = await writeConfigEdits({ filePath: configFile, edits })\n\n const applied = outcomes.filter((outcome) => outcome.applied).length\n return {\n value: { outcomes, changed, file: changed ? await this.#readConfigFileView(patched) : undefined },\n info: `applied ${applied}/${outcomes.length} edits to ${configPath}`,\n }\n } catch (error) {\n // An unreadable config, a read-only filesystem. Reported as a refusal of every edit so\n // Studio hears back rather than waiting on a reply that never comes.\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n\n return { value: refuse(getErrorMessage(error)) }\n }\n }\n\n publishSnapshot(data: PublishSnapshotInput): Promise<PublishSnapshotResult> {\n return this.#command<PublishSnapshotResult>({ command: 'snapshot', run: () => this.#publishSnapshot(data) })\n }\n\n async #publishSnapshot(data: PublishSnapshotInput): Promise<{ value: PublishSnapshotResult; info: string }> {\n if (this.#isSandbox) {\n return this.#refuse('Ignored snapshot: a sandbox agent has no project to build a package from', 'A sandbox agent has no project to build a package from')\n }\n\n const { name, version, bundledDependencies, uploadPath } = data\n\n if (!name || !version || !uploadPath) {\n return this.#refuse('Ignored snapshot: the message was missing required fields', 'The request was missing required fields')\n }\n\n const generation = await this.#generations.latest()\n\n if (!generation) {\n return this.#refuse('Ignored snapshot: no prior generation to pack', 'No prior generation exists to pack, run a generation first')\n }\n\n const bundled = new Set(bundledDependencies ?? [])\n const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency))\n\n if (missing.length) {\n return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(', ')}`, `Missing dependencies: ${missing.join(', ')}`)\n }\n\n try {\n const { createSnapshotPackage, uploadSnapshot } = await import('../operations/snapshotPackage.ts')\n const { files } = await this.#generations.read({ generation, source: 'output', paths: generation.output.paths })\n\n const { bytes, integrity } = await createSnapshotPackage(files, { name, version, peerDependencies: generation.peerDependencies })\n\n const { token, studioUrl, signal } = this.#options\n await uploadSnapshot({ bytes, uploadPath, studioUrl, token, shutdown: signal })\n\n return {\n value: { integrity, peerDependencies: generation.peerDependencies },\n info: `packed ${Object.keys(files).length} file${Object.keys(files).length === 1 ? '' : 's'}`,\n }\n } catch (error) {\n await this.#hooks.callHook('studio:error', { error: toError(error) })\n throw error\n }\n }\n\n /** A sandbox has no project directory to snapshot. */\n get #hasProjectOnDisk(): boolean {\n return !this.#isSandbox && this.#canRead\n }\n\n readFiles(data: ReadFilesInput): Promise<ReadFilesResult> {\n return this.#command<ReadFilesResult>({ command: 'readFiles', run: () => this.#readFiles(data) })\n }\n\n async #readFiles(data: ReadFilesInput): Promise<{ value: ReadFilesResult; info: string }> {\n if (!this.#canRead) {\n return this.#refuse('Ignored files: reading generated files was not granted', 'The agent was not granted permission to read generated files', 'allowRead')\n }\n\n // `paths` came off the wire, so check its shape before walking it.\n if (!Array.isArray(data.paths)) {\n return this.#refuse('Ignored files: the message carried no paths', 'The request carried no paths')\n }\n\n const { paths } = data\n\n if (paths.length > MAX_FILES_PER_REQUEST) {\n return this.#refuse(\n `Ignored files: requested ${paths.length} paths, more than the ${MAX_FILES_PER_REQUEST} allowed per request`,\n `At most ${MAX_FILES_PER_REQUEST} paths may be requested at once`,\n )\n }\n\n if (typeof data.jobId !== 'string' || !data.jobId) {\n return this.#refuse('Ignored files: the message named no job', 'The request named no generation job')\n }\n\n const generation = await this.#generations.get(data.jobId)\n\n if (!generation) {\n return this.#refuse(`Ignored files: job ${data.jobId} is not kept on this agent`, GENERATION_GONE_MESSAGE)\n }\n\n if (!isValidPage(data)) {\n return this.#refuse(\n 'Ignored files: the message carried an invalid page',\n `The cursor must be a whole line from 0 and come with a limit of 1 to ${MAX_PAGE_LINES} lines`,\n )\n }\n\n const source = data.source === 'disk' ? 'disk' : 'output'\n\n if (!generation[source]) {\n return this.#refuse('Ignored files: that job has no snapshot of the files on disk', 'This agent kept no snapshot of the files on disk for that job')\n }\n\n // Only paths the set holds are read, never an arbitrary path.\n const result = await this.#generations.read({ generation, source, paths, cursor: data.cursor, limit: data.limit })\n\n return { value: result, info: `read ${Object.keys(result.files).length}/${paths.length} requested file${paths.length === 1 ? '' : 's'}` }\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { IncompatibleAgentError, InvalidAgentTokenError } from '../operations/api.ts'\nimport { agentDefaults } from '../operations/constants.ts'\nimport { StudioSession, type StudioSessionOptions } from './StudioSession.ts'\n\ntype ConnectionOutcome = 'shutdown' | 'stopped'\n\ntype TokenRejection<TCredentials> = {\n error: InvalidAgentTokenError\n credentials: TCredentials\n /** Whether this credential already served a ready session. */\n live: boolean\n}\n\nexport type ConnectionOptions<TCredentials extends { token: string }> = {\n credentials: TCredentials\n clientOptions: (credentials: TCredentials) => Omit<StudioSessionOptions, 'token' | 'signal'>\n onTokenRejected: (rejection: TokenRejection<TCredentials>) => Promise<TCredentials | null>\n signal?: AbortSignal\n /** Used by the client facade once startup succeeds or schedules its first retry. */\n onStarted?: () => void\n}\n\n/** Runs the process's one connection loop, including retries and credential replacement. */\nexport async function runConnection<TCredentials extends { token: string }>({\n credentials,\n clientOptions,\n onTokenRejected,\n signal,\n onStarted,\n}: ConnectionOptions<TCredentials>): Promise<ConnectionOutcome> {\n let current = credentials\n const initialOptions = clientOptions(current)\n const instanceId = initialOptions.instanceId ?? randomUUID()\n let options = initialOptions\n let attempt = 0\n let live = false\n\n while (!signal?.aborted) {\n const session = new StudioSession({ ...options, instanceId, token: current.token, signal })\n try {\n await session.start()\n live = true\n attempt = 0\n onStarted?.()\n const end = await session.closed\n if (!end.retry) return signal?.aborted ? 'shutdown' : 'stopped'\n } catch (error) {\n if (signal?.aborted) return 'shutdown'\n if (error instanceof IncompatibleAgentError) throw error\n await session.dispose()\n const end = await session.closed\n // A terminal socket close during the handshake must not become a network retry.\n if (!end.retry && end.reason !== 'shutdown') return 'stopped'\n if (error instanceof InvalidAgentTokenError) {\n const next = await onTokenRejected({ error, credentials: current, live })\n if (!next) return 'stopped'\n current = next\n options = clientOptions(current)\n live = false\n attempt = 0\n continue\n }\n } finally {\n await session.dispose()\n }\n\n onStarted?.()\n const cap = Math.min(1_000 * 2 ** Math.min(attempt++, 16), options.retryInterval ?? agentDefaults.retryIntervalMs)\n const delayMs = Math.random() * cap\n await session.reportRetry(delayMs)\n try {\n await delay(delayMs, undefined, { signal })\n } catch (error) {\n if (!signal?.aborted) throw error\n }\n }\n return 'shutdown'\n}\n","import type { InvalidAgentTokenError } from '../operations/api.ts'\nimport type { StudioSessionOptions } from './StudioSession.ts'\nimport { runConnection } from './runConnection.ts'\n\nexport type ClientOptions = Omit<StudioSessionOptions, 'signal'> & {\n /** Called once when a background reconnect rejects the credential. */\n onAuthRequired?: (error: InvalidAgentTokenError) => void\n}\n\nexport type Client = {\n connect: () => Promise<void>\n disconnect: () => void\n}\n\n/** Compatibility facade over the shared connection loop. */\nexport function createClient({ onAuthRequired, ...options }: ClientOptions): Client {\n const shutdown = new AbortController()\n const started = Promise.withResolvers<void>()\n let resolved = false\n let running: Promise<unknown> | undefined\n\n return {\n connect() {\n running ??= runConnection({\n credentials: { token: options.token },\n clientOptions: () => options,\n signal: shutdown.signal,\n onStarted: () => {\n resolved = true\n started.resolve()\n },\n onTokenRejected: async ({ error }) => {\n if (!resolved) throw error\n onAuthRequired?.(error)\n return null\n },\n }).then(\n () => started.resolve(),\n (error: unknown) => started.reject(error),\n )\n return started.promise\n },\n disconnect() {\n shutdown.abort()\n },\n }\n}\n","import { setTimeout as delay } from 'node:timers/promises'\nimport { toError } from '@internals/utils'\nimport { ofetch } from 'ofetch'\nimport { agentDefaults } from './constants.ts'\nimport { getMachineToken } from './machine.ts'\n\n/**\n * RFC 8628 device-authorization response from Studio's `/api/auth/device/code` endpoint.\n * Field names match the RFC; the CLI polls with `device_code` and shows `user_code` to the user.\n */\nexport type PairingSession = {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete: string\n expires_in: number\n interval: number\n}\n\nexport type PairingResult = {\n /**\n * Bearer token for this machine. Write credentials with mode 0600 and never log the value.\n */\n token: string\n agent: {\n /**\n * Stable agent id in Studio.\n */\n id: string\n /**\n * Short slug used in logs and the UI (for example `brave-otter`).\n */\n slug: string\n /**\n * Display name chosen at pairing time.\n */\n name: string\n /**\n * This agent's organization slug, absent for a sandbox or global agent, which has none.\n */\n organizationSlug?: string\n }\n}\n\n/**\n * What this machine registers as. Any member may approve `cli` (`kubb studio`); only an admin may\n * approve `user` and `sandbox` (the Docker image). A `ci` agent never pairs.\n */\nexport type PairingAgentType = 'cli' | 'user' | 'sandbox'\n\n/** Labels, not secrets: a person approving the code in the browser is what authorizes a pairing. */\nconst CLIENT_IDS = { cli: 'kubb-cli', agent: 'kubb-agent' } as const\n\n/**\n * Thrown when a caller aborts `startPairing` or `pollForPairingToken` through their `signal`, such\n * as a `kubb studio` shutdown mid-pairing. Distinct from a denial or an expired code, so a host can\n * exit quietly instead of reporting a pairing failure.\n */\nexport class PairingCanceledError extends Error {\n constructor() {\n super('Pairing was canceled')\n this.name = 'PairingCanceledError'\n }\n}\n\n/** Thrown when the code expired unapproved. A fresh code can still succeed. */\nexport class PairingExpiredError extends Error {\n constructor(message = 'The pairing code expired, pair again') {\n super(message)\n this.name = 'PairingExpiredError'\n }\n}\n\n/** Thrown when the pairing was denied in the browser, including by the organization's agent limit. */\nexport class PairingDeniedError extends Error {\n constructor(message = 'Pairing was denied in the browser') {\n super(message)\n this.name = 'PairingDeniedError'\n }\n}\n\ntype StartPairingOptions = {\n studioUrl?: string\n type: PairingAgentType\n /**\n * Display name for the agent, usually the project or machine name.\n */\n name: string\n hostname: string\n /**\n * Aborting this cancels the request in flight and rejects with {@link PairingCanceledError}.\n */\n signal?: AbortSignal\n}\n\n/**\n * Asks Studio for a pairing code. The machine token travels with the request and is stored against\n * the code, so approval knows which machine it is pairing: the same machine pairing twice rotates\n * one agent's token instead of creating a second agent.\n */\nexport async function startPairing({ studioUrl = agentDefaults.studioUrl, type, name, hostname, signal }: StartPairingOptions): Promise<PairingSession> {\n try {\n return await ofetch<PairingSession>(`${studioUrl}/api/auth/device/code`, {\n method: 'POST',\n body: {\n client_id: type === 'cli' ? CLIENT_IDS.cli : CLIENT_IDS.agent,\n name,\n hostname,\n machine_token: await getMachineToken(),\n agent_kind: type === 'cli' ? undefined : type,\n },\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n throw error\n }\n}\n\ntype PollOptions = {\n studioUrl?: string\n session: PairingSession\n /**\n * Aborting this stops polling and rejects with {@link PairingCanceledError}, whether the abort\n * lands between polls or during the wait for the next one.\n */\n signal?: AbortSignal\n /** Called when a poll could not reach Studio. Polling carries on. */\n onRetry?: (error: Error) => void\n}\n\ntype PollError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied' | 'invalid_grant'\n\ntype PollResponse =\n | PairingResult\n | {\n error: PollError | string\n /**\n * Why, when Studio has something more useful to say than the RFC code. An approval that hits\n * the organization's agent limit comes back as `access_denied` with the limit spelled out.\n */\n error_description?: string\n }\n\nfunction isPairingResult(response: PollResponse | undefined): response is PairingResult {\n return !!response && typeof response === 'object' && 'token' in response && typeof response.token === 'string'\n}\n\n/**\n * Polls until the user approves or denies, honoring the server's `slow_down` back-off.\n *\n * Studio's own endpoint is used rather than the auth layer's `/device/token`, because an approved\n * Kubb pairing is worth an agent bearer token, not a user session.\n *\n * @throws {PairingExpiredError} when the code expires before anyone approves it.\n * @throws {PairingDeniedError} when the pairing is denied in the browser.\n * @throws {PairingCanceledError} when `signal` aborts.\n */\nexport async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, session, signal, onRetry }: PollOptions): Promise<PairingResult> {\n // Both fields cross the network, so neither is trusted as-is: a missing or zero `interval` would\n // spin the poll loop, and a missing or zero `expires_in` would expire the code before the first\n // poll. `> 0` is also false for `NaN` and for a missing field, so it doubles as the type guard.\n const deadline = Date.now() + (session.expires_in > 0 ? session.expires_in : 600) * 1000\n let intervalMs = (session.interval > 0 ? session.interval : 5) * 1000\n\n while (Date.now() < deadline) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n try {\n await delay(intervalMs, undefined, { signal })\n } catch {\n throw new PairingCanceledError()\n }\n\n let response: PollResponse | undefined\n try {\n response = await ofetch<PollResponse | undefined>(`${studioUrl}/api/agent/token`, {\n method: 'POST',\n body: { device_code: session.device_code },\n // A denial, an expiry, and \"not yet\" all come back as 4xx with a body the caller needs to\n // read, so let every response through and switch on `error` instead of catching.\n ignoreResponseError: true,\n signal,\n })\n } catch (error) {\n if (signal?.aborted) {\n throw new PairingCanceledError()\n }\n\n // Studio can be briefly unreachable (a deploy) while the code is still valid, so retry.\n onRetry?.(toError(error))\n continue\n }\n\n if (isPairingResult(response)) {\n return response\n }\n\n if (!response || typeof response !== 'object' || !('error' in response) || typeof response.error !== 'string') {\n throw new Error('Kubb Studio returned an empty pairing response, pair again')\n }\n\n if (response.error === 'authorization_pending') {\n continue\n }\n\n if (response.error === 'slow_down') {\n intervalMs += 5_000\n continue\n }\n\n if (response.error === 'access_denied') {\n throw new PairingDeniedError(response.error_description)\n }\n\n if (response.error === 'expired_token' || response.error === 'invalid_grant') {\n throw new PairingExpiredError(response.error_description)\n }\n\n throw new Error(response.error_description ?? `Pairing failed (${response.error})`)\n }\n\n throw new PairingExpiredError()\n}\n\ntype PairAgentOptions = StartPairingOptions & {\n /** Shows the code to whoever approves it. Called again for each fresh code. */\n onCode: (session: PairingSession, attempt: number) => void | Promise<void>\n /** Called when a poll could not reach Studio. Polling carries on. */\n onRetry?: (error: Error) => void\n /**\n * Codes to ask for in total when one expires unapproved. A denial or abort ends it at once.\n * @default 1\n */\n maxAttempts?: number\n}\n\n/**\n * Pairs this machine with Studio: asks for a code, hands it to the host to show, and waits for approval.\n */\nexport async function pairAgent({ onCode, onRetry, maxAttempts = 1, ...options }: PairAgentOptions): Promise<PairingResult> {\n for (let attempt = 1; ; attempt++) {\n const session = await startPairing(options)\n await onCode(session, attempt)\n\n try {\n return await pollForPairingToken({ studioUrl: options.studioUrl, session, signal: options.signal, onRetry })\n } catch (error) {\n if (!(error instanceof PairingExpiredError) || attempt >= maxAttempts) {\n throw error\n }\n }\n }\n}\n"],"mappings":"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"}
|