@kubb/cli 5.12.0 → 5.12.2

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.
Files changed (65) hide show
  1. package/README.md +2 -0
  2. package/dist/{Telemetry-B8mC2nqp.js → Telemetry-BhoWyCRu.js} +2 -2
  3. package/dist/{Telemetry-B8mC2nqp.js.map → Telemetry-BhoWyCRu.js.map} +1 -1
  4. package/dist/{Telemetry-Gl2uIT4u.cjs → Telemetry-C7v3wFNp.cjs} +2 -2
  5. package/dist/{Telemetry-Gl2uIT4u.cjs.map → Telemetry-C7v3wFNp.cjs.map} +1 -1
  6. package/dist/{background-Bi46Uzf1.cjs → background-BCDVfX30.cjs} +2 -2
  7. package/dist/{background-Bi46Uzf1.cjs.map → background-BCDVfX30.cjs.map} +1 -1
  8. package/dist/{background-D7b5cDs_.js → background-BLkaGx4A.js} +1 -1
  9. package/dist/{background-DyD2KqN5.js → background-CUF-snCU.js} +2 -2
  10. package/dist/{background-DyD2KqN5.js.map → background-CUF-snCU.js.map} +1 -1
  11. package/dist/credentials-CZK6zPLv.js.map +1 -1
  12. package/dist/credentials-DDaKqtJn.cjs.map +1 -1
  13. package/dist/index.cjs +32 -19
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +32 -19
  16. package/dist/index.js.map +1 -1
  17. package/dist/{registry-Cw2X7I3b.js → registry-BUPuJlgX.js} +3 -3
  18. package/dist/{registry-Cw2X7I3b.js.map → registry-BUPuJlgX.js.map} +1 -1
  19. package/dist/{registry-BnkCWWeX.cjs → registry-Cl-wgD7H.cjs} +3 -3
  20. package/dist/{registry-BnkCWWeX.cjs.map → registry-Cl-wgD7H.cjs.map} +1 -1
  21. package/dist/{reporters-lJQRYc6c.cjs → reporters-DkBWh4sD.cjs} +4 -4
  22. package/dist/{reporters-lJQRYc6c.cjs.map → reporters-DkBWh4sD.cjs.map} +1 -1
  23. package/dist/{reporters-BReH227e.js → reporters-DzAzRAlM.js} +4 -4
  24. package/dist/{reporters-BReH227e.js.map → reporters-DzAzRAlM.js.map} +1 -1
  25. package/dist/{run-8KLWLcEf.cjs → run-9uoP-2Xh.cjs} +4 -4
  26. package/dist/{run-8KLWLcEf.cjs.map → run-9uoP-2Xh.cjs.map} +1 -1
  27. package/dist/{run-Cd6W_RNN.js → run-BJXDoxOJ.js} +6 -6
  28. package/dist/{run-Cd6W_RNN.js.map → run-BJXDoxOJ.js.map} +1 -1
  29. package/dist/{run-05nazgoj.js → run-BWT7PtHZ.js} +4 -4
  30. package/dist/{run-05nazgoj.js.map → run-BWT7PtHZ.js.map} +1 -1
  31. package/dist/{run-ChGaCan5.js → run-BcTgwks6.js} +19 -7
  32. package/dist/run-BcTgwks6.js.map +1 -0
  33. package/dist/{run-BDLWu55k.cjs → run-CK19O5v2.cjs} +6 -6
  34. package/dist/{run-BDLWu55k.cjs.map → run-CK19O5v2.cjs.map} +1 -1
  35. package/dist/{run-CIE_og50.js → run-CioQPR0t.js} +2 -2
  36. package/dist/{run-CIE_og50.js.map → run-CioQPR0t.js.map} +1 -1
  37. package/dist/{run-CORf5LJQ.js → run-DklXr_kO.js} +1 -1
  38. package/dist/{run-Czt1mbNa.cjs → run-DqFL5SMR.cjs} +2 -2
  39. package/dist/{run-Czt1mbNa.cjs.map → run-DqFL5SMR.cjs.map} +1 -1
  40. package/dist/{run-CLJnncEi.cjs → run-T82Jef8b.cjs} +19 -7
  41. package/dist/run-T82Jef8b.cjs.map +1 -0
  42. package/dist/{snapshot-DcLGxpoM.cjs → snapshot-D5HiDktY.cjs} +6 -6
  43. package/dist/{snapshot-DcLGxpoM.cjs.map → snapshot-D5HiDktY.cjs.map} +1 -1
  44. package/dist/{snapshot-CYfGZRlq.js → snapshot-DLPs2buj.js} +6 -6
  45. package/dist/{snapshot-CYfGZRlq.js.map → snapshot-DLPs2buj.js.map} +1 -1
  46. package/dist/{status-DrA1XG77.cjs → status-BFMmzCfD.cjs} +3 -3
  47. package/dist/{status-DrA1XG77.cjs.map → status-BFMmzCfD.cjs.map} +1 -1
  48. package/dist/{status-DGDWubBc.js → status-DlLYGONt.js} +3 -3
  49. package/dist/{status-DGDWubBc.js.map → status-DlLYGONt.js.map} +1 -1
  50. package/dist/studioWorker.js +2 -2
  51. package/dist/{summary-DQ_SULKW.cjs → summary-BwbfP0Qj.cjs} +3 -3
  52. package/dist/{summary-DQ_SULKW.cjs.map → summary-BwbfP0Qj.cjs.map} +1 -1
  53. package/dist/{summary-CiCW8bRA.js → summary-DvLq6uim.js} +3 -3
  54. package/dist/{summary-CiCW8bRA.js.map → summary-DvLq6uim.js.map} +1 -1
  55. package/dist/{text-hhzIqdVE.js → text-DG7CyUe7.js} +2 -2
  56. package/dist/{text-hhzIqdVE.js.map → text-DG7CyUe7.js.map} +1 -1
  57. package/dist/{text-BKjdI_f1.cjs → text-DfFJj91M.cjs} +2 -2
  58. package/dist/{text-BKjdI_f1.cjs.map → text-DfFJj91M.cjs.map} +1 -1
  59. package/dist/{utils-CZzj70Rn.js → utils-BeOShrJ6.js} +3 -3
  60. package/dist/{utils-CZzj70Rn.js.map → utils-BeOShrJ6.js.map} +1 -1
  61. package/dist/{utils-Dyd-rk1U.cjs → utils-Cb4fBZiX.cjs} +3 -3
  62. package/dist/{utils-Dyd-rk1U.cjs.map → utils-Cb4fBZiX.cjs.map} +1 -1
  63. package/package.json +7 -7
  64. package/dist/run-CLJnncEi.cjs.map +0 -1
  65. package/dist/run-ChGaCan5.js.map +0 -1
package/README.md CHANGED
@@ -174,6 +174,8 @@ skips the question for that one permission.
174
174
  | `--url <url>` | | string | `https://kubb.studio` | Base URL of the Kubb Studio instance |
175
175
  | `--allow-write` | | boolean | `false` | Write generated files to disk. Asked for once per project when omitted |
176
176
  | `--allow-config-edit` | | boolean | `false` | Edit plugin options in `kubb.config.ts`. Asked for once per project when omitted |
177
+ | `--allow-install` | | boolean | `false` | Let Studio install extensions with `kubb add`; needs `--allow-config-edit` too. Asked once |
178
+ | `--allow-update` | | boolean | `false` | Let Studio update installed extensions with `kubb update`. Asked once |
177
179
  | `--allow-exec` | | boolean | `false` | Run the formatter, the linter, and `output.postGenerate`. Asked once per project when omitted |
