@kubb/studio 5.2.6 → 5.2.8

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/index.d.ts CHANGED
@@ -11,6 +11,141 @@ import { Config, Hookable, KubbHooks } from "@kubb/core";
11
11
  export declare class InvalidAgentTokenError extends Error {
12
12
  constructor(studioUrl: string, options?: ErrorOptions);
13
13
  }
14
+ /**
15
+ * Status values returned by Studio's jobs API.
16
+ */
17
+ type StudioJobStatus = 'queued' | 'running' | 'success' | 'failed';
18
+ /**
19
+ * Package view returned on a successful snapshot job from Studio.
20
+ */
21
+ type StudioSnapshot = {
22
+ /**
23
+ * Immutable snapshot id.
24
+ */
25
+ id: string;
26
+ /**
27
+ * npm package name, or `null` when Studio stored none.
28
+ */
29
+ name: string | null;
30
+ /**
31
+ * npm package version, or `null` when Studio stored none.
32
+ */
33
+ version: string | null;
34
+ /**
35
+ * Subresource integrity hash for the tarball, or `null` when unavailable.
36
+ */
37
+ integrity: string | null;
38
+ /**
39
+ * Preferable download path, often the readable `/packages/{agentSlug}/{name}.tgz` form.
40
+ */
41
+ url: string;
42
+ /**
43
+ * Stable download path keyed by snapshot id.
44
+ */
45
+ snapshotIdUrl: string;
46
+ /**
47
+ * ISO timestamp after which Studio may delete the tarball.
48
+ */
49
+ expiresAt: string;
50
+ };
51
+ /**
52
+ * Job record from `POST /api/jobs` and `GET /api/jobs/{id}`.
53
+ */
54
+ type StudioJob = {
55
+ /**
56
+ * Job id returned by Studio when the job was queued.
57
+ */
58
+ id: string;
59
+ /**
60
+ * Current status. Poll until `success` or `failed`.
61
+ */
62
+ status: StudioJobStatus;
63
+ /**
64
+ * Failure message when `status` is `failed`.
65
+ */
66
+ error?: string;
67
+ /**
68
+ * Package view when a snapshot job finished successfully.
69
+ */
70
+ snapshot?: StudioSnapshot;
71
+ };
72
+ /**
73
+ * Queues a generation or snapshot job on Studio (`POST /api/jobs`).
74
+ *
75
+ * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.
76
+ * Authenticates with the organization CI API key via `x-api-key`.
77
+ *
78
+ * @example Snapshot job
79
+ * ```ts
80
+ * const job = await createJob({
81
+ * studioUrl: 'https://kubb.studio',
82
+ * token: process.env.KUBB_TOKEN!,
83
+ * type: 'snapshot',
84
+ * agentId: agent.id,
85
+ * name: '@kubb/demo',
86
+ * version: '1.0.0',
87
+ * })
88
+ * const finished = await waitForJob({ studioUrl, token, id: job.id })
89
+ * ```
90
+ */
91
+ export declare function createJob({ studioUrl, token, type, agentId, name, version, config }: {
92
+ studioUrl: string;
93
+ token: string;
94
+ type: 'generation' | 'snapshot';
95
+ agentId: string;
96
+ name?: string;
97
+ version?: string;
98
+ config?: Record<string, unknown>;
99
+ }): Promise<StudioJob>;
100
+ /**
101
+ * Polls `GET /api/jobs/{id}` until the job reaches `success` or `failed`.
102
+ *
103
+ * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the
104
+ * deadline passes before Studio finishes.
105
+ */
106
+ export declare function waitForJob({ studioUrl, token, id, timeoutMs }: {
107
+ studioUrl: string;
108
+ token: string;
109
+ id: string;
110
+ /**
111
+ * How long to keep polling before throwing, in milliseconds.
112
+ *
113
+ * @default 60000
114
+ */
115
+ timeoutMs?: number;
116
+ }): Promise<StudioJob>;
117
+ /**
118
+ * CI agent returned by {@link createAgent}. The token is issued only once, at creation or reuse.
119
+ */
120
+ type StudioAgent = {
121
+ /**
122
+ * Agent id, passed to {@link createJob} as `agentId`.
123
+ */
124
+ id: string;
125
+ /**
126
+ * Human-readable slug, used to build the readable snapshot URL and the agent's Studio page.
127
+ */
128
+ slug: string;
129
+ /**
130
+ * Agent display name.
131
+ */
132
+ name: string;
133
+ /**
134
+ * Bearer token for the WebSocket agent session. Mask it before logging.
135
+ */
136
+ token: string;
137
+ };
138
+ /**
139
+ * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.
140
+ * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of
141
+ * consuming a new one from the organization's agent limit.
142
+ */
143
+ export declare function createAgent({ studioUrl, token, name, machineToken }: {
144
+ studioUrl: string;
145
+ token: string;
146
+ name: string;
147
+ machineToken: string;
148
+ }): Promise<StudioAgent>;
14
149
  //#endregion
15
150
  //#region src/StudioSession.d.ts
