@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/README.md +32 -0
- package/dist/index.cjs +87 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +141 -1
- package/dist/index.js +84 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -96,6 +96,38 @@ nothing can read it back.
|
|
|
96
96
|
member can approve. A host that pairs a shared or tier-limited agent passes `clientId: 'kubb-agent'`
|
|
97
97
|
and an `agentKind`, whose codes only an admin can approve.
|
|
98
98
|
|
|
99
|
+
## Asynchronous jobs
|
|
100
|
+
|
|
101
|
+
CI and automation queue work with `createJob` and poll with `waitForJob`. Both send the
|
|
102
|
+
organization CI API key as `x-api-key`. They do not open a WebSocket.
|
|
103
|
+
|
|
104
|
+
| Step | Call | What it does |
|
|
105
|
+
| ------ | -------------------- | --------------------------------------------------------------------- |
|
|
106
|
+
| Queue | `POST /api/jobs` | Accepts a `generation` or `snapshot` job and returns `202` with an id |
|
|
107
|
+
| Status | `GET /api/jobs/{id}` | Returns the job until `success` or `failed` |
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { createJob, waitForJob } from '@kubb/studio'
|
|
111
|
+
|
|
112
|
+
const job = await createJob({
|
|
113
|
+
studioUrl: 'https://kubb.studio',
|
|
114
|
+
token: process.env.KUBB_TOKEN!,
|
|
115
|
+
type: 'snapshot',
|
|
116
|
+
agentId: agent.id,
|
|
117
|
+
name: '@scope/package',
|
|
118
|
+
version: '1.0.0',
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const finished = await waitForJob({
|
|
122
|
+
studioUrl: 'https://kubb.studio',
|
|
123
|
+
token: process.env.KUBB_TOKEN!,
|
|
124
|
+
id: job.id,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
if (finished.status === 'failed') throw new Error(finished.error)
|
|
128
|
+
const snapshot = finished.snapshot
|
|
129
|
+
```
|
|
130
|
+
|
|
99
131
|
## Protocol
|
|
100
132
|
|
|
101
133
|
`@kubb/studio/protocol` holds the WebSocket message types shared by both ends, so the agent and
|
package/dist/index.cjs
CHANGED
|
@@ -411,14 +411,21 @@ async function loadOrCreateFallbackSecret() {
|
|
|
411
411
|
return secret;
|
|
412
412
|
}
|
|
413
413
|
/**
|
|
414
|
+
* Hashes a stable secret into the machine token Studio expects, so a host can derive one from its
|
|
415
|
+
* own identity without duplicating `getMachineToken`'s SHA-256 step.
|
|
416
|
+
*/
|
|
417
|
+
function machineTokenFrom(secret) {
|
|
418
|
+
return (0, node_crypto.hash)("sha256", secret);
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
414
421
|
* Returns the machine token derived from the `KUBB_AGENT_SECRET` environment variable.
|
|
415
422
|
* Falls back to a generated secret persisted in the runtime storage if the env var is not set.
|
|
416
423
|
* The token is hashed with SHA-256.
|
|
417
424
|
*/
|
|
418
425
|
async function getMachineToken() {
|
|
419
|
-
if (node_process.default.env.KUBB_AGENT_SECRET) return (
|
|
426
|
+
if (node_process.default.env.KUBB_AGENT_SECRET) return machineTokenFrom(node_process.default.env.KUBB_AGENT_SECRET);
|
|
420
427
|
fallbackSecretPromise ??= loadOrCreateFallbackSecret();
|
|
421
|
-
return (
|
|
428
|
+
return machineTokenFrom(await fallbackSecretPromise);
|
|
422
429
|
}
|
|
423
430
|
//#endregion
|
|
424
431
|
//#region src/api.ts
|
|
@@ -569,9 +576,82 @@ async function disconnect({ sessionId, token, studioUrl, slug }) {
|
|
|
569
576
|
console.warn((0, node_util.styleText)("yellow", `[${tag}] Failed to notify Studio of disconnection: ${getErrorMessage(error)}`));
|
|
570
577
|
}
|
|
571
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Queues a generation or snapshot job on Studio (`POST /api/jobs`).
|
|
581
|
+
*
|
|
582
|
+
* Returns as soon as Studio accepts the job (`202`). Poll with {@link waitForJob} until it finishes.
|
|
583
|
+
* Authenticates with the organization CI API key via `x-api-key`.
|
|
584
|
+
*
|
|
585
|
+
* @example Snapshot job
|
|
586
|
+
* ```ts
|
|
587
|
+
* const job = await createJob({
|
|
588
|
+
* studioUrl: 'https://kubb.studio',
|
|
589
|
+
* token: process.env.KUBB_TOKEN!,
|
|
590
|
+
* type: 'snapshot',
|
|
591
|
+
* agentId: agent.id,
|
|
592
|
+
* name: '@kubb/demo',
|
|
593
|
+
* version: '1.0.0',
|
|
594
|
+
* })
|
|
595
|
+
* const finished = await waitForJob({ studioUrl, token, id: job.id })
|
|
596
|
+
* ```
|
|
597
|
+
*/
|
|
598
|
+
async function createJob({ studioUrl, token, type, agentId, name, version, config }) {
|
|
599
|
+
const { job } = await (0, ofetch.ofetch)(`${studioUrl}/api/jobs`, {
|
|
600
|
+
method: "POST",
|
|
601
|
+
headers: { "x-api-key": token },
|
|
602
|
+
body: {
|
|
603
|
+
type,
|
|
604
|
+
agentId,
|
|
605
|
+
name,
|
|
606
|
+
version,
|
|
607
|
+
config
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
return job;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Polls `GET /api/jobs/{id}` until the job reaches `success` or `failed`.
|
|
614
|
+
*
|
|
615
|
+
* A `failed` job resolves normally. Check `job.status` and `job.error`. Throws only when the
|
|
616
|
+
* deadline passes before Studio finishes.
|
|
617
|
+
*/
|
|
618
|
+
async function waitForJob({ studioUrl, token, id, timeoutMs = 6e4 }) {
|
|
619
|
+
const deadline = Date.now() + timeoutMs;
|
|
620
|
+
for (;;) {
|
|
621
|
+
const { job } = await (0, ofetch.ofetch)(`${studioUrl}/api/jobs/${id}`, { headers: { "x-api-key": token } });
|
|
622
|
+
if (job.status === "success" || job.status === "failed") return job;
|
|
623
|
+
if (Date.now() >= deadline) throw new Error("Timed out waiting for the Studio job");
|
|
624
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Creates or reuses a CI agent (`POST /api/agents`), keyed by `(organization, machineToken)`.
|
|
629
|
+
* Authenticates via `x-api-key`. Reusing the same `machineToken` reuses the same agent instead of
|
|
630
|
+
* consuming a new one from the organization's agent limit.
|
|
631
|
+
*/
|
|
632
|
+
async function createAgent({ studioUrl, token, name, machineToken }) {
|
|
633
|
+
try {
|
|
634
|
+
return await (0, ofetch.ofetch)(`${studioUrl}/api/agents`, {
|
|
635
|
+
method: "POST",
|
|
636
|
+
headers: { "x-api-key": token },
|
|
637
|
+
body: {
|
|
638
|
+
name,
|
|
639
|
+
machineToken
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
} catch (error) {
|
|
643
|
+
if (error instanceof ofetch.FetchError) {
|
|
644
|
+
const upgradeUrl = error.data?.data?.upgradeUrl;
|
|
645
|
+
const detail = responseMessage(error.data) ?? getErrorMessage(error);
|
|
646
|
+
const hint = upgradeUrl ? ` Agent limit reached; upgrade at ${upgradeUrl}.` : "";
|
|
647
|
+
throw new Error(`Failed to create a Kubb Studio agent: ${detail}${hint}`, { cause: error });
|
|
648
|
+
}
|
|
649
|
+
throw error;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
572
652
|
//#endregion
|
|
573
653
|
//#region package.json
|
|
574
|
-
var version = "5.2.
|
|
654
|
+
var version = "5.2.8";
|
|
575
655
|
//#endregion
|
|
576
656
|
//#region src/hooks.ts
|
|
577
657
|
/**
|
|
@@ -2449,13 +2529,17 @@ async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, sessio
|
|
|
2449
2529
|
//#endregion
|
|
2450
2530
|
exports.InvalidAgentTokenError = InvalidAgentTokenError;
|
|
2451
2531
|
exports.PairingCanceledError = PairingCanceledError;
|
|
2532
|
+
exports.createAgent = createAgent;
|
|
2452
2533
|
exports.createClient = createClient;
|
|
2453
2534
|
exports.createFileStorage = createFileStorage;
|
|
2535
|
+
exports.createJob = createJob;
|
|
2454
2536
|
exports.createJobId = require_protocol.createJobId;
|
|
2455
2537
|
exports.defaultStudioUrl = defaultStudioUrl;
|
|
2538
|
+
exports.machineTokenFrom = machineTokenFrom;
|
|
2456
2539
|
exports.pollForPairingToken = pollForPairingToken;
|
|
2457
2540
|
exports.runConnection = runConnection;
|
|
2458
2541
|
exports.setStorage = setStorage;
|
|
2459
2542
|
exports.startPairing = startPairing;
|
|
2543
|
+
exports.waitForJob = waitForJob;
|
|
2460
2544
|
|
|
2461
2545
|
//# sourceMappingURL=index.cjs.map
|