178
180
  | `--no-open` | | boolean | | Do not open the approval page in a browser while pairing |
179
181
  | `--log-level <level>` | `-l` | string | `info` | Log level: `silent`, `info`, or `verbose` |
@@ -77,7 +77,7 @@ function formatMs(ms) {
77
77
  }
78
78
  //#endregion
79
79
  //#region package.json
80
- var version = "5.12.0";
80
+ var version = "5.12.2";
81
81
  //#endregion
82
82
  //#region src/Telemetry.ts
83
83
  /** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */
@@ -236,4 +236,4 @@ async function sendTelemetry(payload) {
236
236
  //#endregion
237
237
  export { getElapsedMs as a, formatMs as i, trackRun as n, runtime as o, version as r, isCIEnvironment as s, isDisabled as t };
238
238
 
239
- //# sourceMappingURL=Telemetry-B8mC2nqp.js.map
239
+ //# sourceMappingURL=Telemetry-BhoWyCRu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Telemetry-B8mC2nqp.js","names":["process","process"],"sources":["../../../internals/utils/src/isCIEnvironment.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/time.ts","../package.json","../src/Telemetry.ts"],"sourcesContent":["import process from 'node:process'\n\n/**\n * Returns `true` when a known CI provider has set its environment variable.\n * `CI=false` and `CI=0` count as not CI, so a user can opt out locally.\n */\nexport function isCIEnvironment(): boolean {\n const ci = process.env.CI\n return !!(\n (ci && ci !== 'false' && ci !== '0') ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.BITBUCKET_BUILD_NUMBER ||\n process.env.JENKINS_URL ||\n process.env.CIRCLECI ||\n process.env.TRAVIS ||\n process.env.TEAMCITY_VERSION ||\n process.env.BUILDKITE ||\n process.env.TF_BUILD\n )\n}\n","// Bun polyfills `process.versions.node`, so detection keys off the globals instead.\nconst isBun = typeof Bun !== 'undefined'\nconst isDeno = 'Deno' in globalThis\n\n/**\n * The JavaScript runtime executing the current process: its name, version and whether it is Bun.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\nexport const runtime = { isBun, ...detect() } as const\n\nfunction detect(): { name: 'bun' | 'deno' | 'node'; version: string } {\n if (isBun) return { name: 'bun', version: process.versions.bun ?? '' }\n if (isDeno) return { name: 'deno', version: (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? '' }\n return { name: 'node', version: process.versions?.node ?? '' }\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 { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(5_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Telemetry-BhoWyCRu.js","names":["process","process"],"sources":["../../../internals/utils/src/isCIEnvironment.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/time.ts","../package.json","../src/Telemetry.ts"],"sourcesContent":["import process from 'node:process'\n\n/**\n * Returns `true` when a known CI provider has set its environment variable.\n * `CI=false` and `CI=0` count as not CI, so a user can opt out locally.\n */\nexport function isCIEnvironment(): boolean {\n const ci = process.env.CI\n return !!(\n (ci && ci !== 'false' && ci !== '0') ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.BITBUCKET_BUILD_NUMBER ||\n process.env.JENKINS_URL ||\n process.env.CIRCLECI ||\n process.env.TRAVIS ||\n process.env.TEAMCITY_VERSION ||\n process.env.BUILDKITE ||\n process.env.TF_BUILD\n )\n}\n","// Bun polyfills `process.versions.node`, so detection keys off the globals instead.\nconst isBun = typeof Bun !== 'undefined'\nconst isDeno = 'Deno' in globalThis\n\n/**\n * The JavaScript runtime executing the current process: its name, version and whether it is Bun.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\nexport const runtime = { isBun, ...detect() } as const\n\nfunction detect(): { name: 'bun' | 'deno' | 'node'; version: string } {\n if (isBun) return { name: 'bun', version: process.versions.bun ?? '' }\n if (isDeno) return { name: 'deno', version: (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? '' }\n return { name: 'node', version: process.versions?.node ?? '' }\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 { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(5_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
@@ -79,7 +79,7 @@ function formatMs(ms) {
79
79
  }
80
80
  //#endregion
81
81
  //#region package.json
82
- var version = "5.12.0";
82
+ var version = "5.12.2";
83
83
  //#endregion
84
84
  //#region src/Telemetry.ts
85
85
  /** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */
@@ -279,4 +279,4 @@ Object.defineProperty(exports, "version", {
279
279
  }
280
280
  });
281
281
 