16
151
  type StudioSessionOptions = {
@@ -219,6 +354,11 @@ export declare function setStorage(next: Storage): void;
219
354
  * survive a restart. Repeated pairings of one machine depend on that secret staying put.
220
355
  */
221
356
  export declare function createFileStorage(base: string): Storage;
357
+ /**
358
+ * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its
359
+ * own identity without duplicating `getMachineToken`'s SHA-256 step.
360
+ */
361
+ export declare function machineTokenFrom(secret: string): string;
222
362
  //#endregion
223
363
  //#region src/runConnection.d.ts
224
364
  /**
@@ -377,5 +517,5 @@ type PollOptions = {
377
517
  */
378
518
  export declare function pollForPairingToken({ studioUrl, session, signal }: PollOptions): Promise<PairingResult>;
379
519
  //#endregion
380
- export { type Client, type ClientOptions, type ConnectionOptions, type StudioConnectedContext, createJobId };
520
+ export { type Client, type ClientOptions, type ConnectionOptions, type StudioAgent, type StudioConnectedContext, type StudioJob, type StudioJobStatus, type StudioSnapshot, createJobId };
381
521
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -406,14 +406,21 @@ async function loadOrCreateFallbackSecret() {
406
406
  return secret;
407
407
  }
408
408
  /**
409
+ * Hashes a stable secret into the machine token Studio expects, so a host can derive one from its
410
+ * own identity without duplicating `getMachineToken`'s SHA-256 step.
411
+ */
412
+ function machineTokenFrom(secret) {
413
+ return hash("sha256", secret);
414
+ }
415
+ /**
409
416
  * Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.
410
417
  * Falls back to a generated secret persisted in the runtime storage if the env var is not set.
411
418
  * The token is hashed with SHA-256.
412
419
  */
413
420
  async function getMachineToken() {
414
- if (process$1.env.KUBB_AGENT_SECRET) return hash("sha256", process$1.env.KUBB_AGENT_SECRET);
421
+ if (process$1.env.KUBB_AGENT_SECRET) return machineTokenFrom(process$1.env.KUBB_AGENT_SECRET);
415
422
  fallbackSecretPromise ??= loadOrCreateFallbackSecret();
416
- return hash("sha256", await fallbackSecretPromise);
423
+ return machineTokenFrom(await fallbackSecretPromise);
417
424
  }
418
425
  //#endregion
419
426
  //#region src/api.ts
@@ -564,9 +571,82 @@ async function disconnect({ sessionId, token, studioUrl, slug }) {
564
571
  console.warn(styleText("yellow", `[${tag}] Failed to notify Studio of disconnection: ${getErrorMessage(error)}`));
565
572
  }
566
573
  }
574
+ /**
575
+ * Queues a generation or snapshot job on Studio (`POST /api/jobs`).
576
+ *
577
+ * Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.
578
+ * Authenticates with the organization CI API key via `x-api-key`.
579
+ *
580
+ * @example Snapshot job
581
+ * ```ts
582
+ * const job = await createJob({
583
+ * studioUrl: 'https://kubb.studio',
584
+ * token: process.env.KUBB_TOKEN!,
585
+ * type: 'snapshot',
586
+ * agentId: agent.id,
587
+ * name: '@kubb/demo',
588
+ * version: '1.0.0',
589
+ * })
590
+ * const finished = await waitForJob({ studioUrl, token, id: job.id })
591
+ * ```
592
+ */
593
+ async function createJob({ studioUrl, token, type, agentId, name, version, config }) {
594
+ const { job } = await ofetch(`${studioUrl}/api/jobs`, {
595
+ method: "POST",
596
+ headers: { "x-api-key": token },
597
+ body: {
598
+ type,
599
+ agentId,
600
+ name,
601
+ version,
602
+ config
603
+ }
604
+ });
605
+ return job;
606
+ }
607
+ /**
608
+ * Polls `GET /api/jobs/{id}` until the job reaches `success` or `failed`.
609
+ *
610
+ * A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the
611
+ * deadline passes before Studio finishes.
612
+ */
613
+ async function waitForJob({ studioUrl, token, id, timeoutMs = 6e4 }) {
614
+ const deadline = Date.now() + timeoutMs;
615
+ for (;;) {
616
+ const { job } = await ofetch(`${studioUrl}/api/jobs/${id}`, { headers: { "x-api-key": token } });
617
+ if (job.status === "success" || job.status === "failed") return job;
618
+ if (Date.now() >= deadline) throw new Error("Timed out waiting for the Studio job");
619
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
620
+ }
621
+ }
622
+ /**
623
+ * Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.
624
+ * Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of
625
+ * consuming a new one from the organization's agent limit.
626
+ */
627
+ async function createAgent({ studioUrl, token, name, machineToken }) {
628
+ try {
629
+ return await ofetch(`${studioUrl}/api/agents`, {
630
+ method: "POST",
631
+ headers: { "x-api-key": token },
632
+ body: {
633
+ name,
634
+ machineToken
635
+ }
636
+ });
637
+ } catch (error) {
638
+ if (error instanceof FetchError) {
639
+ const upgradeUrl = error.data?.data?.upgradeUrl;
640
+ const detail = responseMessage(error.data) ?? getErrorMessage(error);
641
+ const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : "";
642
+ throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error });
643
+ }
644
+ throw error;
645
+ }
646
+ }
567
647
  //#endregion
568
648
  //#region package.json
569
- var version = "5.2.6";
649
+ var version = "5.2.8";
570
650
  //#endregion
571
651
  //#region src/hooks.ts
572
652
  /**
@@ -2442,6 +2522,6 @@ async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, sessio
2442
2522
  throw new Error("The pairing code expired, pair again");
2443
2523
  }
2444
2524
  //#endregion
2445
- export { InvalidAgentTokenError, PairingCanceledError, createClient, createFileStorage, createJobId, defaultStudioUrl, pollForPairingToken, runConnection, setStorage, startPairing };
2525
+ export { InvalidAgentTokenError, PairingCanceledError, createAgent, createClient, createFileStorage, createJob, createJobId, defaultStudioUrl, machineTokenFrom, pollForPairingToken, runConnection, setStorage, startPairing, waitForJob };
2446
2526
 
2447
2527
  //# sourceMappingURL=index.js.map