@kubb/cli 5.2.8 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{Telemetry-DIDDCARm.js → Telemetry-CJcQkQOT.js} +2 -2
- package/dist/Telemetry-CJcQkQOT.js.map +1 -0
- package/dist/{Telemetry-C-y1OMHs.cjs → Telemetry-CvHQevSK.cjs} +4 -4
- package/dist/Telemetry-CvHQevSK.cjs.map +1 -0
- package/dist/agent-3JmkMghp.js +24 -0
- package/dist/agent-3JmkMghp.js.map +1 -0
- package/dist/agent-BPqs9VRj.cjs +36 -0
- package/dist/agent-BPqs9VRj.cjs.map +1 -0
- package/dist/{generate-Bg8-2XSd.cjs → generate-BX8SdCQd.cjs} +6 -5
- package/dist/generate-BX8SdCQd.cjs.map +1 -0
- package/dist/{generate-CKsAI5H2.js → generate-CdhwxCV3.js} +6 -5
- package/dist/generate-CdhwxCV3.js.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.js +7 -7
- package/dist/{init-BPArhQCK.cjs → init-CQWASBvV.cjs} +3 -3
- package/dist/{init-BPArhQCK.cjs.map → init-CQWASBvV.cjs.map} +1 -1
- package/dist/{init-DroN6dri.js → init-IWtia4-b.js} +3 -3
- package/dist/{init-DroN6dri.js.map → init-IWtia4-b.js.map} +1 -1
- package/dist/{output-LZvrgF3z.js → output-DH2Uoy4x.js} +32 -3
- package/dist/output-DH2Uoy4x.js.map +1 -0
- package/dist/{output-B2V2MN_C.cjs → output-kmklyYIv.cjs} +42 -7
- package/dist/output-kmklyYIv.cjs.map +1 -0
- package/dist/package-BMrVXY1Q.js +6 -0
- package/dist/package-BMrVXY1Q.js.map +1 -0
- package/dist/{package-BMZZ3mUo.cjs → package-Dp02IIsN.cjs} +2 -2
- package/dist/package-Dp02IIsN.cjs.map +1 -0
- package/dist/{run-CphhWHm6.js → run-A85qZfqo.js} +3 -3
- package/dist/{run-CphhWHm6.js.map → run-A85qZfqo.js.map} +1 -1
- package/dist/{run-DoU73Qo5.cjs → run-B0B6Kp3l.cjs} +3 -3
- package/dist/{run-DoU73Qo5.cjs.map → run-B0B6Kp3l.cjs.map} +1 -1
- package/dist/{run-Cni7891O.cjs → run-BI9Ue-_9.cjs} +3 -3
- package/dist/{run-Cni7891O.cjs.map → run-BI9Ue-_9.cjs.map} +1 -1
- package/dist/{run-BHMES-2P.cjs → run-BZKhWxUA.cjs} +51 -36
- package/dist/run-BZKhWxUA.cjs.map +1 -0
- package/dist/{run-B6bZaLqK.js → run-BoDUDlHe.js} +48 -33
- package/dist/run-BoDUDlHe.js.map +1 -0
- package/dist/{run-DL1wCsGn.cjs → run-C7kJobjS.cjs} +6 -7
- package/dist/{run-DL1wCsGn.cjs.map → run-C7kJobjS.cjs.map} +1 -1
- package/dist/{run-BcEQGlz_.js → run-Cj7xyTUs.js} +3 -3
- package/dist/{run-BcEQGlz_.js.map → run-Cj7xyTUs.js.map} +1 -1
- package/dist/{run-SxqXLbrs.js → run-I7VqNIke.js} +6 -7
- package/dist/{run-SxqXLbrs.js.map → run-I7VqNIke.js.map} +1 -1
- package/dist/{run-DRBqVXX0.cjs → run-VK50TiB7.cjs} +2 -2
- package/dist/{run-DRBqVXX0.cjs.map → run-VK50TiB7.cjs.map} +1 -1
- package/dist/{run-BzvsJ0Ek.js → run-kdxqOL8u.js} +2 -2
- package/dist/{run-BzvsJ0Ek.js.map → run-kdxqOL8u.js.map} +1 -1
- package/dist/{utils-BgEQilnN.cjs → utils-CMU1rLfY.cjs} +22 -6
- package/dist/utils-CMU1rLfY.cjs.map +1 -0
- package/dist/{utils-VadUNZOm.js → utils-s6yetN6W.js} +21 -5
- package/dist/utils-s6yetN6W.js.map +1 -0
- package/package.json +7 -7
- package/dist/Telemetry-C-y1OMHs.cjs.map +0 -1
- package/dist/Telemetry-DIDDCARm.js.map +0 -1
- package/dist/env-DHaFBZTX.cjs +0 -82
- package/dist/env-DHaFBZTX.cjs.map +0 -1
- package/dist/env-DqPKEL59.js +0 -59
- package/dist/env-DqPKEL59.js.map +0 -1
- package/dist/generate-Bg8-2XSd.cjs.map +0 -1
- package/dist/generate-CKsAI5H2.js.map +0 -1
- package/dist/output-B2V2MN_C.cjs.map +0 -1
- package/dist/output-LZvrgF3z.js.map +0 -1
- package/dist/package-BMZZ3mUo.cjs.map +0 -1
- package/dist/package-BXaDNEkt.js +0 -6
- package/dist/package-BXaDNEkt.js.map +0 -1
- package/dist/run-B6bZaLqK.js.map +0 -1
- package/dist/run-BHMES-2P.cjs.map +0 -1
- package/dist/utils-BgEQilnN.cjs.map +0 -1
- package/dist/utils-VadUNZOm.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-BMrVXY1Q.js","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region package.json
|
|
2
|
-
var version = "5.
|
|
2
|
+
var version = "5.3.1";
|
|
3
3
|
//#endregion
|
|
4
4
|
Object.defineProperty(exports, "version", {
|
|
5
5
|
enumerable: true,
|
|
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "version", {
|
|
|
8
8
|
}
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
//# sourceMappingURL=package-
|
|
11
|
+
//# sourceMappingURL=package-Dp02IIsN.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-Dp02IIsN.cjs","names":[],"sources":["../package.json"],"sourcesContent":[""],"mappings":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
2
|
import { t as toError } from "./errors-C8yNfV7w.js";
|
|
3
|
-
import { r as sendTelemetry, t as buildTelemetryEvent } from "./Telemetry-
|
|
4
|
-
import { t as version } from "./package-
|
|
3
|
+
import { r as sendTelemetry, t as buildTelemetryEvent } from "./Telemetry-CJcQkQOT.js";
|
|
4
|
+
import { t as version } from "./package-BMrVXY1Q.js";
|
|
5
5
|
import { styleText } from "node:util";
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
//#region src/runners/validate/run.ts
|
|
@@ -53,4 +53,4 @@ const runner = async ({ values }) => {
|
|
|
53
53
|
//#endregion
|
|
54
54
|
export { runner };
|
|
55
55
|
|
|
56
|
-
//# sourceMappingURL=run-
|
|
56
|
+
//# sourceMappingURL=run-A85qZfqo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-
|
|
1
|
+
{"version":3,"file":"run-A85qZfqo.js","names":[],"sources":["../src/runners/validate/run.ts"],"sourcesContent":["import process from 'node:process'\nimport { styleText } from 'node:util'\nimport { toError } from '@internals/utils'\nimport type { CommandRunner } from 'gunshi'\nimport { buildTelemetryEvent, sendTelemetry } from '../../Telemetry.ts'\nimport { version } from '../../../package.json'\nimport type { definition } from '../../commands/validate.ts'\n\ntype ValidateOptions = {\n /**\n * Path or URL to the OpenAPI/Swagger file to validate.\n */\n input: string\n /**\n * Current `@kubb/cli` version string, used for the telemetry payload.\n */\n version: string\n}\n\n/**\n * Validates an OpenAPI/Swagger file at `input` using `@kubb/adapter-oas`.\n * Exits the process with code 1 on validation failure or missing dependency.\n */\nexport async function run({ input, version }: ValidateOptions): Promise<void> {\n const hrStart = process.hrtime()\n const report = (status: 'success' | 'failed') => sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status }))\n\n try {\n const { adapterOas } = await import('@kubb/adapter-oas')\n\n const adapter = adapterOas()\n if (!adapter.validate) {\n throw new Error('The loaded adapter does not support validation.')\n }\n\n await adapter.validate(input, { throwOnError: true })\n await report('success')\n\n console.log('✅ Validation success')\n } catch (error) {\n await report('failed')\n if (error instanceof Error && /@kubb\\/adapter-oas/.test(error.message)) {\n console.error(styleText('red', 'The @kubb/adapter-oas package is not installed.'))\n console.error('')\n console.error('Install it with:')\n console.error(styleText('cyan', ' npm install @kubb/adapter-oas'))\n console.error(styleText('cyan', ' # or'))\n console.error(styleText('cyan', ' pnpm install @kubb/adapter-oas'))\n console.error('')\n }\n console.error('❌ Validation failed')\n console.error(toError(error).message)\n\n process.exit(1)\n }\n}\n\n/**\n * Loaded on demand by `index.ts`, so `@kubb/adapter-oas` stays out of the process for every other\n * command.\n */\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n await run({ input: values.input, version })\n}\n"],"mappings":";;;;;;;;;;;AAuBA,eAAsB,IAAI,EAAE,OAAO,WAA2C;CAC5E,MAAM,UAAU,QAAQ,OAAO;CAC/B,MAAM,UAAU,WAAiC,cAAc,oBAAoB;EAAE,SAAS;EAAY,aAAa;EAAS;EAAS;CAAO,CAAC,CAAC;CAElJ,IAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EAEpC,MAAM,UAAU,WAAW;EAC3B,IAAI,CAAC,QAAQ,UACX,MAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,QAAQ,SAAS,OAAO,EAAE,cAAc,KAAK,CAAC;EACpD,MAAM,OAAO,SAAS;EAEtB,QAAQ,IAAI,sBAAsB;CACpC,SAAS,OAAO;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,iBAAiB,SAAS,qBAAqB,KAAK,MAAM,OAAO,GAAG;GACtE,QAAQ,MAAM,UAAU,OAAO,iDAAiD,CAAC;GACjF,QAAQ,MAAM,EAAE;GAChB,QAAQ,MAAM,kBAAkB;GAChC,QAAQ,MAAM,UAAU,QAAQ,iCAAiC,CAAC;GAClE,QAAQ,MAAM,UAAU,QAAQ,QAAQ,CAAC;GACzC,QAAQ,MAAM,UAAU,QAAQ,kCAAkC,CAAC;GACnE,QAAQ,MAAM,EAAE;EAClB;EACA,QAAQ,MAAM,qBAAqB;EACnC,QAAQ,MAAM,QAAQ,KAAK,CAAC,CAAC,OAAO;EAEpC,QAAQ,KAAK,CAAC;CAChB;AACF;;;;;AAMA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,IAAI;EAAE,OAAO,OAAO;EAAO;CAAQ,CAAC;AAC5C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
|
|
2
2
|
const require_errors = require("./errors-C-wEou02.cjs");
|
|
3
|
-
const require_Telemetry = require("./Telemetry-
|
|
4
|
-
const require_package = require("./package-
|
|
3
|
+
const require_Telemetry = require("./Telemetry-CvHQevSK.cjs");
|
|
4
|
+
const require_package = require("./package-Dp02IIsN.cjs");
|
|
5
5
|
let node_util = require("node:util");
|
|
6
6
|
let node_process = require("node:process");
|
|
7
7
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
@@ -54,4 +54,4 @@ const runner = async ({ values }) => {
|
|
|
54
54
|
//#endregion
|
|
55
55
|
exports.runner = runner;
|
|
56
56
|
|
|
57
|
-
//# sourceMappingURL=run-
|
|
57
|
+
//# sourceMappingURL=run-B0B6Kp3l.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-
|
|
1
|
+
{"version":3,"file":"run-B0B6Kp3l.cjs","names":["process","sendTelemetry","buildTelemetryEvent","styleText","toError"],"sources":["../src/runners/validate/run.ts"],"sourcesContent":["import process from 'node:process'\nimport { styleText } from 'node:util'\nimport { toError } from '@internals/utils'\nimport type { CommandRunner } from 'gunshi'\nimport { buildTelemetryEvent, sendTelemetry } from '../../Telemetry.ts'\nimport { version } from '../../../package.json'\nimport type { definition } from '../../commands/validate.ts'\n\ntype ValidateOptions = {\n /**\n * Path or URL to the OpenAPI/Swagger file to validate.\n */\n input: string\n /**\n * Current `@kubb/cli` version string, used for the telemetry payload.\n */\n version: string\n}\n\n/**\n * Validates an OpenAPI/Swagger file at `input` using `@kubb/adapter-oas`.\n * Exits the process with code 1 on validation failure or missing dependency.\n */\nexport async function run({ input, version }: ValidateOptions): Promise<void> {\n const hrStart = process.hrtime()\n const report = (status: 'success' | 'failed') => sendTelemetry(buildTelemetryEvent({ command: 'validate', kubbVersion: version, hrStart, status }))\n\n try {\n const { adapterOas } = await import('@kubb/adapter-oas')\n\n const adapter = adapterOas()\n if (!adapter.validate) {\n throw new Error('The loaded adapter does not support validation.')\n }\n\n await adapter.validate(input, { throwOnError: true })\n await report('success')\n\n console.log('✅ Validation success')\n } catch (error) {\n await report('failed')\n if (error instanceof Error && /@kubb\\/adapter-oas/.test(error.message)) {\n console.error(styleText('red', 'The @kubb/adapter-oas package is not installed.'))\n console.error('')\n console.error('Install it with:')\n console.error(styleText('cyan', ' npm install @kubb/adapter-oas'))\n console.error(styleText('cyan', ' # or'))\n console.error(styleText('cyan', ' pnpm install @kubb/adapter-oas'))\n console.error('')\n }\n console.error('❌ Validation failed')\n console.error(toError(error).message)\n\n process.exit(1)\n }\n}\n\n/**\n * Loaded on demand by `index.ts`, so `@kubb/adapter-oas` stays out of the process for every other\n * command.\n */\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n await run({ input: values.input, version })\n}\n"],"mappings":";;;;;;;;;;;;AAuBA,eAAsB,IAAI,EAAE,OAAO,WAA2C;CAC5E,MAAM,UAAUA,aAAAA,QAAQ,OAAO;CAC/B,MAAM,UAAU,WAAiCC,kBAAAA,cAAcC,kBAAAA,oBAAoB;EAAE,SAAS;EAAY,aAAa;EAAS;EAAS;CAAO,CAAC,CAAC;CAElJ,IAAI;EACF,MAAM,EAAE,eAAe,MAAM,OAAO;EAEpC,MAAM,UAAU,WAAW;EAC3B,IAAI,CAAC,QAAQ,UACX,MAAM,IAAI,MAAM,iDAAiD;EAGnE,MAAM,QAAQ,SAAS,OAAO,EAAE,cAAc,KAAK,CAAC;EACpD,MAAM,OAAO,SAAS;EAEtB,QAAQ,IAAI,sBAAsB;CACpC,SAAS,OAAO;EACd,MAAM,OAAO,QAAQ;EACrB,IAAI,iBAAiB,SAAS,qBAAqB,KAAK,MAAM,OAAO,GAAG;GACtE,QAAQ,OAAA,GAAMC,UAAAA,UAAAA,CAAU,OAAO,iDAAiD,CAAC;GACjF,QAAQ,MAAM,EAAE;GAChB,QAAQ,MAAM,kBAAkB;GAChC,QAAQ,OAAA,GAAMA,UAAAA,UAAAA,CAAU,QAAQ,iCAAiC,CAAC;GAClE,QAAQ,OAAA,GAAMA,UAAAA,UAAAA,CAAU,QAAQ,QAAQ,CAAC;GACzC,QAAQ,OAAA,GAAMA,UAAAA,UAAAA,CAAU,QAAQ,kCAAkC,CAAC;GACnE,QAAQ,MAAM,EAAE;EAClB;EACA,QAAQ,MAAM,qBAAqB;EACnC,QAAQ,MAAMC,eAAAA,QAAQ,KAAK,CAAC,CAAC,OAAO;EAEpC,aAAA,QAAQ,KAAK,CAAC;CAChB;AACF;;;;;AAMA,MAAa,SAA0E,OAAO,EAAE,aAAa;CAC3G,MAAM,IAAI;EAAE,OAAO,OAAO;EAAO,SAAA,gBAAA;CAAQ,CAAC;AAC5C"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
|
|
2
2
|
const require_errors = require("./errors-C-wEou02.cjs");
|
|
3
|
-
const require_Telemetry = require("./Telemetry-
|
|
4
|
-
const require_package = require("./package-
|
|
3
|
+
const require_Telemetry = require("./Telemetry-CvHQevSK.cjs");
|
|
4
|
+
const require_package = require("./package-Dp02IIsN.cjs");
|
|
5
5
|
let node_util = require("node:util");
|
|
6
6
|
let node_process = require("node:process");
|
|
7
7
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
@@ -38,4 +38,4 @@ const runner = async () => {
|
|
|
38
38
|
//#endregion
|
|
39
39
|
exports.runner = runner;
|
|
40
40
|
|
|
41
|
-
//# sourceMappingURL=run-
|
|
41
|
+
//# sourceMappingURL=run-BI9Ue-_9.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-
|
|
1
|
+
{"version":3,"file":"run-BI9Ue-_9.cjs","names":["process","sendTelemetry","buildTelemetryEvent","styleText","toError"],"sources":["../src/runners/mcp/run.ts"],"sourcesContent":["import process from 'node:process'\nimport { styleText } from 'node:util'\nimport { toError } from '@internals/utils'\nimport type * as McpModule from '@kubb/mcp'\nimport type { CommandRunner } from 'gunshi'\nimport { buildTelemetryEvent, sendTelemetry } from '../../Telemetry.ts'\nimport { version } from '../../../package.json'\nimport type { definition } from '../../commands/mcp.ts'\n\ntype McpOptions = {\n /**\n * Current `@kubb/cli` version string, used for the telemetry payload.\n */\n version: string\n}\n\n/**\n * Starts the `@kubb/mcp` server over stdio and reports the outcome to telemetry.\n */\nexport async function run({ version }: McpOptions): Promise<void> {\n const { run: startMcpServer } = (await import('@kubb/mcp')) as typeof McpModule\n\n const hrStart = process.hrtime()\n const report = (status: 'success' | 'failed') => sendTelemetry(buildTelemetryEvent({ command: 'mcp', kubbVersion: version, hrStart, status }))\n\n try {\n console.log(styleText('cyan', '⏳ Starting MCP server...'))\n console.warn(styleText('yellow', 'This feature is still under development, use with caution'))\n\n await startMcpServer()\n await report('success')\n } catch (error) {\n await report('failed')\n console.error(toError(error).message)\n process.exitCode = 1\n }\n}\n\n/**\n * Loaded on demand by `index.ts`, so `@kubb/mcp` stays out of the process for every other command.\n */\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async () => {\n await run({ version })\n}\n"],"mappings":";;;;;;;;;;;AAmBA,eAAsB,IAAI,EAAE,WAAsC;CAChE,MAAM,EAAE,KAAK,mBAAoB,MAAM,OAAO;CAE9C,MAAM,UAAUA,aAAAA,QAAQ,OAAO;CAC/B,MAAM,UAAU,WAAiCC,kBAAAA,cAAcC,kBAAAA,oBAAoB;EAAE,SAAS;EAAO,aAAa;EAAS;EAAS;CAAO,CAAC,CAAC;CAE7I,IAAI;EACF,QAAQ,KAAA,GAAIC,UAAAA,UAAAA,CAAU,QAAQ,0BAA0B,CAAC;EACzD,QAAQ,MAAA,GAAKA,UAAAA,UAAAA,CAAU,UAAU,2DAA2D,CAAC;EAE7F,MAAM,eAAe;EACrB,MAAM,OAAO,SAAS;CACxB,SAAS,OAAO;EACd,MAAM,OAAO,QAAQ;EACrB,QAAQ,MAAMC,eAAAA,QAAQ,KAAK,CAAC,CAAC,OAAO;EACpC,aAAA,QAAA,WAAmB;CACrB;AACF;;;;AAKA,MAAa,SAA0E,YAAY;CACjG,MAAM,IAAI,EAAE,SAAA,gBAAA,QAAQ,CAAC;AACvB"}
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-qbf5tadS.cjs");
|
|
2
2
|
const require_errors = require("./errors-C-wEou02.cjs");
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const require_package = require("./package-
|
|
7
|
-
const require_output = require("./output-
|
|
3
|
+
const require_agent = require("./agent-BPqs9VRj.cjs");
|
|
4
|
+
const require_Telemetry = require("./Telemetry-CvHQevSK.cjs");
|
|
5
|
+
const require_utils = require("./utils-CMU1rLfY.cjs");
|
|
6
|
+
const require_package = require("./package-Dp02IIsN.cjs");
|
|
7
|
+
const require_output = require("./output-kmklyYIv.cjs");
|
|
8
8
|
const require_constants = require("./constants-CdxpgX3_.cjs");
|
|
9
9
|
let node_util = require("node:util");
|
|
10
10
|
let node_crypto = require("node:crypto");
|
|
11
11
|
let node_os = require("node:os");
|
|
12
12
|
let node_process = require("node:process");
|
|
13
13
|
node_process = require_rolldown_runtime.__toESM(node_process, 1);
|
|
14
|
+
let node_child_process = require("node:child_process");
|
|
14
15
|
let node_fs_promises = require("node:fs/promises");
|
|
15
16
|
let node_path = require("node:path");
|
|
16
17
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
@@ -18,8 +19,22 @@ let node_fs = require("node:fs");
|
|
|
18
19
|
let _kubb_core = require("@kubb/core");
|
|
19
20
|
let _clack_prompts = require("@clack/prompts");
|
|
20
21
|
_clack_prompts = require_rolldown_runtime.__toESM(_clack_prompts, 1);
|
|
21
|
-
let tinyexec = require("tinyexec");
|
|
22
22
|
let _kubb_studio = require("@kubb/studio");
|
|
23
|
+
//#region ../../internals/utils/src/openInBrowser.ts
|
|
24
|
+
/**
|
|
25
|
+
* Opens a URL or local file in the default browser, unless running in CI.
|
|
26
|
+
*/
|
|
27
|
+
function openInBrowser(target) {
|
|
28
|
+
if (require_agent.isCIEnvironment()) return;
|
|
29
|
+
if (node_process.default.platform === "win32") (0, node_child_process.execFile)("cmd", [
|
|
30
|
+
"/c",
|
|
31
|
+
"start",
|
|
32
|
+
"",
|
|
33
|
+
target
|
|
34
|
+
], () => {});
|
|
35
|
+
else (0, node_child_process.execFile)(node_process.default.platform === "darwin" ? "open" : "xdg-open", [target], () => {});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
23
38
|
//#region ../../internals/utils/src/fs.ts
|
|
24
39
|
/**
|
|
25
40
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
@@ -224,11 +239,11 @@ function resolveToken(options) {
|
|
|
224
239
|
return token;
|
|
225
240
|
}
|
|
226
241
|
function resolveCiIdentity(options) {
|
|
242
|
+
const detected = detectCi();
|
|
227
243
|
if (options.id) return {
|
|
228
244
|
id: options.id,
|
|
229
|
-
name: options.id
|
|
245
|
+
name: detected?.name ?? options.id
|
|
230
246
|
};
|
|
231
|
-
const detected = detectCi();
|
|
232
247
|
if (!detected) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
|
|
233
248
|
return detected;
|
|
234
249
|
}
|
|
@@ -280,7 +295,15 @@ async function snapshot(options) {
|
|
|
280
295
|
const { name, version: packageVersion } = await resolvePackageMetadata(options);
|
|
281
296
|
node_process.default.env.KUBB_AGENT_SECRET = ci.id;
|
|
282
297
|
const spinner = options.json ? null : require_output.createSpinner();
|
|
283
|
-
|
|
298
|
+
const log = (message) => {
|
|
299
|
+
if (options.json) {
|
|
300
|
+
console.error(message);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
spinner?.message(message);
|
|
304
|
+
};
|
|
305
|
+
if (options.json) log("Creating Kubb Studio agent");
|
|
306
|
+
else spinner?.start("Creating Kubb Studio agent");
|
|
284
307
|
const agent = await (0, _kubb_studio.createAgent)({
|
|
285
308
|
studioUrl: options.studioUrl,
|
|
286
309
|
token,
|
|
@@ -297,7 +320,13 @@ async function snapshot(options) {
|
|
|
297
320
|
client: { kind: "ci" },
|
|
298
321
|
loadConfig: async () => (await loadConfigs(options)).config,
|
|
299
322
|
installLogger: (hooks) => {
|
|
300
|
-
hooks.hook("studio:
|
|
323
|
+
hooks.hook("studio:connecting", ({ url }) => log(`Connecting to Kubb Studio at ${url}`));
|
|
324
|
+
hooks.hook("studio:connected", ({ url }) => log(`Connected to Kubb Studio at ${url}`));
|
|
325
|
+
hooks.hook("studio:ready", () => {
|
|
326
|
+
log("Kubb Studio connection ready");
|
|
327
|
+
markReady();
|
|
328
|
+
});
|
|
329
|
+
hooks.hook("studio:warn", ({ message }) => log(`Kubb Studio warning: ${message}`));
|
|
301
330
|
hooks.hook("studio:error", ({ error }) => markFailed(error));
|
|
302
331
|
}
|
|
303
332
|
});
|
|
@@ -311,7 +340,7 @@ async function snapshot(options) {
|
|
|
311
340
|
} finally {
|
|
312
341
|
clearTimeout(readyTimeoutHandle);
|
|
313
342
|
}
|
|
314
|
-
|
|
343
|
+
log("Creating snapshot job");
|
|
315
344
|
const job = await (0, _kubb_studio.createJob)({
|
|
316
345
|
studioUrl: options.studioUrl,
|
|
317
346
|
token,
|
|
@@ -320,6 +349,7 @@ async function snapshot(options) {
|
|
|
320
349
|
name,
|
|
321
350
|
version: packageVersion
|
|
322
351
|
});
|
|
352
|
+
log(`Snapshot job queued: ${job.id}`);
|
|
323
353
|
const finished = await (0, _kubb_studio.waitForJob)({
|
|
324
354
|
studioUrl: options.studioUrl,
|
|
325
355
|
token,
|
|
@@ -329,13 +359,16 @@ async function snapshot(options) {
|
|
|
329
359
|
if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
|
|
330
360
|
if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
|
|
331
361
|
const result = toResult(options.studioUrl, finished.snapshot, agent.slug);
|
|
332
|
-
|
|
362
|
+
if (options.json) log("Snapshot published");
|
|
363
|
+
else spinner?.stop("Snapshot published");
|
|
333
364
|
if (options.json) console.log(JSON.stringify(result));
|
|
334
365
|
else printSummary(result);
|
|
335
366
|
} catch (error) {
|
|
336
|
-
|
|
367
|
+
if (options.json) log("Snapshot failed");
|
|
368
|
+
else spinner?.stop("Snapshot failed");
|
|
337
369
|
throw error;
|
|
338
370
|
} finally {
|
|
371
|
+
if (options.json) log("Disconnecting from Kubb Studio");
|
|
339
372
|
client.disconnect();
|
|
340
373
|
}
|
|
341
374
|
}
|
|
@@ -349,24 +382,6 @@ const ACTIONS = [
|
|
|
349
382
|
"snapshot"
|
|
350
383
|
];
|
|
351
384
|
/**
|
|
352
|
-
* Opens a URL in the user's browser. Best effort: a failure just means the user follows the
|
|
353
|
-
* printed link instead.
|
|
354
|
-
*
|
|
355
|
-
* `start` is a `cmd.exe` builtin, not an executable on PATH, so Windows runs it through `cmd`.
|
|
356
|
-
* The empty string is the window-title argument `start` expects before the URL.
|
|
357
|
-
*/
|
|
358
|
-
async function openInBrowser(url) {
|
|
359
|
-
try {
|
|
360
|
-
if (node_process.default.platform === "win32") await (0, tinyexec.x)("cmd", [
|
|
361
|
-
"/c",
|
|
362
|
-
"start",
|
|
363
|
-
"",
|
|
364
|
-
url
|
|
365
|
-
]);
|
|
366
|
-
else await (0, tinyexec.x)(node_process.default.platform === "darwin" ? "open" : "xdg-open", [url]);
|
|
367
|
-
} catch {}
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
385
|
* Pairs this machine with Studio and stores the resulting token.
|
|
371
386
|
*
|
|
372
387
|
* The CLI holds a code and the browser approves it, rather than the user copying a token out of
|
|
@@ -381,7 +396,7 @@ async function login({ studioUrl, autoOpen }, { signal, previousCredentials } =
|
|
|
381
396
|
signal
|
|
382
397
|
});
|
|
383
398
|
console.log(`\nOpen ${(0, node_util.styleText)("cyan", session.verification_uri)} and approve the code ${(0, node_util.styleText)("bold", session.user_code)}`);
|
|
384
|
-
if (autoOpen)
|
|
399
|
+
if (autoOpen) openInBrowser(session.verification_uri_complete);
|
|
385
400
|
const spinner = require_output.createSpinner();
|
|
386
401
|
spinner.start("Waiting for approval");
|
|
387
402
|
try {
|
|
@@ -458,7 +473,7 @@ async function resolvePermissions(options, credentials, configPath = require_con
|
|
|
458
473
|
granted[key] = options.permission[key] || remembered?.[key] === true;
|
|
459
474
|
continue;
|
|
460
475
|
}
|
|
461
|
-
if (
|
|
476
|
+
if (require_agent.isCIEnvironment() || !require_output.canUseTTY()) {
|
|
462
477
|
granted[key] = false;
|
|
463
478
|
continue;
|
|
464
479
|
}
|
|
@@ -566,7 +581,7 @@ var StudioConnection = class {
|
|
|
566
581
|
agentSlug: ""
|
|
567
582
|
} : stored?.studioUrl === this.#options.studioUrl ? stored : null;
|
|
568
583
|
if (resolved) return resolved;
|
|
569
|
-
if (
|
|
584
|
+
if (require_agent.isCIEnvironment()) throw new Error(`Not paired with ${this.#options.studioUrl}. Set KUBB_AGENT_TOKEN, or run \`kubb studio login\` on a machine with a browser.`);
|
|
570
585
|
return login(this.#options, { signal: this.#shutdown.signal });
|
|
571
586
|
}
|
|
572
587
|
#printBanner() {
|
|
@@ -613,7 +628,7 @@ var StudioConnection = class {
|
|
|
613
628
|
*/
|
|
614
629
|
#assertCanReauthenticate(error) {
|
|
615
630
|
if (this.#hasReauthenticated) throw explainRejectedToken(error, "reauthExhausted");
|
|
616
|
-
if (
|
|
631
|
+
if (require_agent.isCIEnvironment() || !require_output.canUseTTY()) throw explainRejectedToken(error, "nonInteractive");
|
|
617
632
|
}
|
|
618
633
|
/**
|
|
619
634
|
* Studio rejected the token, either before a session ever opened or once one was already live.
|
|
@@ -753,4 +768,4 @@ const runner = async ({ values }) => {
|
|
|
753
768
|
//#endregion
|
|
754
769
|
exports.runner = runner;
|
|
755
770
|
|
|
756
|
-
//# sourceMappingURL=run-
|
|
771
|
+
//# sourceMappingURL=run-BZKhWxUA.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-BZKhWxUA.cjs","names":["isCIEnvironment","process","runtime","access","readFile","rm","process","path","homedir","hash","mkdir","writeFile","readFileSync","process","join","dirname","process","styleText","createSpinner","createAgent","machineTokenFrom","createClient","createJob","waitForJob","startPairing","path","process","hostname","styleText","createSpinner","pollForPairingToken","PairingCanceledError","KUBB_CONFIG_FILENAME","isCIEnvironment","canUseTTY","prompts","getConfigs","runConnection","setupReporters","logLevelMap","cliReporter","createFileStorage","sendTelemetry","buildTelemetryEvent","toError","defaultStudioUrl"],"sources":["../../../internals/utils/src/openInBrowser.ts","../../../internals/utils/src/fs.ts","../src/runners/studio/credentials.ts","../src/runners/studio/ci.ts","../src/runners/studio/snapshot.ts","../src/runners/studio/run.ts"],"sourcesContent":["import { execFile } from 'node:child_process'\nimport process from 'node:process'\nimport { isCIEnvironment } from './isCIEnvironment.ts'\n\n/**\n * Opens a URL or local file in the default browser, unless running in CI.\n */\nexport function openInBrowser(target: string): void {\n if (isCIEnvironment()) return\n\n if (process.platform === 'win32') {\n execFile('cmd', ['/c', 'start', '', target], () => {})\n } else {\n execFile(process.platform === 'darwin' ? 'open' : 'xdg-open', [target], () => {})\n }\n}\n","import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises'\nimport { dirname, isAbsolute, relative, resolve } from 'node:path'\nimport { runtime } from './runtime.ts'\n\n/**\n * Resolves to `true` when the file or directory at `path` exists.\n * Uses `Bun.file().exists()` when running under Bun, `fs.access` otherwise.\n *\n * @example\n * ```ts\n * if (await exists('./kubb.config.ts')) {\n * const content = await read('./kubb.config.ts')\n * }\n * ```\n */\nexport async function exists(path: string): Promise<boolean> {\n if (runtime.isBun) {\n return Bun.file(path).exists()\n }\n return access(path).then(\n () => true,\n () => false,\n )\n}\n\n/**\n * Reads the file at `path` as a UTF-8 string.\n * Uses `Bun.file().text()` when running under Bun, `fs.readFile` otherwise.\n *\n * @example\n * ```ts\n * const source = await read('./src/Pet.ts')\n * ```\n */\nexport async function read(path: string): Promise<string> {\n if (runtime.isBun) {\n return Bun.file(path).text()\n }\n return readFile(path, { encoding: 'utf8' })\n}\n\ntype WriteOptions = {\n /**\n * Previously read content, or `null` when the file does not exist.\n * Omitting this value reads the file before writing.\n */\n stored?: string | null\n /**\n * When `true`, re-reads the file immediately after writing and throws if the\n * content does not match — useful for catching write failures on unreliable file systems.\n */\n sanity?: boolean\n}\n\n/**\n * Whether `stored` already holds `source`, comparing on the trimmed text rather than the exact\n * bytes. Surrounding whitespace is what a formatter adds and what editors strip, and neither is a\n * reason to rewrite the file.\n *\n * Both sides are trimmed, so a storage that keeps bytes verbatim settles on the same answer as one\n * that normalizes what it stores. Trimming only `stored` would leave a source with leading\n * whitespace rewritten on every build, since the stored copy keeps the whitespace the comparison\n * has already dropped.\n */\nexport function matchesStored({ stored, source }: { stored: string; source: string }): boolean {\n return stored.trim() === source.trim()\n}\n\n/**\n * Writes `data` to `path`, trimming surrounding whitespace and ending the file with a single newline\n * the way prettier, biome, and oxfmt all do.\n * Skips the write when the trimmed content is empty, or when the file already holds that content.\n * Creates any missing parent directories automatically.\n * When `sanity` is `true`, re-reads the file after writing and throws if the content does not match.\n *\n * @example\n * ```ts\n * await write('./src/Pet.ts', source) // writes and returns the trimmed content plus a newline\n * await write('./src/Pet.ts', source) // null — file unchanged\n * await write('./src/Pet.ts', ' ') // null — empty content skipped\n * ```\n */\nexport async function write(path: string, data: string, options: WriteOptions = {}): Promise<string | null> {\n const trimmed = data.trim()\n if (trimmed === '') return null\n\n const content = `${trimmed}\\n`\n const resolved = resolve(path)\n let stored = options.stored\n\n if (stored === undefined) {\n if (runtime.isBun) {\n const file = Bun.file(resolved)\n stored = (await file.exists()) ? await file.text() : null\n } else {\n try {\n stored = await readFile(resolved, { encoding: 'utf-8' })\n } catch {\n /* file doesn't exist yet */\n stored = null\n }\n }\n }\n if (matchesStored({ stored: stored ?? '', source: trimmed })) return null\n\n if (runtime.isBun) {\n await Bun.write(resolved, content)\n return content\n }\n\n // Creating the directory up front costs a syscall per file, and every file after the first in a\n // directory pays it for nothing. Write first and only fall back when the directory is missing,\n // which also stays correct when something removed it mid-run.\n try {\n await writeFile(resolved, content, { encoding: 'utf-8' })\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error\n\n await mkdir(dirname(resolved), { recursive: true })\n await writeFile(resolved, content, { encoding: 'utf-8' })\n }\n\n if (options.sanity) {\n const savedData = await readFile(resolved, { encoding: 'utf-8' })\n if (savedData !== content) {\n throw new Error(`Sanity check failed for ${path}\\n\\nData[${data.length}]:\\n${data}\\n\\nSaved[${savedData.length}]:\\n${savedData}\\n`)\n }\n return savedData\n }\n\n return content\n}\n\n/**\n * Recursively removes `path`. Silently succeeds when `path` does not exist.\n *\n * @example\n * ```ts\n * await clean('./dist')\n * ```\n */\nexport async function clean(path: string): Promise<void> {\n return rm(path, { recursive: true, force: true })\n}\n\n/**\n * Converts a filesystem path to use POSIX (`/`) separators.\n *\n * Most of the codebase compares and composes paths as strings (prefix matching, joining for\n * import specifiers, splitting on `/`). On POSIX `path.resolve` already returns `/`-separated\n * paths, but on Windows it returns `\\`-separated paths, which breaks every such comparison.\n *\n * Routing every path that crosses a module boundary through `toPosixPath` keeps the rest of the\n * code platform-agnostic. The conversion runs unconditionally so Windows-specific behavior is\n * exercisable from POSIX CI.\n *\n * @example\n * toPosixPath('C:\\\\repo\\\\src\\\\pet.ts') // 'C:/repo/src/pet.ts'\n */\nexport function toPosixPath(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n\n/**\n * Strips the file extension from a path or file name.\n * Only removes the last `.ext` segment when the dot is not part of a directory name.\n *\n * @example\n * trimExtName('petStore.ts') // 'petStore'\n * trimExtName('/src/models/pet.ts') // '/src/models/pet'\n * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'\n * trimExtName('noExtension') // 'noExtension'\n */\nexport function trimExtName(text: string): string {\n const dotIndex = text.lastIndexOf('.')\n if (dotIndex > 0 && !text.includes('/', dotIndex)) {\n return text.slice(0, dotIndex)\n }\n return text\n}\n\n/**\n * Resolves to `true` when `path` is `parent` itself or nested inside it. Both sides are resolved\n * to absolute paths first, so relative and `..`-containing inputs compare correctly.\n *\n * Guards a destructive or an out-of-tree operation: before wiping an output directory, check that\n * it does not contain the project root, and before loading a path a caller supplied, check that it\n * did not escape the directory it is allowed to read from.\n *\n * @example\n * isPathInside('./src/gen', '.') // true — nested inside the root\n * isPathInside('.', '.') // true — the same directory counts as inside\n * isPathInside('.', './src/gen') // false — the root is not inside its own output\n * isPathInside('../other', '.') // false — escapes the root\n */\nexport function isPathInside(path: string, parent: string): boolean {\n const resolvedPath = resolve(path)\n const resolvedParent = resolve(parent)\n if (resolvedPath === resolvedParent) {\n return true\n }\n\n const rel = relative(resolvedParent, resolvedPath)\n\n return rel !== '' && !rel.startsWith('..') && !isAbsolute(rel)\n}\n","import { hash } from 'node:crypto'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { homedir } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { clean, read } from '@internals/utils'\n\n/**\n * Root for everything the CLI persists between runs: the paired credential, the machine secret,\n * the last Studio config, and the live-session registry. `KUBB_HOME` relocates all of it.\n */\nexport function getKubbHome(): string {\n return process.env.KUBB_HOME ?? path.join(homedir(), '.kubb')\n}\n\n/**\n * Root for the credential and machine identity of the current project.\n *\n * Studio agents are project connections, so two projects on one machine must not share the same\n * persisted token or machine secret.\n */\nexport function getProjectKubbHome(project = process.cwd()): string {\n const projectId = hash('sha256', project)\n return path.join(getKubbHome(), 'projects', projectId)\n}\n\nconst CREDENTIALS_FILENAME = 'credentials.json'\n\n/**\n * Absolute path of the stored credential, so callers can name it in output without rebuilding it.\n */\nexport function getCredentialsPath(): string {\n return path.join(getProjectKubbHome(), CREDENTIALS_FILENAME)\n}\n\nexport type Credentials = {\n /**\n * The Studio instance this machine paired with. A different `--url` means re-pairing.\n */\n studioUrl: string\n /**\n * Bearer token for this machine. Never logged, never printed.\n */\n token: string\n agentId: string\n agentSlug: string\n /**\n * Permissions the user has already granted, keyed by absolute project path, so the CLI asks\n * once per project instead of on every run.\n */\n projects?: Record<string, { allowWrite?: boolean; allowConfigEdit?: boolean; allowInput?: boolean; allowExec?: boolean }>\n}\n\n/**\n * Reads the stored credential, or null when this machine has not paired yet or the file is\n * unreadable or corrupt. A bad file is treated as \"not paired\" so `kubb studio login` can\n * overwrite it rather than the CLI dying on it.\n */\nexport async function readCredentials(): Promise<Credentials | null> {\n const raw = await read(getCredentialsPath()).catch(() => null)\n if (!raw) {\n return null\n }\n\n try {\n return JSON.parse(raw) as Credentials\n } catch {\n return null\n }\n}\n\n/**\n * Writes the credential with owner-only permissions. The file holds a bearer token, so the mode\n * is set at creation time rather than left to the process umask or fixed up afterward, which\n * would leave the token briefly readable under the umask's default mode.\n */\nexport async function writeCredentials(credentials: Credentials): Promise<void> {\n const file = getCredentialsPath()\n\n await mkdir(path.dirname(file), { recursive: true, mode: 0o700 })\n await writeFile(file, JSON.stringify(credentials, null, 2), { mode: 0o600 })\n}\n\n/**\n * Forgets the stored credential. Succeeds when there was nothing to forget.\n */\nexport async function clearCredentials(): Promise<void> {\n await clean(getCredentialsPath())\n}\n","import { readFileSync } from 'node:fs'\nimport process from 'node:process'\n\n/**\n * Stable identity for the CI agent a snapshot run registers or reuses.\n */\nexport type CiContext = {\n /**\n * Fed into the machine token, so the same merge or pull request reuses one agent.\n */\n id: string\n /**\n * Agent display name shown in Studio.\n */\n name: string\n}\n\nfunction readGithubPullRequestNumber(eventPath: string | undefined): string | undefined {\n if (!eventPath) {\n return undefined\n }\n\n try {\n const event = JSON.parse(readFileSync(eventPath, 'utf8')) as { pull_request?: { number?: number } }\n return event.pull_request?.number ? String(event.pull_request.number) : undefined\n } catch {\n return undefined\n }\n}\n\n/**\n * Detects the CI provider from its environment variables and derives a stable identity from it.\n * The GitHub row reproduces `gh:<repositoryId>:<prNumber>`, the identity `kubb-labs/action` has\n * always registered agents under, so an existing repository keeps reusing its agent.\n */\nexport function detectCi(env: Record<string, string | undefined> = process.env): CiContext | null {\n if (env.GITHUB_ACTIONS) {\n const pullRequestNumber = readGithubPullRequestNumber(env.GITHUB_EVENT_PATH)\n const repositoryId = env.GITHUB_REPOSITORY_ID ?? env.GITHUB_REPOSITORY ?? ''\n const scope = pullRequestNumber ?? env.GITHUB_RUN_ID ?? ''\n\n return { id: `gh:${repositoryId}:${scope}`, name: `${env.GITHUB_REPOSITORY ?? 'github'}#${scope}` }\n }\n\n if (env.GITLAB_CI) {\n const scope = env.CI_MERGE_REQUEST_IID ?? env.CI_COMMIT_REF_SLUG ?? ''\n\n return { id: `gl:${env.CI_PROJECT_ID ?? ''}:${scope}`, name: `${env.CI_PROJECT_PATH ?? 'gitlab'}#${scope}` }\n }\n\n if (env.BITBUCKET_BUILD_NUMBER) {\n const scope = env.BITBUCKET_PR_ID ?? env.BITBUCKET_BRANCH ?? ''\n\n return { id: `bb:${env.BITBUCKET_REPO_UUID ?? ''}:${scope}`, name: `${env.BITBUCKET_REPO_FULL_NAME ?? 'bitbucket'}#${scope}` }\n }\n\n if (env.CIRCLECI) {\n // Owner-qualified: two projects with the same name under different CircleCI orgs must not\n // collide on one agent, since registering it purges the other project's live sessions.\n const owner = env.CIRCLE_PROJECT_USERNAME ?? ''\n const project = env.CIRCLE_PROJECT_REPONAME ?? 'circleci'\n const scope = env.CIRCLE_PR_NUMBER ?? env.CIRCLE_BRANCH ?? ''\n\n return { id: `circle:${owner}:${project}:${scope}`, name: `${owner ? `${owner}/` : ''}${project}#${scope}` }\n }\n\n return null\n}\n","import { dirname, join } from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport { exists, read } from '@internals/utils'\nimport { createAgent, createClient, createJob, machineTokenFrom, waitForJob, type StudioSnapshot } from '@kubb/studio'\nimport { createSpinner, logBlock } from '../../loggers/output.ts'\nimport { detectCi } from './ci.ts'\nimport { loadConfigs, type StudioOptions } from './run.ts'\n\n/**\n * How long to wait for Studio's `studio:ready` acknowledgement, above the client's own 10s\n * handshake-ack timeout.\n */\nconst READY_TIMEOUT_MS = 15_000\n\n/**\n * Longest `--timeout` accepted. Job records expire from Studio's storage after an hour.\n */\nconst MAX_TIMEOUT_SECONDS = 3600\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1'])\n\nasync function findPackageJson(startDirectory: string): Promise<{ name: string; version: string }> {\n let directory = startDirectory\n\n for (;;) {\n const file = join(directory, 'package.json')\n\n if (await exists(file)) {\n const packageJson = JSON.parse(await read(file)) as Partial<{ name: string; version: string }>\n\n if (packageJson.name && packageJson.version) {\n return { name: packageJson.name, version: packageJson.version }\n }\n }\n\n const parent = dirname(directory)\n\n if (parent === directory) {\n break\n }\n\n directory = parent\n }\n\n throw new Error(`No package.json with a name and a version found above ${startDirectory}`)\n}\n\n/** Reads package.json only for whichever of name/version --name and --version did not supply. */\nasync function resolvePackageMetadata(options: StudioOptions): Promise<{ name: string; version: string }> {\n if (options.name && options.packageVersion) {\n return { name: options.name, version: options.packageVersion }\n }\n\n const packageMetadata = await findPackageJson(process.cwd())\n\n return { name: options.name ?? packageMetadata.name, version: options.packageVersion ?? packageMetadata.version }\n}\n\nfunction resolveToken(options: StudioOptions): string {\n const token = options.token ?? process.env.KUBB_TOKEN\n\n if (!token) {\n throw new Error('An organization CI API key is required. Pass --token or set KUBB_TOKEN.')\n }\n\n return token\n}\n\nfunction resolveCiIdentity(options: StudioOptions): { id: string; name: string } {\n const detected = detectCi()\n\n if (options.id) {\n return { id: options.id, name: detected?.name ?? options.id }\n }\n\n if (!detected) {\n throw new Error('Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.')\n }\n\n return detected\n}\n\n/** Rejects a bad `--timeout` up front instead of letting it reach `waitForJob` as NaN or <= 0. */\nfunction resolveTimeoutMs(options: StudioOptions): number {\n const seconds = options.timeout ?? 600\n\n if (!Number.isFinite(seconds) || seconds <= 0) {\n throw new Error('--timeout must be a positive number of seconds')\n }\n\n return Math.min(seconds, MAX_TIMEOUT_SECONDS) * 1000\n}\n\n/** The CI key is a credential, so refuse to send it anywhere but HTTPS or a loopback dev host. */\nfunction assertSecureStudioUrl(studioUrl: string): void {\n const url = new URL(studioUrl)\n\n if (url.protocol !== 'https:' && !LOOPBACK_HOSTS.has(url.hostname)) {\n throw new Error(`Refusing to send the CI API key to ${studioUrl}. Use an https:// Studio URL, or a loopback host for local development.`)\n }\n}\n\nfunction absoluteUrl(studioUrl: string, path: string): string {\n return new URL(path, `${studioUrl}/`).toString()\n}\n\ntype SnapshotResult = {\n id: string\n name: string | null\n version: string | null\n integrity: string | null\n url: string\n snapshotIdUrl: string\n expiresAt: string\n agentUrl: string\n}\n\nfunction toResult(studioUrl: string, snapshot: StudioSnapshot, agentSlug: string): SnapshotResult {\n return {\n id: snapshot.id,\n name: snapshot.name,\n version: snapshot.version,\n integrity: snapshot.integrity,\n url: absoluteUrl(studioUrl, snapshot.url),\n snapshotIdUrl: absoluteUrl(studioUrl, snapshot.snapshotIdUrl),\n expiresAt: snapshot.expiresAt,\n agentUrl: absoluteUrl(studioUrl, `/agents/${agentSlug}`),\n }\n}\n\nfunction printSummary(result: SnapshotResult): void {\n logBlock([\n `${styleText('dim', 'Package'.padEnd(10))} ${result.name ?? '(unnamed)'}@${result.version ?? '0.0.0'}`,\n `${styleText('dim', 'Tarball'.padEnd(10))} ${styleText('cyan', result.url)}`,\n `${styleText('dim', 'Agent'.padEnd(10))} ${result.agentUrl}`,\n `${styleText('dim', 'Expires'.padEnd(10))} ${result.expiresAt}`,\n ])\n}\n\n/**\n * Generates a Kubb Studio snapshot from a script: registers or reuses a CI agent, connects it,\n * queues a snapshot job, and polls until the tarball is ready. A snapshot job runs generation and\n * packs the tarball in one step, so this needs no separate generation run.\n */\nexport async function snapshot(options: StudioOptions): Promise<void> {\n const timeoutMs = resolveTimeoutMs(options)\n const token = resolveToken(options)\n assertSecureStudioUrl(options.studioUrl)\n const { configPath } = await loadConfigs(options)\n const ci = resolveCiIdentity(options)\n const { name, version: packageVersion } = await resolvePackageMetadata(options)\n\n // Set before the first `getMachineToken()` call (inside `client.connect()`), so the WebSocket\n // session registers under the same machine token `createAgent` just registered with Studio.\n process.env.KUBB_AGENT_SECRET = ci.id\n\n const spinner = options.json ? null : createSpinner()\n const log = (message: string) => {\n if (options.json) {\n console.error(message)\n return\n }\n\n spinner?.message(message)\n }\n\n if (options.json) {\n log('Creating Kubb Studio agent')\n } else {\n spinner?.start('Creating Kubb Studio agent')\n }\n\n const agent = await createAgent({ studioUrl: options.studioUrl, token, name: ci.name, machineToken: machineTokenFrom(ci.id) })\n\n const { promise: ready, reject: markFailed, resolve: markReady } = Promise.withResolvers<void>()\n\n const client = createClient({\n studioUrl: options.studioUrl,\n token: agent.token,\n configPath,\n root: process.cwd(),\n version: options.version,\n client: { kind: 'ci' },\n loadConfig: async () => (await loadConfigs(options)).config,\n installLogger: (hooks) => {\n hooks.hook('studio:connecting', ({ url }) => log(`Connecting to Kubb Studio at ${url}`))\n hooks.hook('studio:connected', ({ url }) => log(`Connected to Kubb Studio at ${url}`))\n hooks.hook('studio:ready', () => {\n log('Kubb Studio connection ready')\n markReady()\n })\n hooks.hook('studio:warn', ({ message }) => log(`Kubb Studio warning: ${message}`))\n hooks.hook('studio:error', ({ error }) => markFailed(error))\n },\n })\n\n await client.connect()\n\n try {\n let readyTimeoutHandle: ReturnType<typeof setTimeout> | undefined\n\n try {\n await Promise.race([\n ready,\n new Promise<void>((_, reject) => {\n readyTimeoutHandle = setTimeout(() => reject(new Error('Timed out waiting for Kubb Studio to confirm the agent was ready')), READY_TIMEOUT_MS)\n }),\n ])\n } finally {\n clearTimeout(readyTimeoutHandle)\n }\n\n log('Creating snapshot job')\n\n const job = await createJob({ studioUrl: options.studioUrl, token, type: 'snapshot', agentId: agent.id, name, version: packageVersion })\n log(`Snapshot job queued: ${job.id}`)\n const finished = await waitForJob({ studioUrl: options.studioUrl, token, id: job.id, timeoutMs })\n\n if (finished.status === 'failed') {\n throw new Error(finished.error ?? 'Snapshot job failed')\n }\n\n if (!finished.snapshot) {\n throw new Error('Snapshot job succeeded without a snapshot')\n }\n\n const result = toResult(options.studioUrl, finished.snapshot, agent.slug)\n\n if (options.json) {\n log('Snapshot published')\n } else {\n spinner?.stop('Snapshot published')\n }\n\n if (options.json) {\n console.log(JSON.stringify(result))\n } else {\n printSummary(result)\n }\n } catch (error) {\n if (options.json) {\n log('Snapshot failed')\n } else {\n spinner?.stop('Snapshot failed')\n }\n throw error\n } finally {\n if (options.json) {\n log('Disconnecting from Kubb Studio')\n }\n client.disconnect()\n }\n}\n","import { hostname } from 'node:os'\nimport path from 'node:path'\nimport process from 'node:process'\nimport { styleText } from 'node:util'\nimport * as prompts from '@clack/prompts'\nimport { KUBB_CONFIG_FILENAME } from '@internals/shared'\nimport { isCIEnvironment, openInBrowser, toError } from '@internals/utils'\nimport type { CLIOptions, Config } from '@kubb/core'\nimport { cliReporter, logLevel as logLevelMap } from '@kubb/core'\nimport {\n createFileStorage,\n type ClientOptions,\n defaultStudioUrl,\n type InvalidAgentTokenError,\n PairingCanceledError,\n pollForPairingToken,\n runConnection,\n setStorage,\n startPairing,\n} from '@kubb/studio'\nimport type { CommandRunner } from 'gunshi'\nimport { buildTelemetryEvent, sendTelemetry } from '../../Telemetry.ts'\nimport { version } from '../../../package.json'\nimport type { definition } from '../../commands/studio.ts'\nimport setupReporters from '../../loggers/utils.ts'\nimport { createSpinner, logBlock, logIntro, logOutro } from '../../loggers/output.ts'\nimport { canUseTTY } from '../../utils/env.ts'\nimport { getConfigs } from '../generate/utils.ts'\nimport { clearCredentials, type Credentials, getCredentialsPath, getProjectKubbHome, readCredentials, writeCredentials } from './credentials.ts'\nimport { snapshot } from './snapshot.ts'\n\nconst ACTIONS = ['connect', 'login', 'logout', 'status', 'snapshot'] as const\n\nexport type StudioAction = (typeof ACTIONS)[number]\n\ntype Permission = 'allowWrite' | 'allowConfigEdit' | 'allowInput' | 'allowExec'\n\nexport type StudioOptions = {\n action: StudioAction\n /**\n * Current `@kubb/cli` version, reported to Studio and used for the telemetry payload.\n */\n version: string\n configPath?: string\n /**\n * Base URL of the Studio instance, for a self-hosted deployment. Resolved before it reaches here,\n * since stored credentials are bound to it.\n */\n studioUrl: string\n /**\n * What Studio may do in this project. Each flag grants outright; the rest are asked once per\n * project through {@link resolvePermissions}.\n */\n permission: Record<Permission, boolean>\n /**\n * Whether to open the approval page in a browser during pairing.\n */\n autoOpen: boolean\n logLevel?: CLIOptions['logLevel']\n /**\n * `snapshot` only: organization CI API key. Falls back to `KUBB_TOKEN`.\n */\n token?: string\n /**\n * `snapshot` only: stable identity for the CI agent. Falls back to CI auto-detection.\n */\n id?: string\n /**\n * `snapshot` only: package name for the generated tarball. Falls back to the nearest package.json.\n */\n name?: string\n /**\n * `snapshot` only: package version for the generated tarball. Falls back to the nearest package.json.\n */\n packageVersion?: string\n /**\n * `snapshot` only: seconds to wait for the job to finish.\n */\n timeout?: number\n /**\n * `snapshot` only: print the result as one JSON object instead of a summary.\n */\n json?: boolean\n}\n\ntype LoginOptions = {\n /**\n * Aborting this cancels an in-flight pairing request or poll and rejects with\n * {@link PairingCanceledError}. Wired to `kubb studio`'s shutdown signal, so Ctrl+C during\n * pairing cancels it instead of leaving the poll running.\n */\n signal?: AbortSignal\n /**\n * Credentials from before this login. When the fresh pairing resolves to the same agent on the\n * same Studio, its saved project permissions carry forward instead of being asked again.\n * Left out for an explicitly requested `kubb studio login`, which always starts clean.\n */\n previousCredentials?: Credentials | null\n}\n\n/**\n * Pairs this machine with Studio and stores the resulting token.\n *\n * The CLI holds a code and the browser approves it, rather than the user copying a token out of\n * the UI. The token comes back over the CLI's own HTTPS POST, so it never lands in a URL, a\n * server log, or a `Referer` header.\n */\nasync function login({ studioUrl, autoOpen }: StudioOptions, { signal, previousCredentials }: LoginOptions = {}): Promise<Credentials> {\n const session = await startPairing({ studioUrl, name: path.basename(process.cwd()), hostname: hostname(), signal })\n\n console.log(`\\nOpen ${styleText('cyan', session.verification_uri)} and approve the code ${styleText('bold', session.user_code)}`)\n\n if (autoOpen) {\n openInBrowser(session.verification_uri_complete)\n }\n\n const spinner = createSpinner()\n spinner.start('Waiting for approval')\n\n try {\n const { token, agent } = await pollForPairingToken({ studioUrl, session, signal })\n spinner.stop(`Paired as ${agent.name}`)\n\n const keepsIdentity = previousCredentials?.studioUrl === studioUrl && previousCredentials.agentId === agent.id\n const credentials: Credentials = {\n studioUrl,\n token,\n agentId: agent.id,\n agentSlug: agent.slug,\n ...(keepsIdentity && previousCredentials?.projects ? { projects: previousCredentials.projects } : {}),\n }\n await writeCredentials(credentials)\n\n console.log(`Credentials stored in ${getCredentialsPath()}`)\n\n return credentials\n } catch (error) {\n spinner.stop(error instanceof PairingCanceledError ? 'Pairing canceled' : 'Pairing failed')\n throw error\n }\n}\n\n/**\n * What each permission is asked as, in the order the questions appear.\n */\nconst PERMISSIONS: ReadonlyArray<{\n key: Permission\n /**\n * Short label for status output and the connect summary.\n */\n label: string\n question: (project: string, configPath: string) => string\n}> = [\n { key: 'allowWrite', label: 'write generated files', question: (project) => `Let Kubb Studio write generated files into ${project}?` },\n {\n key: 'allowConfigEdit',\n label: 'edit kubb.config.ts',\n question: (_project, configPath) => `Let Kubb Studio change plugin options in ${configPath}?`,\n },\n {\n key: 'allowInput',\n label: 'use a Studio spec',\n question: () => 'Let Kubb Studio generate from an OpenAPI spec it sends, instead of the one on disk?',\n },\n {\n key: 'allowExec',\n label: 'run formatter, linter, postGenerate',\n question: () => 'Let Kubb Studio run the formatter, the linter, and output.postGenerate?',\n },\n]\n\n/**\n * One row per permission, for the connect banner and `kubb studio status`. A list rather than a\n * joined line: four labels this long read as one run-on sentence side by side.\n */\nexport function formatPermissionRows(granted: Record<Permission, boolean>): Array<string> {\n return PERMISSIONS.map(({ key, label }) => `${granted[key] ? styleText('green', '✔') : styleText('red', '✘')} ${label}`)\n}\n\n/**\n * Resolves Studio permissions for the current project.\n * Flags win, saved answers are reused, and new answers are stored unless `persist` is false.\n */\nexport async function resolvePermissions(\n options: StudioOptions,\n credentials: Credentials,\n configPath: string = KUBB_CONFIG_FILENAME,\n /**\n * Stores new answers in `~/.kubb/credentials.json`.\n * Pass `false` for `KUBB_AGENT_TOKEN` sessions so a temporary token is not written back to disk.\n */\n persist = true,\n): Promise<Record<Permission, boolean>> {\n const project = process.cwd()\n const remembered = credentials.projects?.[project]\n const granted: Record<Permission, boolean> = { allowWrite: false, allowConfigEdit: false, allowInput: false, allowExec: false }\n const answers: Partial<Record<Permission, boolean>> = {}\n\n for (const { key, question } of PERMISSIONS) {\n if (options.permission[key] || typeof remembered?.[key] === 'boolean') {\n granted[key] = options.permission[key] || remembered?.[key] === true\n continue\n }\n\n if (isCIEnvironment() || !canUseTTY()) {\n granted[key] = false\n continue\n }\n\n granted[key] = (await prompts.confirm({ message: question(project, configPath), initialValue: false })) === true\n answers[key] = granted[key]\n }\n\n if (persist && Object.keys(answers).length) {\n await writeCredentials({\n ...credentials,\n projects: { ...credentials.projects, [project]: { ...remembered, ...answers } },\n })\n }\n\n return granted\n}\n\n/**\n * Loads the project's Kubb config the same way `kubb generate` does.\n * Returns the first config.\n */\nexport async function loadConfigs(options: StudioOptions): Promise<{ configPath: string; config: Config }> {\n const { configPath, configs } = await getConfigs({ configPath: options.configPath, logLevel: options.logLevel })\n const [config] = configs\n\n if (!config) {\n throw new Error('Config not defined, create a kubb.config.ts or pass it with --config')\n }\n\n // `getConfigs` resolves this to an absolute path. Relativized here, once, so the permission\n // prompt and the path Studio receives over the wire both show the project-relative form instead\n // of leaking the local filesystem layout.\n return { configPath: path.relative(process.cwd(), configPath) || configPath, config }\n}\n\n/**\n * Why a rejected token could not be recovered automatically: it came from `KUBB_AGENT_TOKEN`, no\n * browser is available to re-pair with, or the token this run already re-paired for was rejected\n * again.\n */\ntype RejectedTokenReason = 'envToken' | 'nonInteractive' | 'reauthExhausted'\n\n/**\n * Explains a rejected token to the operator. Builds the error and nothing else, so each caller\n * decides what happens to the credentials.\n */\nfunction explainRejectedToken(error: InvalidAgentTokenError, reason: RejectedTokenReason): Error {\n if (reason === 'envToken') {\n return new Error(`${error.message} Pair again and update KUBB_AGENT_TOKEN.`)\n }\n\n if (reason === 'reauthExhausted') {\n return new Error(`${error.message} Studio rejected the newly approved token too. Run \\`kubb studio login\\` and try again.`)\n }\n\n return new Error(`${error.message} Run \\`kubb studio login\\` to pair again.`)\n}\n\n/**\n * One `kubb studio` connect run: pairing if needed, connecting, and pairing again whenever Studio\n * rejects the token. Reached through `connect()`, which is what the command runs.\n */\nclass StudioConnection {\n readonly #options: StudioOptions\n readonly #configPath: string\n readonly #shutdown = new AbortController()\n readonly #requestShutdown = (): void => this.#shutdown.abort()\n // `bun-types` narrows `process.on`/`process.off` to its own event union, which omits Node's\n // process signal events, so the listener is installed and removed through the plain emitter API.\n readonly #processEvents = process as unknown as NodeJS.EventEmitter\n\n // Known once `run()` resolves the initial credentials, before anything else reads this field.\n #credentials!: Credentials\n // Whether the \"Press Ctrl+C\" hint already printed, so a reconnect never repeats it.\n #hinted = false\n // One automatic re-pair per run, whether the rejection lands at startup or once the session is\n // live. A token rejected right after a fresh login is a hard failure, not a reason to keep\n // pairing.\n #hasReauthenticated = false\n // Resolved by `run()` from the flags and the project's saved answers, before anything reads it.\n #granted!: Record<Permission, boolean>\n\n constructor(options: StudioOptions, configPath: string) {\n this.#options = options\n this.#configPath = configPath\n }\n\n #reportDisconnected(): void {\n if (this.#options.logLevel === 'silent') {\n return\n }\n\n logOutro('Disconnected')\n }\n\n /**\n * Connects and streams generation events until the process is stopped or Studio rejects the\n * token.\n *\n * One `AbortController` covers the whole run: it cancels an in-flight pairing poll on Ctrl+C and\n * ends the wait in `#connectAndWait`. Its signal listeners are armed once here, so a retried\n * pairing cannot leave a duplicate behind.\n */\n async run(): Promise<void> {\n this.#processEvents.once('SIGINT', this.#requestShutdown)\n this.#processEvents.once('SIGTERM', this.#requestShutdown)\n\n try {\n this.#credentials = await this.#resolveInitialCredentials()\n\n this.#granted = await resolvePermissions(this.#options, this.#credentials, this.#configPath, !process.env.KUBB_AGENT_TOKEN)\n\n this.#printBanner()\n\n const outcome = await runConnection({\n credentials: this.#credentials,\n signal: this.#shutdown.signal,\n clientOptions: () => this.#clientOptions(),\n onTokenRejected: ({ error, live }) => this.#handleRejection(error, live),\n })\n\n if (outcome === 'shutdown') {\n this.#reportDisconnected()\n }\n } catch (error) {\n // Canceling the very first pairing (before anything was ever connected) is Ctrl+C working as\n // intended, not a failure to report.\n if (error instanceof PairingCanceledError) {\n return\n }\n\n throw error\n } finally {\n this.#processEvents.off('SIGINT', this.#requestShutdown)\n this.#processEvents.off('SIGTERM', this.#requestShutdown)\n }\n }\n\n async #resolveInitialCredentials(): Promise<Credentials> {\n const envToken = process.env.KUBB_AGENT_TOKEN\n const stored = envToken ? null : await readCredentials()\n\n // A credential is only reused for the Studio it was issued by, so switching `--url` re-pairs\n // instead of sending one instance's token to another.\n const resolved = envToken\n ? { studioUrl: this.#options.studioUrl, token: envToken, agentId: '', agentSlug: '' }\n : stored?.studioUrl === this.#options.studioUrl\n ? stored\n : null\n\n if (resolved) {\n return resolved\n }\n\n if (isCIEnvironment()) {\n throw new Error(`Not paired with ${this.#options.studioUrl}. Set KUBB_AGENT_TOKEN, or run \\`kubb studio login\\` on a machine with a browser.`)\n }\n\n return login(this.#options, { signal: this.#shutdown.signal })\n }\n\n #printBanner(): void {\n if (this.#options.logLevel === 'silent') {\n return\n }\n\n const detail = (label: string, value: string) => `${styleText('dim', label.padEnd(7))} ${value}`\n\n logBlock([\n detail('Studio', styleText('cyan', this.#options.studioUrl)),\n detail('Project', path.basename(process.cwd())),\n detail('Config', path.relative(process.cwd(), this.#configPath) || this.#configPath),\n '',\n styleText('dim', 'Permissions'),\n ...formatPermissionRows(this.#granted),\n ])\n }\n\n /**\n * What every connection attempt opens with. Rebuilt per attempt, so a re-pair that changed the\n * granted permissions takes effect on the next one.\n */\n #clientOptions(): Omit<ClientOptions, 'token' | 'onAuthRequired'> {\n return {\n studioUrl: this.#options.studioUrl,\n configPath: this.#configPath,\n version: this.#options.version,\n // Reloaded on every generate, so an edit to kubb.config.ts is picked up without reconnecting.\n loadConfig: async () => (await loadConfigs(this.#options)).config,\n client: { kind: 'cli' },\n root: process.cwd(),\n permissions: this.#granted,\n // The loggers `kubb generate` installs, so one place renders the session events and the\n // generations it drives.\n installLogger: async (hooks) => {\n await setupReporters(hooks, { logLevel: logLevelMap[this.#options.logLevel ?? 'info'], reporters: [cliReporter] })\n\n // `client.connect()` resolves once the agent is registered, not once a session is open, so\n // this is the only point that knows the connection is live. Registered after the loggers so\n // it lands under their \"Connected to ...\" line, and once, since every reconnect fires again.\n hooks.hook('studio:connected', () => {\n if (this.#hinted || this.#options.logLevel === 'silent') {\n return\n }\n this.#hinted = true\n\n logBlock(styleText('dim', 'Press Ctrl+C to disconnect'))\n })\n },\n }\n }\n\n /**\n * Throws when a rejected token cannot be replaced by pairing again: this run already paired once\n * and was rejected anyway, or there is no browser to approve a new pairing.\n */\n #assertCanReauthenticate(error: InvalidAgentTokenError): void {\n if (this.#hasReauthenticated) {\n throw explainRejectedToken(error, 'reauthExhausted')\n }\n\n if (isCIEnvironment() || !canUseTTY()) {\n throw explainRejectedToken(error, 'nonInteractive')\n }\n }\n\n /**\n * Studio rejected the token, either before a session ever opened or once one was already live.\n * Keeping a rejected token only produces 401s on every run, so it is forgotten and paired again.\n * Returns the credential to reconnect with, or null when the run is over.\n */\n async #handleRejection(error: InvalidAgentTokenError, live: boolean): Promise<Credentials | null> {\n // An operator-supplied token is never replaced automatically.\n if (process.env.KUBB_AGENT_TOKEN) {\n throw explainRejectedToken(error, 'envToken')\n }\n\n // A token dead at startup is forgotten either way. A live one is only forgotten once this run\n // knows it can pair again, so a CI run keeps the credential it could not replace.\n if (!live) {\n await clearCredentials()\n }\n\n this.#assertCanReauthenticate(error)\n\n console.log(styleText('yellow', live ? `${error.message} Studio needs you to approve access again.` : `${error.message} Pairing again...`))\n\n if (live) {\n await clearCredentials()\n }\n\n const credentials = await this.#reauthenticate()\n\n // Nothing was ever connected at startup, so there is no session to report the end of.\n if (!credentials && live) {\n this.#reportDisconnected()\n }\n\n return credentials\n }\n\n /**\n * Re-pairs and stores the resulting credentials. Returns null when the operator canceled it.\n */\n async #reauthenticate(): Promise<Credentials | null> {\n try {\n this.#credentials = await login(this.#options, { signal: this.#shutdown.signal, previousCredentials: this.#credentials })\n } catch (loginError) {\n if (loginError instanceof PairingCanceledError) {\n return null\n }\n throw loginError\n }\n\n this.#hasReauthenticated = true\n\n // The approved agent may be a different identity, whose saved permissions did not carry\n // forward. Resolving again asks for whatever this credential does not already hold, instead\n // of handing the new agent what the previous one was granted.\n this.#granted = await resolvePermissions(this.#options, this.#credentials, this.#configPath, !process.env.KUBB_AGENT_TOKEN)\n\n return this.#credentials\n }\n}\n\n/**\n * Connects this project to Studio and streams generation events until the process is stopped or\n * Studio rejects the token.\n */\nexport async function connect(options: StudioOptions): Promise<void> {\n // Resolved before any network call to Studio (pairing included), so a project with no config\n // fails fast instead of starting a device-authorization flow it can never use.\n const { configPath } = await loadConfigs(options)\n\n await new StudioConnection(options, configPath).run()\n}\n\n/**\n * Reports the paired agent and any saved permissions for the current project.\n */\nasync function status(options: StudioOptions): Promise<void> {\n const credentials = await readCredentials()\n\n if (!credentials) {\n console.log('Not paired. Run `kubb studio login`.')\n\n return\n }\n\n console.log(`Paired with ${credentials.studioUrl} as ${styleText('cyan', credentials.agentSlug || credentials.agentId)}`)\n\n if (credentials.studioUrl !== options.studioUrl) {\n console.log(styleText('yellow', `Connecting to ${options.studioUrl} needs pairing again.`))\n }\n\n const remembered = credentials.projects?.[process.cwd()]\n\n if (!remembered) {\n console.log(styleText('dim', 'No saved permissions for this project. Run `kubb studio` to connect and choose.'))\n\n return\n }\n\n console.log(styleText('dim', 'Saved permissions'))\n\n for (const row of formatPermissionRows({\n allowWrite: remembered.allowWrite === true,\n allowConfigEdit: remembered.allowConfigEdit === true,\n allowInput: remembered.allowInput === true,\n allowExec: remembered.allowExec === true,\n })) {\n console.log(row)\n }\n}\n\n/**\n * Runs a `kubb studio` action and reports the outcome to telemetry.\n */\nasync function run(options: StudioOptions): Promise<void> {\n // The machine secret lives here and pairing binds it, so storage is installed before anything\n // reads `getMachineToken()`, which `startPairing` does, before any client exists.\n setStorage(createFileStorage(getProjectKubbHome()))\n\n const hrStart = process.hrtime()\n const report = (status: 'success' | 'failed') => sendTelemetry(buildTelemetryEvent({ command: 'studio', kubbVersion: options.version, hrStart, status }))\n\n try {\n // `snapshot --json` prints exactly one JSON object on stdout, so nothing else may write there.\n if (options.logLevel !== 'silent' && !(options.action === 'snapshot' && options.json)) {\n logIntro({\n title: `Kubb Studio ${styleText('dim', `v${options.version}`)}`,\n warning: styleText('yellow', 'This feature is still under development, use with caution'),\n // Only `connect` stays open long enough to close the block again with `logOutro`.\n block: options.action === 'connect',\n })\n }\n\n switch (options.action) {\n case 'login':\n await login(options)\n break\n case 'logout':\n await clearCredentials()\n console.log('Signed out of Kubb Studio.')\n break\n case 'status':\n await status(options)\n break\n case 'connect':\n await connect(options)\n break\n case 'snapshot':\n await snapshot(options)\n break\n default:\n throw new Error(`Unknown action \"${options.action}\", expected one of ${ACTIONS.join(', ')}`)\n }\n\n await report('success')\n } catch (error) {\n await report('failed')\n console.error(toError(error).message)\n process.exitCode = 1\n }\n}\n\n/**\n * Maps the parsed `kubb studio` flags onto {@link run}. Loaded on demand by `index.ts`, so the\n * Studio client stays out of the process for every other command.\n */\nexport const runner: CommandRunner<{ args: typeof definition.args; extensions: {} }> = async ({ values }) => {\n await run({\n action: (values.action ?? 'connect') as StudioAction,\n version,\n configPath: values.config,\n studioUrl: values.url ?? defaultStudioUrl,\n permission: {\n allowWrite: values.allowWrite,\n allowConfigEdit: values.allowConfigEdit,\n allowInput: values.allowInput,\n allowExec: values.allowExec,\n },\n autoOpen: values.open,\n logLevel: values.logLevel,\n token: values.token,\n id: values.id,\n name: values.name,\n packageVersion: values.packageVersion,\n timeout: values.timeout,\n json: values.json,\n })\n}\n"],"mappings":"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"}
|
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
import "./rolldown-runtime-C0LytTxp.js";
|
|
2
2
|
import { t as toError } from "./errors-C8yNfV7w.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { t as version } from "./package-
|
|
7
|
-
import { a as logIntro, n as logBlock, o as logOutro, t as createSpinner } from "./output-
|
|
3
|
+
import { n as isCIEnvironment } from "./agent-3JmkMghp.js";
|
|
4
|
+
import { r as sendTelemetry, t as buildTelemetryEvent, u as runtime } from "./Telemetry-CJcQkQOT.js";
|
|
5
|
+
import { l as setupReporters, n as getConfigs } from "./utils-s6yetN6W.js";
|
|
6
|
+
import { t as version } from "./package-BMrVXY1Q.js";
|
|
7
|
+
import { a as logIntro, l as canUseTTY, n as logBlock, o as logOutro, t as createSpinner } from "./output-DH2Uoy4x.js";
|
|
8
8
|
import { t as KUBB_CONFIG_FILENAME } from "./constants-DHRspBTo.js";
|
|
9
9
|
import { styleText } from "node:util";
|
|
10
10
|
import { hash } from "node:crypto";
|
|
11
11
|
import { homedir, hostname } from "node:os";
|
|
12
12
|
import process from "node:process";
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
13
14
|
import { access, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
14
15
|
import path, { dirname, join } from "node:path";
|
|
15
16
|
import { readFileSync } from "node:fs";
|
|
16
17
|
import { cliReporter, logLevel } from "@kubb/core";
|
|
17
18
|
import * as prompts from "@clack/prompts";
|
|
18
|
-
import { x } from "tinyexec";
|
|
19
19
|
import { PairingCanceledError, createAgent, createClient, createFileStorage, createJob, defaultStudioUrl, machineTokenFrom, pollForPairingToken, runConnection, setStorage, startPairing, waitForJob } from "@kubb/studio";
|
|
20
|
+
//#region ../../internals/utils/src/openInBrowser.ts
|
|
21
|
+
/**
|
|
22
|
+
* Opens a URL or local file in the default browser, unless running in CI.
|
|
23
|
+
*/
|
|
24
|
+
function openInBrowser(target) {
|
|
25
|
+
if (isCIEnvironment()) return;
|
|
26
|
+
if (process.platform === "win32") execFile("cmd", [
|
|
27
|
+
"/c",
|
|
28
|
+
"start",
|
|
29
|
+
"",
|
|
30
|
+
target
|
|
31
|
+
], () => {});
|
|
32
|
+
else execFile(process.platform === "darwin" ? "open" : "xdg-open", [target], () => {});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
20
35
|
//#region ../../internals/utils/src/fs.ts
|
|
21
36
|
/**
|
|
22
37
|
* Resolves to `true` when the file or directory at `path` exists.
|
|
@@ -221,11 +236,11 @@ function resolveToken(options) {
|
|
|
221
236
|
return token;
|
|
222
237
|
}
|
|
223
238
|
function resolveCiIdentity(options) {
|
|
239
|
+
const detected = detectCi();
|
|
224
240
|
if (options.id) return {
|
|
225
241
|
id: options.id,
|
|
226
|
-
name: options.id
|
|
242
|
+
name: detected?.name ?? options.id
|
|
227
243
|
};
|
|
228
|
-
const detected = detectCi();
|
|
229
244
|
if (!detected) throw new Error("Could not detect a supported CI provider (GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI). Pass --id.");
|
|
230
245
|
return detected;
|
|
231
246
|
}
|
|
@@ -277,7 +292,15 @@ async function snapshot(options) {
|
|
|
277
292
|
const { name, version: packageVersion } = await resolvePackageMetadata(options);
|
|
278
293
|
process.env.KUBB_AGENT_SECRET = ci.id;
|
|
279
294
|
const spinner = options.json ? null : createSpinner();
|
|
280
|
-
|
|
295
|
+
const log = (message) => {
|
|
296
|
+
if (options.json) {
|
|
297
|
+
console.error(message);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
spinner?.message(message);
|
|
301
|
+
};
|
|
302
|
+
if (options.json) log("Creating Kubb Studio agent");
|
|
303
|
+
else spinner?.start("Creating Kubb Studio agent");
|
|
281
304
|
const agent = await createAgent({
|
|
282
305
|
studioUrl: options.studioUrl,
|
|
283
306
|
token,
|
|
@@ -294,7 +317,13 @@ async function snapshot(options) {
|
|
|
294
317
|
client: { kind: "ci" },
|
|
295
318
|
loadConfig: async () => (await loadConfigs(options)).config,
|
|
296
319
|
installLogger: (hooks) => {
|
|
297
|
-
hooks.hook("studio:
|
|
320
|
+
hooks.hook("studio:connecting", ({ url }) => log(`Connecting to Kubb Studio at ${url}`));
|
|
321
|
+
hooks.hook("studio:connected", ({ url }) => log(`Connected to Kubb Studio at ${url}`));
|
|
322
|
+
hooks.hook("studio:ready", () => {
|
|
323
|
+
log("Kubb Studio connection ready");
|
|
324
|
+
markReady();
|
|
325
|
+
});
|
|
326
|
+
hooks.hook("studio:warn", ({ message }) => log(`Kubb Studio warning: ${message}`));
|
|
298
327
|
hooks.hook("studio:error", ({ error }) => markFailed(error));
|
|
299
328
|
}
|
|
300
329
|
});
|
|
@@ -308,7 +337,7 @@ async function snapshot(options) {
|
|
|
308
337
|
} finally {
|
|
309
338
|
clearTimeout(readyTimeoutHandle);
|
|
310
339
|
}
|
|
311
|
-
|
|
340
|
+
log("Creating snapshot job");
|
|
312
341
|
const job = await createJob({
|
|
313
342
|
studioUrl: options.studioUrl,
|
|
314
343
|
token,
|
|
@@ -317,6 +346,7 @@ async function snapshot(options) {
|
|
|
317
346
|
name,
|
|
318
347
|
version: packageVersion
|
|
319
348
|
});
|
|
349
|
+
log(`Snapshot job queued: ${job.id}`);
|
|
320
350
|
const finished = await waitForJob({
|
|
321
351
|
studioUrl: options.studioUrl,
|
|
322
352
|
token,
|
|
@@ -326,13 +356,16 @@ async function snapshot(options) {
|
|
|
326
356
|
if (finished.status === "failed") throw new Error(finished.error ?? "Snapshot job failed");
|
|
327
357
|
if (!finished.snapshot) throw new Error("Snapshot job succeeded without a snapshot");
|
|
328
358
|
const result = toResult(options.studioUrl, finished.snapshot, agent.slug);
|
|
329
|
-
|
|
359
|
+
if (options.json) log("Snapshot published");
|
|
360
|
+
else spinner?.stop("Snapshot published");
|
|
330
361
|
if (options.json) console.log(JSON.stringify(result));
|
|
331
362
|
else printSummary(result);
|
|
332
363
|
} catch (error) {
|
|
333
|
-
|
|
364
|
+
if (options.json) log("Snapshot failed");
|
|
365
|
+
else spinner?.stop("Snapshot failed");
|
|
334
366
|
throw error;
|
|
335
367
|
} finally {
|
|
368
|
+
if (options.json) log("Disconnecting from Kubb Studio");
|
|
336
369
|
client.disconnect();
|
|
337
370
|
}
|
|
338
371
|
}
|
|
@@ -346,24 +379,6 @@ const ACTIONS = [
|
|
|
346
379
|
"snapshot"
|
|
347
380
|
];
|
|
348
381
|
/**
|
|
349
|
-
* Opens a URL in the user's browser. Best effort: a failure just means the user follows the
|
|
350
|
-
* printed link instead.
|
|
351
|
-
*
|
|
352
|
-
* `start` is a `cmd.exe` builtin, not an executable on PATH, so Windows runs it through `cmd`.
|
|
353
|
-
* The empty string is the window-title argument `start` expects before the URL.
|
|
354
|
-
*/
|
|
355
|
-
async function openInBrowser(url) {
|
|
356
|
-
try {
|
|
357
|
-
if (process.platform === "win32") await x("cmd", [
|
|
358
|
-
"/c",
|
|
359
|
-
"start",
|
|
360
|
-
"",
|
|
361
|
-
url
|
|
362
|
-
]);
|
|
363
|
-
else await x(process.platform === "darwin" ? "open" : "xdg-open", [url]);
|
|
364
|
-
} catch {}
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
382
|
* Pairs this machine with Studio and stores the resulting token.
|
|
368
383
|
*
|
|
369
384
|
* The CLI holds a code and the browser approves it, rather than the user copying a token out of
|
|
@@ -378,7 +393,7 @@ async function login({ studioUrl, autoOpen }, { signal, previousCredentials } =
|
|
|
378
393
|
signal
|
|
379
394
|
});
|
|
380
395
|
console.log(`\nOpen ${styleText("cyan", session.verification_uri)} and approve the code ${styleText("bold", session.user_code)}`);
|
|
381
|
-
if (autoOpen)
|
|
396
|
+
if (autoOpen) openInBrowser(session.verification_uri_complete);
|
|
382
397
|
const spinner = createSpinner();
|
|
383
398
|
spinner.start("Waiting for approval");
|
|
384
399
|
try {
|
|
@@ -750,4 +765,4 @@ const runner = async ({ values }) => {
|
|
|
750
765
|
//#endregion
|
|
751
766
|
export { runner };
|
|
752
767
|
|
|
753
|
-
//# sourceMappingURL=run-
|
|
768
|
+
//# sourceMappingURL=run-BoDUDlHe.js.map
|