282
- //# sourceMappingURL=Telemetry-Gl2uIT4u.cjs.map
282
+ //# sourceMappingURL=Telemetry-C7v3wFNp.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Telemetry-Gl2uIT4u.cjs","names":["process","process","randomBytes","getAgentProfile","os"],"sources":["../../../internals/utils/src/isCIEnvironment.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/time.ts","../package.json","../src/Telemetry.ts"],"sourcesContent":["import process from 'node:process'\n\n/**\n * Returns `true` when a known CI provider has set its environment variable.\n * `CI=false` and `CI=0` count as not CI, so a user can opt out locally.\n */\nexport function isCIEnvironment(): boolean {\n const ci = process.env.CI\n return !!(\n (ci && ci !== 'false' && ci !== '0') ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.BITBUCKET_BUILD_NUMBER ||\n process.env.JENKINS_URL ||\n process.env.CIRCLECI ||\n process.env.TRAVIS ||\n process.env.TEAMCITY_VERSION ||\n process.env.BUILDKITE ||\n process.env.TF_BUILD\n )\n}\n","// Bun polyfills `process.versions.node`, so detection keys off the globals instead.\nconst isBun = typeof Bun !== 'undefined'\nconst isDeno = 'Deno' in globalThis\n\n/**\n * The JavaScript runtime executing the current process: its name, version and whether it is Bun.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\nexport const runtime = { isBun, ...detect() } as const\n\nfunction detect(): { name: 'bun' | 'deno' | 'node'; version: string } {\n if (isBun) return { name: 'bun', version: process.versions.bun ?? '' }\n if (isDeno) return { name: 'deno', version: (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? '' }\n return { name: 'node', version: process.versions?.node ?? '' }\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 { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(5_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"Telemetry-C7v3wFNp.cjs","names":["process","process","randomBytes","getAgentProfile","os"],"sources":["../../../internals/utils/src/isCIEnvironment.ts","../../../internals/utils/src/runtime.ts","../../../internals/utils/src/time.ts","../package.json","../src/Telemetry.ts"],"sourcesContent":["import process from 'node:process'\n\n/**\n * Returns `true` when a known CI provider has set its environment variable.\n * `CI=false` and `CI=0` count as not CI, so a user can opt out locally.\n */\nexport function isCIEnvironment(): boolean {\n const ci = process.env.CI\n return !!(\n (ci && ci !== 'false' && ci !== '0') ||\n process.env.GITHUB_ACTIONS ||\n process.env.GITLAB_CI ||\n process.env.BITBUCKET_BUILD_NUMBER ||\n process.env.JENKINS_URL ||\n process.env.CIRCLECI ||\n process.env.TRAVIS ||\n process.env.TEAMCITY_VERSION ||\n process.env.BUILDKITE ||\n process.env.TF_BUILD\n )\n}\n","// Bun polyfills `process.versions.node`, so detection keys off the globals instead.\nconst isBun = typeof Bun !== 'undefined'\nconst isDeno = 'Deno' in globalThis\n\n/**\n * The JavaScript runtime executing the current process: its name, version and whether it is Bun.\n *\n * @example\n * ```ts\n * runtime.name // 'bun' when run with `bun kubb`, 'node' otherwise\n * ```\n */\nexport const runtime = { isBun, ...detect() } as const\n\nfunction detect(): { name: 'bun' | 'deno' | 'node'; version: string } {\n if (isBun) return { name: 'bun', version: process.versions.bun ?? '' }\n if (isDeno) return { name: 'deno', version: (globalThis as { Deno?: { version?: { deno?: string } } }).Deno?.version?.deno ?? '' }\n return { name: 'node', version: process.versions?.node ?? '' }\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 { randomBytes } from 'node:crypto'\nimport os from 'node:os'\nimport process from 'node:process'\nimport { getElapsedMs, isCIEnvironment, runtime } from '@internals/utils'\nimport { getAgentProfile } from 'gunshi/agent'\nimport { version } from '../package.json'\n\n/** OpenTelemetry ingestion endpoint for anonymous usage telemetry. */\nconst OTLP_ENDPOINT = 'https://otlp.kubb.dev' as const\n\ntype TelemetryCommand = 'generate' | 'mcp' | 'studio' | 'validate'\n\n/**\n * Anonymous plugin name and options snapshot sent with each telemetry event.\n */\nexport type TelemetryPlugin = {\n /**\n * Plugin name as registered in the Kubb config, e.g. `'@kubb/plugin-ts'`.\n */\n name: string\n /**\n * Anonymized snapshot of the plugin options. Values are included but cannot be traced back to a user.\n */\n options: Record<string, unknown>\n}\n\n/** How a command run ended, as the command reports it to the function {@link trackRun} returned. */\ntype RunResult = {\n status: 'success' | 'failed'\n plugins?: Array<TelemetryPlugin>\n filesCreated?: number\n /**\n * Name the MCP client sends in its `initialize` request, e.g. `'claude-code'`. Only set for `kubb mcp`.\n */\n mcpClient?: string\n}\n\n/**\n * Returns `true` when telemetry is disabled via `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY`.\n */\nexport function isDisabled(): boolean {\n return ['DO_NOT_TRACK', 'KUBB_DISABLE_TELEMETRY'].some((name) => ['1', 'true'].includes(process.env[name] ?? ''))\n}\n\n/** Tracks one command run for the running CLI version; returns the reporter the command calls once it knows how it ended. */\nexport function trackRun({ command, hrStart }: { command: TelemetryCommand; hrStart: [number, number] }): (result: RunResult) => Promise<void> {\n return (result) => sendTelemetry(buildOtlpPayload({ command, duration: Math.round(getElapsedMs(hrStart)), ...result }))\n}\n\n/**\n * The OTLP trace payload for one run: a span named after the command whose attributes are the\n * anonymous run facts. The attribute keys are the contract with otlp.kubb.dev.\n *\n * @see https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/\n */\nexport function buildOtlpPayload({\n command,\n duration,\n status,\n plugins = [],\n filesCreated = 0,\n mcpClient,\n}: RunResult & { command: TelemetryCommand; duration: number }) {\n const traceId = randomBytes(16).toString('hex')\n const spanId = randomBytes(8).toString('hex')\n const endTimeNs = BigInt(Date.now()) * 1_000_000n\n const startTimeNs = endTimeNs - BigInt(duration) * 1_000_000n\n const pluginNames = [...new Set(plugins.map((plugin) => plugin.name))].sort()\n const agent = getAgentProfile()\n\n const attributes = [\n { key: 'kubb.command', value: { stringValue: command } },\n { key: 'kubb.version', value: { stringValue: version } },\n { key: 'kubb.node_version', value: { stringValue: process.versions.node.split('.')[0] as string } },\n { key: 'kubb.runtime', value: { stringValue: runtime.name } },\n { key: 'kubb.runtime_version', value: { stringValue: runtime.version.split('.')[0] as string } },\n { key: 'kubb.platform', value: { stringValue: os.platform() } },\n { key: 'kubb.ci', value: { boolValue: isCIEnvironment() } },\n // A terminal on stdin means a person typed the command; agents, scripts, and CI pipe it.\n { key: 'kubb.tty', value: { boolValue: process.stdin.isTTY === true } },\n ...(agent.isAgent && agent.name ? [{ key: 'kubb.agent', value: { stringValue: agent.name } }] : []),\n // The client picks this name, so cap its length before it leaves the machine.\n ...(mcpClient ? [{ key: 'kubb.mcp_client', value: { stringValue: mcpClient.slice(0, 64) } }] : []),\n { key: 'kubb.files_created', value: { intValue: filesCreated } },\n { key: 'kubb.status', value: { stringValue: status } },\n {\n key: 'kubb.plugins',\n value: { arrayValue: { values: pluginNames.map((name) => ({ stringValue: name })) } },\n },\n ...pluginNames.map((name) => ({ key: `kubb.plugin.${name}`, value: { boolValue: true } })),\n {\n key: 'kubb.plugin_options',\n value: {\n arrayValue: {\n values: plugins.map((p) => ({\n kvlistValue: {\n values: [\n { key: 'name', value: { stringValue: p.name } },\n {\n key: 'options',\n value: {\n stringValue: JSON.stringify({\n ...p.options,\n usedEnumNames: undefined,\n }),\n },\n },\n ],\n },\n })),\n },\n },\n },\n ]\n\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'kubb' } },\n { key: 'service.version', value: { stringValue: version } },\n { key: 'telemetry.sdk.language', value: { stringValue: 'nodejs' } },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'kubb-core', version },\n spans: [\n {\n traceId,\n spanId,\n name: command,\n kind: 1,\n startTimeUnixNano: String(startTimeNs),\n endTimeUnixNano: String(endTimeNs),\n attributes,\n status: {\n code: status === 'success' ? 1 : 2,\n },\n },\n ],\n },\n ],\n },\n ],\n }\n}\n\n/**\n * Send an OTLP payload to the Kubb endpoint. Respects `DO_NOT_TRACK` and\n * `KUBB_DISABLE_TELEMETRY`, and fails silently so telemetry never interrupts a run. No file\n * paths, OpenAPI specs, or secrets are sent.\n */\nexport async function sendTelemetry(payload: ReturnType<typeof buildOtlpPayload>): Promise<void> {\n if (isDisabled()) {\n return\n }\n\n try {\n await fetch(`${OTLP_ENDPOINT}/v1/traces`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Kubb-Telemetry-Version': '1',\n 'Kubb-Telemetry-Source': 'kubb-cli',\n },\n body: JSON.stringify(payload),\n signal: AbortSignal.timeout(5_000),\n })\n } catch (_e) {\n // Fail silently, telemetry must never break the run\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-CPE0t6oh.cjs");
2
2
  const require_credentials = require("./credentials-DDaKqtJn.cjs");
3
- const require_run = require("./run-CLJnncEi.cjs");
3
+ const require_run = require("./run-T82Jef8b.cjs");
4
4
  require("node:crypto");
5
5
  let tinyexec = require("tinyexec");
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -190,4 +190,4 @@ exports.getWorkerStatus = getWorkerStatus;
190
190
  exports.startWorker = startWorker;
191
191
  exports.stopWorker = stopWorker;
192
192
 
193
- //# sourceMappingURL=background-Bi46Uzf1.cjs.map
193
+ //# sourceMappingURL=background-BCDVfX30.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"background-Bi46Uzf1.cjs","names":["path","getProjectKubbHome","readFile","connectSocket","Diagnostics","delay","prepareConnection","createRequire","x"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"background-BCDVfX30.cjs","names":["path","getProjectKubbHome","readFile","connectSocket","Diagnostics","delay","prepareConnection","createRequire","x"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- export { t as getWorkerLogPath, n as getWorkerStatus, i as startWorker, a as stopWorker } from "./background-DyD2KqN5.js";
1
+ export { t as getWorkerLogPath, n as getWorkerStatus, i as startWorker, a as stopWorker } from "./background-CUF-snCU.js";
@@ -1,5 +1,5 @@
1
1
  import "./rolldown-runtime-CRm0XQPb.js";
2
- import { o as prepareConnection } from "./run-ChGaCan5.js";
2
+ import { o as prepareConnection } from "./run-BcTgwks6.js";
3
3
  import { i as getProjectKubbHome } from "./credentials-CZK6zPLv.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { hash, randomBytes } from "node:crypto";
@@ -272,4 +272,4 @@ async function startWorker(options) {
272
272
  //#endregion
273
273
  export { stopWorker as a, startWorker as i, getWorkerStatus as n, serveWorker as r, getWorkerLogPath as t };
274
274
 
275
- //# sourceMappingURL=background-DyD2KqN5.js.map
275
+ //# sourceMappingURL=background-CUF-snCU.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"background-DyD2KqN5.js","names":["connectSocket","delay"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"background-CUF-snCU.js","names":["connectSocket","delay"],"sources":["../src/runners/studio/background.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport { readFile, rename, writeFile } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { connect as connectSocket, createServer } from 'node:net'\nimport path from 'node:path'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { Diagnostics } from '@kubb/core'\nimport { x } from 'tinyexec'\nimport { getProjectKubbHome } from './credentials.ts'\nimport { prepareConnection, type PreparedConnection, type StudioOptions, type WorkerState } from './run.ts'\n\nexport type WorkerRecord = {\n id: string\n pid: number\n port: number\n state: WorkerState\n /**\n * When the worker started, in epoch milliseconds. Absent in a record from an older CLI.\n */\n startedAt?: number\n}\nconst states: Array<WorkerState> = ['starting', 'connected', 'reconnecting', 'authentication required', 'stopped']\nexport const getWorkerLogPath = () => path.join(getProjectKubbHome(), 'worker.log')\nconst statePath = () => path.join(getProjectKubbHome(), 'worker.json')\n\nasync function readWorker(): Promise<WorkerRecord | null> {\n try {\n const value: unknown = JSON.parse(await readFile(statePath(), 'utf8'))\n if (!value || typeof value !== 'object') return null\n const record = value as Partial<WorkerRecord>\n return typeof record.id === 'string' &&\n /^[a-f0-9]{32}$/.test(record.id) &&\n Number.isInteger(record.pid) &&\n (record.pid ?? 0) > 1 &&\n Number.isInteger(record.port) &&\n (record.port ?? 0) > 0 &&\n (record.port ?? 0) <= 65535 &&\n states.includes(record.state as WorkerState)\n ? (record as WorkerRecord)\n : null\n } catch {\n return null\n }\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === 'EPERM'\n }\n}\n\n/** Authenticated loopback IPC; a stored PID never authorizes sending a process signal. */\nfunction requestWorker(record: WorkerRecord, command: 'status' | 'stop'): Promise<WorkerRecord> {\n return new Promise((resolve, reject) => {\n const socket = connectSocket({ host: '127.0.0.1', port: record.port })\n let received = ''\n socket.setTimeout(2_000, () => socket.destroy(new Error('Worker control timed out')))\n socket.once('error', reject)\n socket.once('connect', () => socket.write(JSON.stringify({ id: record.id, command }) + '\\n'))\n socket.on('data', (chunk: Buffer) => {\n received += chunk.toString()\n if (received.length > 4096) socket.destroy(new Error('Invalid worker response'))\n })\n socket.once('end', () => {\n try {\n const response = JSON.parse(received) as WorkerRecord\n if (response.id !== record.id || response.pid !== record.pid || !states.includes(response.state))\n throw new Diagnostics.Error({ code: Diagnostics.code.internalError, severity: 'error', message: 'Worker identity changed' })\n resolve(response)\n } catch (error) {\n reject(error)\n }\n })\n })\n}\n\nexport async function getWorkerStatus(): Promise<{ running: boolean; state: WorkerState; pid?: number; startedAt?: number }> {\n const record = await readWorker()\n if (!record) return { running: false, state: 'stopped' }\n try {\n const response = await requestWorker(record, 'status')\n return { running: true, state: response.state, pid: response.pid, startedAt: Number.isFinite(response.startedAt) ? response.startedAt : undefined }\n } catch {\n return { running: false, state: record.state === 'authentication required' ? record.state : 'stopped' }\n }\n}\n\nexport async function stopWorker(): Promise<void> {\n const record = await readWorker()\n if (!record) return\n try {\n await requestWorker(record, 'stop')\n } catch (error) {\n if (\n (error as NodeJS.ErrnoException).code !== 'ECONNREFUSED' &&\n isAlive(record.pid) &&\n record.state !== 'stopped' &&\n record.state !== 'authentication required'\n ) {\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Cannot verify the background worker. No process was signaled.',\n help: 'Check `worker.log`, then stop the process by hand if it is still running.',\n })\n }\n return\n }\n const deadline = Date.now() + 15_000\n while (Date.now() < deadline) {\n try {\n await requestWorker(record, 'status')\n } catch {\n return\n }\n await delay(100)\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The worker has not finished shutting down. Check worker.log and try again.',\n })\n}\n\n/** The listening port is the project lock, so stale state never needs a PID-based takeover. */\nexport async function serveWorker(shutdown: AbortController) {\n const record: WorkerRecord = { id: randomBytes(16).toString('hex'), pid: process.pid, port: 0, state: 'starting', startedAt: Date.now() }\n const server = createServer((socket) => {\n let input = ''\n socket.setTimeout(2_000, () => socket.destroy())\n socket.on('error', () => {})\n socket.on('data', (chunk: Buffer) => {\n input += chunk.toString()\n if (input.length > 4096) {\n socket.destroy()\n return\n }\n if (!input.includes('\\n')) return\n try {\n const request = JSON.parse(input) as { id?: unknown; command?: unknown }\n if (request.id !== record.id || !['status', 'stop'].includes(String(request.command))) {\n socket.destroy()\n return\n }\n socket.end(JSON.stringify(record))\n if (request.command === 'stop') shutdown.abort()\n } catch {\n socket.destroy()\n }\n })\n })\n // Hash the project directory to choose its loopback port; fail on collisions.\n const port = 20_000 + (Number.parseInt(hash('sha256', getProjectKubbHome()).slice(0, 8), 16) % 30_000)\n try {\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', resolve)\n })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EADDRINUSE') throw error\n // Let a simultaneous starter finish writing its record before checking ownership.\n await delay(100)\n const existing = await readWorker()\n if (existing?.port === port) {\n try {\n return { existing: await requestWorker(existing, 'status') }\n } catch {\n /* Refuse an unverified listener. */\n }\n }\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'The project control port is occupied or another worker is starting. Try again after stopping that listener.',\n })\n }\n record.port = port\n let saving = Promise.resolve()\n const save = () => {\n const value = JSON.stringify(record)\n saving = saving.then(async () => {\n const temporary = `${statePath()}.${record.id}`\n await writeFile(temporary, value, { mode: 0o600 })\n await rename(temporary, statePath())\n })\n return saving\n }\n try {\n await save()\n } catch (error) {\n server.close()\n throw error\n }\n return {\n async state(state: WorkerState) {\n record.state = state\n await save()\n },\n async close() {\n try {\n if (record.state !== 'authentication required') {\n record.state = 'stopped'\n await save()\n }\n } finally {\n await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))\n }\n },\n }\n}\n\nexport type WorkerLaunch = { options: StudioOptions; prepared: PreparedConnection }\n\nexport async function startWorker(options: StudioOptions): Promise<void> {\n const current = await getWorkerStatus()\n if (current.running) {\n console.log(`Already running (${current.state}, PID ${current.pid}).`)\n return\n }\n const prepared = await prepareConnection(options)\n const entry = path.join(path.dirname(createRequire(import.meta.url).resolve('@kubb/cli/package.json')), 'dist', 'studioWorker.js')\n const child = x(process.execPath, [...process.execArgv, entry], {\n nodeOptions: { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] },\n }).process\n if (!child)\n throw new Diagnostics.Error({\n code: Diagnostics.code.unknown,\n severity: 'error',\n message: 'Background worker failed to start',\n help: 'Run `kubb studio` in the foreground to see why it fails.',\n })\n try {\n const started = new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => done(new Error('Background worker startup timed out')), 20_000)\n const done = (error?: Error) => {\n clearTimeout(timer)\n child.removeListener('error', failed)\n child.removeListener('exit', exited)\n child.removeListener('message', ready)\n if (error) reject(error)\n else resolve()\n }\n const failed = (error: Error) => done(error)\n const exited = () => done(new Error(`Background worker exited during startup. Check ${getWorkerLogPath()}`))\n child.once('error', failed)\n child.once('exit', exited)\n const ready = (message: unknown) => {\n if (!message || typeof message !== 'object' || !('started' in message) || message.started !== true)\n return done(new Error('Invalid worker startup response'))\n done()\n }\n child.once('message', ready)\n child.send({ options: { ...options, autoOpen: false }, prepared } satisfies WorkerLaunch, (error) => {\n if (error) done(error)\n })\n })\n await started\n if (child.connected) child.disconnect()\n child.unref()\n const status = await getWorkerStatus()\n console.log(`Background worker ${status.state} (PID ${status.pid ?? child.pid}). Logs: ${getWorkerLogPath()}`)\n } catch (error) {\n // This is the child we just spawned, not a PID recovered from disk.\n child.kill('SIGTERM')\n throw error\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"credentials-CZK6zPLv.js","names":[],"sources":["../src/runners/studio/credentials.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { kubbStorage, resolveKubbDir } from '@kubb/core'\n\nconst projectBase = (project = process.cwd()) => path.join('projects', hash('sha256', project))\n\n/**\n * Root for the credential and machine identity of the current project, inside `KUBB_HOME`.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n return resolveKubbDir(projectBase(project))\n}\n\nconst projectStorage = () => kubbStorage({ base: projectBase() })\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n /**\n * The Studio instance this machine paired with. A different `--url` means re-pairing.\n */\n studioUrl: string\n /**\n * Bearer token for this machine. Never logged, never printed.\n */\n token: string\n agentId: string\n agentSlug: string\n /**\n * The organization that approved the pairing, absent when Studio sends none.\n */\n organizationSlug?: string\n /**\n * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n * once per project instead of on every run.\n */\n projects?: Record<string, { allowRead?: boolean; allowReadAst?: boolean; allowWrite?: boolean; allowConfigEdit?: boolean; allowExec?: boolean }>\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n const stored = await projectStorage()\n .getItem<Credentials>(CREDENTIALS_FILENAME)\n .catch(() => null)\n\n return typeof stored?.token === 'string' && stored.token !== '' ? stored : null\n}\n\n/**\n * Writes the credential into the project folder, which only its owner can open.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n await projectStorage().setItem(CREDENTIALS_FILENAME, credentials)\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n await projectStorage().removeItem(CREDENTIALS_FILENAME)\n}\n\n/**\n * Reads this project's machine secret, creating it on first use. Pairing sends its hash, so pairing\n * the same project again rotates one agent's token instead of creating a second agent. It lives\n * apart from the credential, which {@link clearCredentials} deletes.\n */\nexport async function getMachineId(): Promise<string> {\n const storage = projectStorage()\n const stored = await storage.getItem('machine-secret').catch(() => null)\n if (typeof stored === 'string' && stored.trim()) return stored.trim()\n\n const secret = randomBytes(32).toString('hex')\n await storage.setItem('machine-secret', secret)\n\n return secret\n}\n"],"mappings":";;;;;;AAKA,MAAM,eAAe,UAAU,QAAQ,IAAI,MAAM,KAAK,KAAK,YAAY,KAAK,UAAU,OAAO,CAAC;;;;;;;AAQ9F,SAAgB,mBAAmB,UAAU,QAAQ,IAAI,GAAW;CAClE,OAAO,eAAe,YAAY,OAAO,CAAC;AAC5C;AAEA,MAAM,uBAAuB,YAAY,EAAE,MAAM,YAAY,EAAE,CAAC;AAEhE,MAAM,uBAAuB;;;;AAK7B,SAAgB,qBAA6B;CAC3C,OAAO,KAAK,KAAK,mBAAmB,GAAG,oBAAoB;AAC7D;;;;;;AA6BA,eAAsB,kBAA+C;CACnE,MAAM,SAAS,MAAM,eAAe,CAAC,CAClC,QAAqB,oBAAoB,CAAC,CAC1C,YAAY,IAAI;CAEnB,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,UAAU,KAAK,SAAS;AAC7E;;;;AAKA,eAAsB,iBAAiB,aAAyC;CAC9E,MAAM,eAAe,CAAC,CAAC,QAAQ,sBAAsB,WAAW;AAClE;;;;AAKA,eAAsB,mBAAkC;CACtD,MAAM,eAAe,CAAC,CAAC,WAAW,oBAAoB;AACxD;;;;;;AAOA,eAAsB,eAAgC;CACpD,MAAM,UAAU,eAAe;CAC/B,MAAM,SAAS,MAAM,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,YAAY,IAAI;CACvE,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK;CAEpE,MAAM,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC7C,MAAM,QAAQ,QAAQ,kBAAkB,MAAM;CAE9C,OAAO;AACT"}
1
+ {"version":3,"file":"credentials-CZK6zPLv.js","names":[],"sources":["../src/runners/studio/credentials.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { kubbStorage, resolveKubbDir } from '@kubb/core'\n\nconst projectBase = (project = process.cwd()) => path.join('projects', hash('sha256', project))\n\n/**\n * Root for the credential and machine identity of the current project, inside `KUBB_HOME`.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n return resolveKubbDir(projectBase(project))\n}\n\nconst projectStorage = () => kubbStorage({ base: projectBase() })\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n /**\n * The Studio instance this machine paired with. A different `--url` means re-pairing.\n */\n studioUrl: string\n /**\n * Bearer token for this machine. Never logged, never printed.\n */\n token: string\n agentId: string\n agentSlug: string\n /**\n * The organization that approved the pairing, absent when Studio sends none.\n */\n organizationSlug?: string\n /**\n * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n * once per project instead of on every run.\n */\n projects?: Record<\n string,\n {\n allowRead?: boolean\n allowReadAst?: boolean\n allowWrite?: boolean\n allowConfigEdit?: boolean\n allowInstall?: boolean\n allowUpdate?: boolean\n allowExec?: boolean\n }\n >\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n const stored = await projectStorage()\n .getItem<Credentials>(CREDENTIALS_FILENAME)\n .catch(() => null)\n\n return typeof stored?.token === 'string' && stored.token !== '' ? stored : null\n}\n\n/**\n * Writes the credential into the project folder, which only its owner can open.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n await projectStorage().setItem(CREDENTIALS_FILENAME, credentials)\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n await projectStorage().removeItem(CREDENTIALS_FILENAME)\n}\n\n/**\n * Reads this project's machine secret, creating it on first use. Pairing sends its hash, so pairing\n * the same project again rotates one agent's token instead of creating a second agent. It lives\n * apart from the credential, which {@link clearCredentials} deletes.\n */\nexport async function getMachineId(): Promise<string> {\n const storage = projectStorage()\n const stored = await storage.getItem('machine-secret').catch(() => null)\n if (typeof stored === 'string' && stored.trim()) return stored.trim()\n\n const secret = randomBytes(32).toString('hex')\n await storage.setItem('machine-secret', secret)\n\n return secret\n}\n"],"mappings":";;;;;;AAKA,MAAM,eAAe,UAAU,QAAQ,IAAI,MAAM,KAAK,KAAK,YAAY,KAAK,UAAU,OAAO,CAAC;;;;;;;AAQ9F,SAAgB,mBAAmB,UAAU,QAAQ,IAAI,GAAW;CAClE,OAAO,eAAe,YAAY,OAAO,CAAC;AAC5C;AAEA,MAAM,uBAAuB,YAAY,EAAE,MAAM,YAAY,EAAE,CAAC;AAEhE,MAAM,uBAAuB;;;;AAK7B,SAAgB,qBAA6B;CAC3C,OAAO,KAAK,KAAK,mBAAmB,GAAG,oBAAoB;AAC7D;;;;;;AAwCA,eAAsB,kBAA+C;CACnE,MAAM,SAAS,MAAM,eAAe,CAAC,CAClC,QAAqB,oBAAoB,CAAC,CAC1C,YAAY,IAAI;CAEnB,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,UAAU,KAAK,SAAS;AAC7E;;;;AAKA,eAAsB,iBAAiB,aAAyC;CAC9E,MAAM,eAAe,CAAC,CAAC,QAAQ,sBAAsB,WAAW;AAClE;;;;AAKA,eAAsB,mBAAkC;CACtD,MAAM,eAAe,CAAC,CAAC,WAAW,oBAAoB;AACxD;;;;;;AAOA,eAAsB,eAAgC;CACpD,MAAM,UAAU,eAAe;CAC/B,MAAM,SAAS,MAAM,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,YAAY,IAAI;CACvE,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK;CAEpE,MAAM,SAAS,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC7C,MAAM,QAAQ,QAAQ,kBAAkB,MAAM;CAE9C,OAAO;AACT"}
@@ -1 +1 @@
1
- {"version":3,"file":"credentials-DDaKqtJn.cjs","names":["process","path","hash","resolveKubbDir","kubbStorage","randomBytes"],"sources":["../src/runners/studio/credentials.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { kubbStorage, resolveKubbDir } from '@kubb/core'\n\nconst projectBase = (project = process.cwd()) => path.join('projects', hash('sha256', project))\n\n/**\n * Root for the credential and machine identity of the current project, inside `KUBB_HOME`.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n return resolveKubbDir(projectBase(project))\n}\n\nconst projectStorage = () => kubbStorage({ base: projectBase() })\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n /**\n * The Studio instance this machine paired with. A different `--url` means re-pairing.\n */\n studioUrl: string\n /**\n * Bearer token for this machine. Never logged, never printed.\n */\n token: string\n agentId: string\n agentSlug: string\n /**\n * The organization that approved the pairing, absent when Studio sends none.\n */\n organizationSlug?: string\n /**\n * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n * once per project instead of on every run.\n */\n projects?: Record<string, { allowRead?: boolean; allowReadAst?: boolean; allowWrite?: boolean; allowConfigEdit?: boolean; allowExec?: boolean }>\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n const stored = await projectStorage()\n .getItem<Credentials>(CREDENTIALS_FILENAME)\n .catch(() => null)\n\n return typeof stored?.token === 'string' && stored.token !== '' ? stored : null\n}\n\n/**\n * Writes the credential into the project folder, which only its owner can open.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n await projectStorage().setItem(CREDENTIALS_FILENAME, credentials)\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n await projectStorage().removeItem(CREDENTIALS_FILENAME)\n}\n\n/**\n * Reads this project's machine secret, creating it on first use. Pairing sends its hash, so pairing\n * the same project again rotates one agent's token instead of creating a second agent. It lives\n * apart from the credential, which {@link clearCredentials} deletes.\n */\nexport async function getMachineId(): Promise<string> {\n const storage = projectStorage()\n const stored = await storage.getItem('machine-secret').catch(() => null)\n if (typeof stored === 'string' && stored.trim()) return stored.trim()\n\n const secret = randomBytes(32).toString('hex')\n await storage.setItem('machine-secret', secret)\n\n return secret\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAKA,MAAM,eAAe,UAAUA,aAAAA,QAAQ,IAAI,MAAMC,UAAAA,QAAK,KAAK,aAAA,GAAYC,YAAAA,KAAAA,CAAK,UAAU,OAAO,CAAC;;;;;;;AAQ9F,SAAgB,mBAAmB,UAAUF,aAAAA,QAAQ,IAAI,GAAW;CAClE,QAAA,GAAOG,WAAAA,eAAAA,CAAe,YAAY,OAAO,CAAC;AAC5C;AAEA,MAAM,wBAAA,GAAuBC,WAAAA,YAAAA,CAAY,EAAE,MAAM,YAAY,EAAE,CAAC;AAEhE,MAAM,uBAAuB;;;;AAK7B,SAAgB,qBAA6B;CAC3C,OAAOH,UAAAA,QAAK,KAAK,mBAAmB,GAAG,oBAAoB;AAC7D;;;;;;AA6BA,eAAsB,kBAA+C;CACnE,MAAM,SAAS,MAAM,eAAe,CAAC,CAClC,QAAqB,oBAAoB,CAAC,CAC1C,YAAY,IAAI;CAEnB,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,UAAU,KAAK,SAAS;AAC7E;;;;AAKA,eAAsB,iBAAiB,aAAyC;CAC9E,MAAM,eAAe,CAAC,CAAC,QAAQ,sBAAsB,WAAW;AAClE;;;;AAKA,eAAsB,mBAAkC;CACtD,MAAM,eAAe,CAAC,CAAC,WAAW,oBAAoB;AACxD;;;;;;AAOA,eAAsB,eAAgC;CACpD,MAAM,UAAU,eAAe;CAC/B,MAAM,SAAS,MAAM,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,YAAY,IAAI;CACvE,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK;CAEpE,MAAM,UAAA,GAASI,YAAAA,YAAAA,CAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC7C,MAAM,QAAQ,QAAQ,kBAAkB,MAAM;CAE9C,OAAO;AACT"}
1
+ {"version":3,"file":"credentials-DDaKqtJn.cjs","names":["process","path","hash","resolveKubbDir","kubbStorage","randomBytes"],"sources":["../src/runners/studio/credentials.ts"],"sourcesContent":["import { hash, randomBytes } from 'node:crypto'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { kubbStorage, resolveKubbDir } from '@kubb/core'\n\nconst projectBase = (project = process.cwd()) => path.join('projects', hash('sha256', project))\n\n/**\n * Root for the credential and machine identity of the current project, inside `KUBB_HOME`.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n return resolveKubbDir(projectBase(project))\n}\n\nconst projectStorage = () => kubbStorage({ base: projectBase() })\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n /**\n * The Studio instance this machine paired with. A different `--url` means re-pairing.\n */\n studioUrl: string\n /**\n * Bearer token for this machine. Never logged, never printed.\n */\n token: string\n agentId: string\n agentSlug: string\n /**\n * The organization that approved the pairing, absent when Studio sends none.\n */\n organizationSlug?: string\n /**\n * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n * once per project instead of on every run.\n */\n projects?: Record<\n string,\n {\n allowRead?: boolean\n allowReadAst?: boolean\n allowWrite?: boolean\n allowConfigEdit?: boolean\n allowInstall?: boolean\n allowUpdate?: boolean\n allowExec?: boolean\n }\n >\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n const stored = await projectStorage()\n .getItem<Credentials>(CREDENTIALS_FILENAME)\n .catch(() => null)\n\n return typeof stored?.token === 'string' && stored.token !== '' ? stored : null\n}\n\n/**\n * Writes the credential into the project folder, which only its owner can open.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n await projectStorage().setItem(CREDENTIALS_FILENAME, credentials)\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n await projectStorage().removeItem(CREDENTIALS_FILENAME)\n}\n\n/**\n * Reads this project's machine secret, creating it on first use. Pairing sends its hash, so pairing\n * the same project again rotates one agent's token instead of creating a second agent. It lives\n * apart from the credential, which {@link clearCredentials} deletes.\n */\nexport async function getMachineId(): Promise<string> {\n const storage = projectStorage()\n const stored = await storage.getItem('machine-secret').catch(() => null)\n if (typeof stored === 'string' && stored.trim()) return stored.trim()\n\n const secret = randomBytes(32).toString('hex')\n await storage.setItem('machine-secret', secret)\n\n return secret\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAKA,MAAM,eAAe,UAAUA,aAAAA,QAAQ,IAAI,MAAMC,UAAAA,QAAK,KAAK,aAAA,GAAYC,YAAAA,KAAAA,CAAK,UAAU,OAAO,CAAC;;;;;;;AAQ9F,SAAgB,mBAAmB,UAAUF,aAAAA,QAAQ,IAAI,GAAW;CAClE,QAAA,GAAOG,WAAAA,eAAAA,CAAe,YAAY,OAAO,CAAC;AAC5C;AAEA,MAAM,wBAAA,GAAuBC,WAAAA,YAAAA,CAAY,EAAE,MAAM,YAAY,EAAE,CAAC;AAEhE,MAAM,uBAAuB;;;;AAK7B,SAAgB,qBAA6B;CAC3C,OAAOH,UAAAA,QAAK,KAAK,mBAAmB,GAAG,oBAAoB;AAC7D;;;;;;AAwCA,eAAsB,kBAA+C;CACnE,MAAM,SAAS,MAAM,eAAe,CAAC,CAClC,QAAqB,oBAAoB,CAAC,CAC1C,YAAY,IAAI;CAEnB,OAAO,OAAO,QAAQ,UAAU,YAAY,OAAO,UAAU,KAAK,SAAS;AAC7E;;;;AAKA,eAAsB,iBAAiB,aAAyC;CAC9E,MAAM,eAAe,CAAC,CAAC,QAAQ,sBAAsB,WAAW;AAClE;;;;AAKA,eAAsB,mBAAkC;CACtD,MAAM,eAAe,CAAC,CAAC,WAAW,oBAAoB;AACxD;;;;;;AAOA,eAAsB,eAAgC;CACpD,MAAM,UAAU,eAAe;CAC/B,MAAM,SAAS,MAAM,QAAQ,QAAQ,gBAAgB,CAAC,CAAC,YAAY,IAAI;CACvE,IAAI,OAAO,WAAW,YAAY,OAAO,KAAK,GAAG,OAAO,OAAO,KAAK;CAEpE,MAAM,UAAA,GAASI,YAAAA,YAAAA,CAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAC7C,MAAM,QAAQ,QAAQ,kBAAkB,MAAM;CAE9C,OAAO;AACT"}
package/dist/index.cjs CHANGED
@@ -1,12 +1,12 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("./rolldown-runtime-CPE0t6oh.cjs");
3
3
  const require_errors = require("./errors-Cl69p_vI.cjs");
4
- const require_Telemetry = require("./Telemetry-Gl2uIT4u.cjs");
5
- const require_utils = require("./utils-Dyd-rk1U.cjs");
6
- const require_text = require("./text-BKjdI_f1.cjs");
7
- const require_registry = require("./registry-BnkCWWeX.cjs");
4
+ const require_Telemetry = require("./Telemetry-C7v3wFNp.cjs");
5
+ const require_utils = require("./utils-Cb4fBZiX.cjs");
6
+ const require_text = require("./text-DfFJj91M.cjs");
7
+ const require_registry = require("./registry-Cl-wgD7H.cjs");
8
8
  const require_config = require("./config-D63Gum4F.cjs");
9
- const require_summary = require("./summary-DQ_SULKW.cjs");
9
+ const require_summary = require("./summary-BwbfP0Qj.cjs");
10
10
  let node_util = require("node:util");
11
11
  let _gunshi_plugin_dryrun = require("@gunshi/plugin-dryrun");
12
12
  _gunshi_plugin_dryrun = require_rolldown_runtime.__toESM(_gunshi_plugin_dryrun, 1);
@@ -80,6 +80,18 @@ const studioPermissionArgs = {
80
80
  default: false,
81
81
  toKebab: true
82
82
  },
83
+ allowInstall: {
84
+ type: "boolean",
85
+ description: "Let Studio install extensions from the registry with kubb add; needs --allow-config-edit too. Asked for once per project when omitted",
86
+ default: false,
87
+ toKebab: true
88
+ },
89
+ allowUpdate: {
90
+ type: "boolean",
91
+ description: "Let Studio update installed extensions with kubb update. Asked for once per project when omitted",
92
+ default: false,
93
+ toKebab: true
94
+ },
83
95
  allowExec: {
84
96
  type: "boolean",
85
97
  description: "Run the formatter, the linter, and output.postGenerate after a generation",
@@ -167,7 +179,7 @@ const command$13 = (0, gunshi.defineWithTypes)()({
167
179
  async run(ctx) {
168
180
  const { values } = ctx;
169
181
  const logLevel = resolveLogLevel(values);
170
- const { run } = await Promise.resolve().then(() => require("./run-BDLWu55k.cjs"));
182
+ const { run } = await Promise.resolve().then(() => require("./run-CK19O5v2.cjs"));
171
183
  await run({
172
184
  input: values.input,
173
185
  configPath: values.config,
@@ -191,7 +203,7 @@ const command$12 = (0, gunshi.define)({
191
203
  ].join("\n"),
192
204
  rendering: { header: null },
193
205
  async run() {
194
- const { run } = await Promise.resolve().then(() => require("./run-Czt1mbNa.cjs"));
206
+ const { run } = await Promise.resolve().then(() => require("./run-DqFL5SMR.cjs"));
195
207
  await run();
196
208
  }
197
209
  });
@@ -206,7 +218,7 @@ function studioCommand({ action, block, ...definition }) {
206
218
  ...definition,
207
219
  toKebab: true,
208
220
  async run({ values }) {
209
- const { runStudio } = await Promise.resolve().then(() => require("./run-CLJnncEi.cjs"));
221
+ const { runStudio } = await Promise.resolve().then(() => require("./run-T82Jef8b.cjs"));
210
222
  await runStudio(values, action, { block });
211
223
  }
212
224
  });
@@ -236,7 +248,7 @@ const command$11 = studioCommand({
236
248
  },
237
249
  block: true,
238
250
  async action(options) {
239
- const { connect } = await Promise.resolve().then(() => require("./run-CLJnncEi.cjs"));
251
+ const { connect } = await Promise.resolve().then(() => require("./run-T82Jef8b.cjs"));
240
252
  await connect(options);
241
253
  }
242
254
  });
@@ -248,7 +260,7 @@ const command$10 = studioCommand({
248
260
  examples: ["kubb studio login", "kubb studio login --url http://localhost:3000"].join("\n"),
249
261
  args: command$11.args,
250
262
  async action(options) {
251
- const { login } = await Promise.resolve().then(() => require("./run-CLJnncEi.cjs"));
263
+ const { login } = await Promise.resolve().then(() => require("./run-T82Jef8b.cjs"));
252
264
  await login(options);
253
265
  }
254
266
  });
@@ -260,7 +272,7 @@ const command$9 = studioCommand({
260
272
  examples: "kubb studio logout",
261
273
  args: {},
262
274
  async action() {
263
- const { stopWorker } = await Promise.resolve().then(() => require("./background-Bi46Uzf1.cjs"));
275
+ const { stopWorker } = await Promise.resolve().then(() => require("./background-BCDVfX30.cjs"));
264
276
  const { clearCredentials } = await Promise.resolve().then(() => require("./credentials-DDaKqtJn.cjs")).then((n) => n.credentials_exports);
265
277
  await stopWorker();
266
278
  await clearCredentials();
@@ -303,7 +315,7 @@ const command$8 = (0, gunshi.define)({
303
315
  ...logLevelArg
304
316
  },
305
317
  async run(ctx) {
306
- const { runner } = await Promise.resolve().then(() => require("./snapshot-DcLGxpoM.cjs"));
318
+ const { runner } = await Promise.resolve().then(() => require("./snapshot-D5HiDktY.cjs"));
307
319
  await runner(ctx);
308
320
  }
309
321
  });
@@ -314,7 +326,7 @@ const command$7 = studioCommand({
314
326
  description: "Keep this project connected to Kubb Studio in the background.",
315
327
  args: command$11.args,
316
328
  async action(options) {
317
- const { startWorker } = await Promise.resolve().then(() => require("./background-Bi46Uzf1.cjs"));
329
+ const { startWorker } = await Promise.resolve().then(() => require("./background-BCDVfX30.cjs"));
318
330
  await startWorker(options);
319
331
  }
320
332
  });
@@ -322,7 +334,7 @@ const stopCommand = (0, gunshi.define)({
322
334
  name: "stop",
323
335
  description: "Stop this project's background connection and keep its pairing.",
324
336
  async run() {
325
- const { stopWorker } = await Promise.resolve().then(() => require("./background-Bi46Uzf1.cjs"));
337
+ const { stopWorker } = await Promise.resolve().then(() => require("./background-BCDVfX30.cjs"));
326
338
  await stopWorker();
327
339
  console.log("Background connection stopped.");
328
340
  }
@@ -335,9 +347,9 @@ const command$6 = studioCommand({
335
347
  examples: "kubb studio status",
336
348
  args: { url: studioConnectionArgs.url },
337
349
  async action(options) {
338
- const { getWorkerLogPath, getWorkerStatus } = await Promise.resolve().then(() => require("./background-Bi46Uzf1.cjs"));
350
+ const { getWorkerLogPath, getWorkerStatus } = await Promise.resolve().then(() => require("./background-BCDVfX30.cjs"));
339
351
  const { readCredentials } = await Promise.resolve().then(() => require("./credentials-DDaKqtJn.cjs")).then((n) => n.credentials_exports);
340
- const { formatStatus } = await Promise.resolve().then(() => require("./status-DrA1XG77.cjs"));
352
+ const { formatStatus } = await Promise.resolve().then(() => require("./status-BFMmzCfD.cjs"));
341
353
  const [worker, credentials] = await Promise.all([getWorkerStatus(), readCredentials()]);
342
354
  console.log([
343
355
  "",
@@ -405,7 +417,7 @@ const command$4 = (0, gunshi.define)({
405
417
  }
406
418
  },
407
419
  async run({ values }) {
408
- const { run } = await Promise.resolve().then(() => require("./run-8KLWLcEf.cjs"));
420
+ const { run } = await Promise.resolve().then(() => require("./run-9uoP-2Xh.cjs"));
409
421
  await run({
410
422
  input: values.input,
411
423
  configPath: values.config
@@ -946,6 +958,7 @@ async function run$4({ id: input, configPath, yes, install }) {
946
958
  unconfigured.push(target);
947
959
  }
948
960
  const manual = unconfigured.length > 0;
961
+ if (manual && manifest.kind === "plugin") node_process.default.exitCode = 1;
949
962
  if (manual) require_text.logBlock([
950
963
  "Add the import to your Kubb config and merge the property into its defineConfig call:",
951
964
  "",
@@ -1436,7 +1449,7 @@ async function regenerate({ cwd, configPath }) {
1436
1449
  const spinner = require_utils.createSpinner();
1437
1450
  spinner.start("Generating with the new versions");
1438
1451
  try {
1439
- const [{ getConfigs }, { generate }] = await Promise.all([Promise.resolve().then(() => require("./config-D63Gum4F.cjs")).then((n) => n.config_exports), Promise.resolve().then(() => require("./run-BDLWu55k.cjs"))]);
1452
+ const [{ getConfigs }, { generate }] = await Promise.all([Promise.resolve().then(() => require("./config-D63Gum4F.cjs")).then((n) => n.config_exports), Promise.resolve().then(() => require("./run-CK19O5v2.cjs"))]);
1440
1453
  const { configs } = await getConfigs({ configPath });
1441
1454
  const dirs = configs.map((config) => node_path.default.resolve(config.root ?? cwd, config.output.path));
1442
1455
  const before = new Map((await Promise.all(dirs.map(require_utils.readOutputFiles))).flatMap((files) => [...files]));
@@ -1539,7 +1552,7 @@ async function run$1({ ids, dryRun, yes }) {
1539
1552
  summary("Updates available", "planned", [`Dry run: nothing was installed. Run kubb update to install ${proposals.length === 1 ? "it" : "them"}.`]);
1540
1553
  return;
1541
1554
  }
1542
- const defaults = proposals.filter((entry) => !isMajor(entry));
1555
+ const defaults = proposals.filter((entry) => wanted.has(entry.id) || !isMajor(entry));
1543
1556
  const chosen = yes ? defaults.map((entry) => entry.id) : await _clack_prompts.multiselect({
1544
1557
  message: "Select the updates to install",
1545
1558
  options: proposals.map((entry) => ({