@kubb/studio 5.3.5 → 5.3.7
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 -11
- package/dist/index.cjs +469 -516
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +19 -5
- package/dist/index.js +433 -506
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs +31 -45
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +126 -275
- package/dist/protocol.js +31 -39
- package/dist/protocol.js.map +1 -1
- package/package.json +6 -4
- package/dist/rolldown-runtime-qbf5tadS.cjs +0 -38
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __name } from "./rolldown-runtime-CRm0XQPb.js";
|
|
2
|
-
import {
|
|
2
|
+
import { generationEventTypes } from "./protocol.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { promisify, styleText } from "node:util";
|
|
5
5
|
import process$1 from "node:process";
|
|
@@ -19,6 +19,7 @@ import { mergeDeep } from "remeda";
|
|
|
19
19
|
import { tmpdir } from "node:os";
|
|
20
20
|
import { gzip } from "node:zlib";
|
|
21
21
|
import { build } from "tsdown";
|
|
22
|
+
import { RpcTarget, newWebSocketRpcSession } from "capnweb";
|
|
22
23
|
import WebSocket from "ws";
|
|
23
24
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
24
25
|
//#region src/constants.ts
|
|
@@ -41,6 +42,8 @@ const agentDefaults = {
|
|
|
41
42
|
* a slower cadence would make a healthy agent look dead after a single missed ping.
|
|
42
43
|
*/
|
|
43
44
|
maxHeartbeatIntervalMs: 6e4,
|
|
45
|
+
/** How long a heartbeat ping may take before the session is treated as dead. */
|
|
46
|
+
heartbeatTimeoutMs: 1e4,
|
|
44
47
|
poolSize: 1
|
|
45
48
|
};
|
|
46
49
|
//#endregion
|
|
@@ -618,7 +621,7 @@ const INITIAL_POLL_DELAY_MS = 2e3;
|
|
|
618
621
|
*/
|
|
619
622
|
const MAX_POLL_INTERVAL_MS = 3e4;
|
|
620
623
|
/**
|
|
621
|
-
* Polls `GET /api/jobs/{id}` until the job reaches
|
|
624
|
+
* Polls `GET /api/jobs/{id}` until the job reaches a terminal status, waiting
|
|
622
625
|
* {@link INITIAL_POLL_DELAY_MS} first and doubling up to {@link MAX_POLL_INTERVAL_MS} so a long
|
|
623
626
|
* job stays inside the API key's rate limit.
|
|
624
627
|
*
|
|
@@ -637,7 +640,7 @@ async function waitForJob({ studioUrl, token, id, timeoutMs = 6e4 }) {
|
|
|
637
640
|
headers: { "x-api-key": token },
|
|
638
641
|
retry: false
|
|
639
642
|
});
|
|
640
|
-
if (job.status === "success" || job.status === "failed") return job;
|
|
643
|
+
if (job.status === "success" || job.status === "failed" || job.status === "canceled") return job;
|
|
641
644
|
} catch (error) {
|
|
642
645
|
const response = error.response;
|
|
643
646
|
if (response?.status !== 429) throw error;
|
|
@@ -673,7 +676,7 @@ async function createAgent({ studioUrl, token, name, machineToken }) {
|
|
|
673
676
|
}
|
|
674
677
|
//#endregion
|
|
675
678
|
//#region package.json
|
|
676
|
-
var version = "5.3.
|
|
679
|
+
var version = "5.3.7";
|
|
677
680
|
//#endregion
|
|
678
681
|
//#region src/hooks.ts
|
|
679
682
|
/**
|
|
@@ -684,16 +687,19 @@ var version = "5.3.5";
|
|
|
684
687
|
* Returns a remover, so a session that runs one generation after another on the same emitter does
|
|
685
688
|
* not stack a listener per run.
|
|
686
689
|
*/
|
|
687
|
-
function setupHookListener(hooks, root) {
|
|
690
|
+
function setupHookListener(hooks, root, signal) {
|
|
688
691
|
return hooks.hook("kubb:hook:start", async (ctx) => {
|
|
689
692
|
const { id, command, args } = ctx;
|
|
690
693
|
if (!id) return;
|
|
691
694
|
const commandWithArgs = args?.length ? `${command} ${args.join(" ")}` : command;
|
|
692
695
|
try {
|
|
693
|
-
const proc = x(command, [...args ?? []], {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
696
|
+
const proc = x(command, [...args ?? []], {
|
|
697
|
+
signal,
|
|
698
|
+
nodeOptions: {
|
|
699
|
+
cwd: root,
|
|
700
|
+
detached: true
|
|
701
|
+
}
|
|
702
|
+
});
|
|
697
703
|
for await (const line of proc) await hooks.callHook("kubb:hook:line", {
|
|
698
704
|
id,
|
|
699
705
|
line
|
|
@@ -742,8 +748,11 @@ function waitForHookEnd(hooks, hookId) {
|
|
|
742
748
|
const handleHookEnd = (ctx) => {
|
|
743
749
|
if (ctx.id !== hookId) return;
|
|
744
750
|
hooks.removeHook("kubb:hook:end", handleHookEnd);
|
|
745
|
-
if (ctx.success)
|
|
746
|
-
|
|
751
|
+
if (ctx.success) {
|
|
752
|
+
resolve();
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
reject(ctx.error);
|
|
747
756
|
};
|
|
748
757
|
hooks.hook("kubb:hook:end", handleHookEnd);
|
|
749
758
|
});
|
|
@@ -1282,14 +1291,14 @@ function applyRemove(call, path) {
|
|
|
1282
1291
|
});
|
|
1283
1292
|
}
|
|
1284
1293
|
/**
|
|
1285
|
-
* Adds a `pluginX(...)` call to a config's plugins array.
|
|
1286
|
-
*
|
|
1294
|
+
* Adds a `pluginX(...)` call to a config's plugins array. Replaying the same add is a no-op, while
|
|
1295
|
+
* an import name collision with an unrelated package remains an error.
|
|
1287
1296
|
*/
|
|
1288
1297
|
function applyAddPlugin(mod, config, edit) {
|
|
1289
1298
|
if (!isKubbPluginSpecifier(edit.plugin)) return { reason: `"${edit.plugin}" is not a @kubb/plugin-* package` };
|
|
1290
1299
|
const importName = edit.importName ?? toExportName(edit.plugin);
|
|
1291
1300
|
if (!IDENTIFIER.test(importName)) return { reason: `"${importName}" is not a valid import name` };
|
|
1292
|
-
if (pluginCalls(mod, config).some((plugin) => plugin.packageName === edit.plugin)) return {
|
|
1301
|
+
if (pluginCalls(mod, config).some((plugin) => plugin.packageName === edit.plugin)) return { noop: true };
|
|
1293
1302
|
const taken = importedFrom(mod).get(importName);
|
|
1294
1303
|
if (taken && taken !== edit.plugin) return { reason: `${importName} is already imported from ${taken}` };
|
|
1295
1304
|
const options = edit.options ?? {};
|
|
@@ -1410,6 +1419,10 @@ function applyConfigEdits(source, edits) {
|
|
|
1410
1419
|
applied: false,
|
|
1411
1420
|
reason: result.reason
|
|
1412
1421
|
};
|
|
1422
|
+
if ("noop" in result) return {
|
|
1423
|
+
edit,
|
|
1424
|
+
applied: true
|
|
1425
|
+
};
|
|
1413
1426
|
const afterLine = lastImportEndLine(mod);
|
|
1414
1427
|
let next = generateCode(mod, { format }).code;
|
|
1415
1428
|
if (result.addImport) next = insertImportLine({
|
|
@@ -1543,14 +1556,19 @@ function formatGenerationFailure(diagnostics) {
|
|
|
1543
1556
|
* can forward progress to connected clients. After a successful build, auto-formatting and
|
|
1544
1557
|
* linting are applied when configured, followed by any user-defined `hooks.done` commands.
|
|
1545
1558
|
*/
|
|
1546
|
-
async function generate({ config, hooks }) {
|
|
1559
|
+
async function generate({ config, hooks, signal }) {
|
|
1560
|
+
signal?.throwIfAborted();
|
|
1547
1561
|
const hrStart = process$1.hrtime();
|
|
1548
1562
|
await hooks.callHook("kubb:generation:start", { config });
|
|
1549
1563
|
await hooks.callHook("kubb:info", { message: config.name ? `Setup generation ${config.name}` : "Setup generation" });
|
|
1550
|
-
const kubb = createKubb(config, {
|
|
1564
|
+
const kubb = createKubb(config, {
|
|
1565
|
+
hooks,
|
|
1566
|
+
signal
|
|
1567
|
+
});
|
|
1551
1568
|
await kubb.setup();
|
|
1552
1569
|
await hooks.callHook("kubb:info", { message: config.name ? `Build generation ${config.name}` : "Build generation" });
|
|
1553
1570
|
const { files, diagnostics, storage } = await kubb.safeBuild();
|
|
1571
|
+
signal?.throwIfAborted();
|
|
1554
1572
|
await hooks.callHook("kubb:info", { message: "Load summary" });
|
|
1555
1573
|
for (const diagnostic of diagnostics.filter(isProblemErrorDiagnostic)) await hooks.callHook("kubb:error", { error: new Error(diagnostic.plugin ? `${diagnostic.plugin}: ${diagnostic.message}` : diagnostic.message) });
|
|
1556
1574
|
const status = Diagnostics.hasError(diagnostics) ? "failed" : "success";
|
|
@@ -1585,6 +1603,7 @@ async function generate({ config, hooks }) {
|
|
|
1585
1603
|
await hooks.callHook("kubb:success", { message: `${step.verbing} with ${tool} successfully` });
|
|
1586
1604
|
} catch (caughtError) {
|
|
1587
1605
|
await hooks.callHook("kubb:error", { error: new Error(command.errorMessage, { cause: caughtError }) });
|
|
1606
|
+
signal?.throwIfAborted();
|
|
1588
1607
|
}
|
|
1589
1608
|
await hooks.callHook(`kubb:${step.kind}:end`);
|
|
1590
1609
|
}
|
|
@@ -1702,16 +1721,28 @@ async function createSnapshotPackage(files, packageInfo) {
|
|
|
1702
1721
|
const filePath = join(entry.parentPath, entry.name);
|
|
1703
1722
|
return [`package/dist/${relative(dist, filePath).split(sep).join("/")}`, await readFile(filePath, "utf8")];
|
|
1704
1723
|
}));
|
|
1724
|
+
const builtPaths = new Set(builtEntries.map(([path]) => path));
|
|
1725
|
+
const hasBarrel = builtPaths.has("package/dist/index.mjs") && builtPaths.has("package/dist/index.cjs");
|
|
1726
|
+
const barrelFields = hasBarrel ? {
|
|
1727
|
+
main: "./dist/index.cjs",
|
|
1728
|
+
module: "./dist/index.mjs"
|
|
1729
|
+
} : {};
|
|
1730
|
+
const barrelExport = hasBarrel ? { ".": {
|
|
1731
|
+
import: "./dist/index.mjs",
|
|
1732
|
+
require: "./dist/index.cjs"
|
|
1733
|
+
} } : {};
|
|
1705
1734
|
const entries = {
|
|
1706
1735
|
"package/package.json": JSON.stringify({
|
|
1707
1736
|
...packageInfo,
|
|
1708
1737
|
type: "module",
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1738
|
+
...barrelFields,
|
|
1739
|
+
exports: {
|
|
1740
|
+
...barrelExport,
|
|
1741
|
+
"./*": {
|
|
1742
|
+
import: "./dist/*.mjs",
|
|
1743
|
+
require: "./dist/*.cjs"
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1715
1746
|
}, null, 2),
|
|
1716
1747
|
...Object.fromEntries(resolvedPaths.map(({ content, target }) => [target, content])),
|
|
1717
1748
|
...Object.fromEntries(builtEntries)
|
|
@@ -1740,13 +1771,6 @@ async function createSnapshotPackage(files, packageInfo) {
|
|
|
1740
1771
|
* takes over.
|
|
1741
1772
|
*/
|
|
1742
1773
|
const CONNECT_TIMEOUT_MS = 5e3;
|
|
1743
|
-
/**
|
|
1744
|
-
* Per-socket event counter. Every data message carries the next value so Studio can restore the
|
|
1745
|
-
* agent's emission order even when the relay delivers frames out of order. Keyed by the socket so
|
|
1746
|
-
* the count stays monotonic across every generation run on one connection, and is dropped
|
|
1747
|
-
* automatically once the socket is collected.
|
|
1748
|
-
*/
|
|
1749
|
-
const eventSeqCounters = /* @__PURE__ */ new WeakMap();
|
|
1750
1774
|
const require = createRequire(import.meta.url);
|
|
1751
1775
|
function relativeStoragePath(root, filePath) {
|
|
1752
1776
|
return (isAbsolute(filePath) ? relative(resolve(root), filePath) : filePath).replaceAll("\\", "/");
|
|
@@ -1771,19 +1795,17 @@ async function resolvePeerDependencies(names) {
|
|
|
1771
1795
|
}));
|
|
1772
1796
|
for (const [index, name] of uniqueNames.entries()) {
|
|
1773
1797
|
const version = versions[index];
|
|
1774
|
-
if (version)
|
|
1775
|
-
|
|
1798
|
+
if (version) {
|
|
1799
|
+
peerDependencies[name] = version;
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
missingDependencies.push(name);
|
|
1776
1803
|
}
|
|
1777
1804
|
return {
|
|
1778
1805
|
peerDependencies,
|
|
1779
1806
|
missingDependencies
|
|
1780
1807
|
};
|
|
1781
1808
|
}
|
|
1782
|
-
function nextEventSeq(ws) {
|
|
1783
|
-
const seq = eventSeqCounters.get(ws) ?? 0;
|
|
1784
|
-
eventSeqCounters.set(ws, seq + 1);
|
|
1785
|
-
return seq;
|
|
1786
|
-
}
|
|
1787
1809
|
/**
|
|
1788
1810
|
* Opens a Studio WebSocket connection and closes it when the initial handshake exceeds the configured timeout.
|
|
1789
1811
|
*/
|
|
@@ -1796,41 +1818,15 @@ function createWebsocket(url, options) {
|
|
|
1796
1818
|
ws.once("close", () => clearTimeout(timer));
|
|
1797
1819
|
return ws;
|
|
1798
1820
|
}
|
|
1799
|
-
/**
|
|
1800
|
-
|
|
1801
|
-
*/
|
|
1802
|
-
function sendAgentMessage(ws, message) {
|
|
1803
|
-
try {
|
|
1804
|
-
if (ws.readyState !== WebSocket.OPEN) return;
|
|
1805
|
-
ws.send(JSON.stringify(message));
|
|
1806
|
-
} catch (error) {
|
|
1807
|
-
throw new Error("Failed to send message to Kubb Studio", { cause: error });
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
/**
|
|
1811
|
-
* Sends a single `kubb:error` payload to Studio, stamped from the same per-socket counter the event stream
|
|
1812
|
-
* uses so Studio can still order it against the generation events around it.
|
|
1813
|
-
*/
|
|
1814
|
-
function sendErrorMessage(ws, error, jobId) {
|
|
1815
|
-
sendAgentMessage(ws, {
|
|
1816
|
-
type: "agent:data",
|
|
1817
|
-
jobId,
|
|
1818
|
-
payload: {
|
|
1819
|
-
type: "kubb:error",
|
|
1820
|
-
data: [{
|
|
1821
|
-
message: error.message,
|
|
1822
|
-
stack: error.stack
|
|
1823
|
-
}],
|
|
1824
|
-
timestamp: Date.now(),
|
|
1825
|
-
seq: nextEventSeq(ws)
|
|
1826
|
-
}
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
/**
|
|
1830
|
-
* Forwards selected Kubb lifecycle events to Studio as data messages for the active session.
|
|
1831
|
-
*/
|
|
1832
|
-
function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
1821
|
+
/** Forwards selected Kubb lifecycle events to a native Cap'n Web stream. */
|
|
1822
|
+
function createGenerationStream(hooks, jobId, options = {}) {
|
|
1833
1823
|
const unhooks = [];
|
|
1824
|
+
let root = "";
|
|
1825
|
+
const transform = new TransformStream(void 0, void 0, { highWaterMark: Infinity });
|
|
1826
|
+
const writer = transform.writable.getWriter();
|
|
1827
|
+
let writes = Promise.resolve();
|
|
1828
|
+
let closed = false;
|
|
1829
|
+
let streamError;
|
|
1834
1830
|
/**
|
|
1835
1831
|
* Registers a listener and keeps its remover, so one generation's listeners come off the session
|
|
1836
1832
|
* emitter again when that generation ends.
|
|
@@ -1838,98 +1834,73 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
1838
1834
|
function on(name, handler) {
|
|
1839
1835
|
unhooks.push(hooks.hook(name, handler));
|
|
1840
1836
|
}
|
|
1841
|
-
function
|
|
1842
|
-
|
|
1843
|
-
type: "agent:data",
|
|
1837
|
+
function emitEvent(type, data) {
|
|
1838
|
+
const event = {
|
|
1844
1839
|
jobId,
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1840
|
+
type,
|
|
1841
|
+
data,
|
|
1842
|
+
version: 1,
|
|
1843
|
+
timestamp: Date.now()
|
|
1844
|
+
};
|
|
1845
|
+
writes = writes.then(() => writer.write(event)).catch((error) => {
|
|
1846
|
+
streamError = error;
|
|
1850
1847
|
});
|
|
1851
1848
|
}
|
|
1852
1849
|
on("kubb:plugin:start", (ctx) => {
|
|
1853
|
-
|
|
1854
|
-
type: "kubb:plugin:start",
|
|
1855
|
-
data: [{ plugin: ctx.plugin }]
|
|
1856
|
-
});
|
|
1850
|
+
emitEvent("kubb:plugin:start", [{ plugin: { name: ctx.plugin.name } }]);
|
|
1857
1851
|
});
|
|
1858
1852
|
on("kubb:plugin:end", (ctx) => {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
success: ctx.success
|
|
1865
|
-
}]
|
|
1866
|
-
});
|
|
1853
|
+
emitEvent("kubb:plugin:end", [{
|
|
1854
|
+
plugin: { name: ctx.plugin.name },
|
|
1855
|
+
duration: ctx.duration,
|
|
1856
|
+
success: ctx.success
|
|
1857
|
+
}]);
|
|
1867
1858
|
});
|
|
1868
1859
|
on("kubb:build:start", ({ config, adapter }) => {
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
}]
|
|
1875
|
-
});
|
|
1860
|
+
root = config.root;
|
|
1861
|
+
emitEvent("kubb:build:start", [{
|
|
1862
|
+
config: { name: config.name },
|
|
1863
|
+
adapter: { name: adapter.name }
|
|
1864
|
+
}]);
|
|
1876
1865
|
});
|
|
1877
1866
|
on("kubb:build:end", ({ files, config, outputDir }) => {
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
outputDir
|
|
1886
|
-
}]
|
|
1887
|
-
});
|
|
1867
|
+
emitEvent("kubb:build:end", [{
|
|
1868
|
+
files: files.map((file) => ({
|
|
1869
|
+
path: relativeStoragePath(config.root, file.path),
|
|
1870
|
+
name: file.name
|
|
1871
|
+
})),
|
|
1872
|
+
outputDir
|
|
1873
|
+
}]);
|
|
1888
1874
|
});
|
|
1889
1875
|
on("kubb:files:processing:start", ({ files }) => {
|
|
1890
|
-
|
|
1891
|
-
type: "kubb:files:processing:start",
|
|
1892
|
-
data: [{ total: files.length }]
|
|
1893
|
-
});
|
|
1876
|
+
emitEvent("kubb:files:processing:start", [{ total: files.length }]);
|
|
1894
1877
|
});
|
|
1895
1878
|
on("kubb:files:processing:update", ({ files }) => {
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
percentage
|
|
1903
|
-
})) }]
|
|
1904
|
-
});
|
|
1879
|
+
emitEvent("kubb:files:processing:update", [{ files: files.map(({ file, processed, total, percentage }) => ({
|
|
1880
|
+
file: relativeStoragePath(root, file.path),
|
|
1881
|
+
processed,
|
|
1882
|
+
total,
|
|
1883
|
+
percentage
|
|
1884
|
+
})) }]);
|
|
1905
1885
|
});
|
|
1906
1886
|
on("kubb:files:processing:end", ({ files }) => {
|
|
1907
|
-
|
|
1908
|
-
type: "kubb:files:processing:end",
|
|
1909
|
-
data: [{ total: files.length }]
|
|
1910
|
-
});
|
|
1887
|
+
emitEvent("kubb:files:processing:end", [{ total: files.length }]);
|
|
1911
1888
|
});
|
|
1912
1889
|
for (const type of [
|
|
1913
1890
|
"kubb:info",
|
|
1914
1891
|
"kubb:success",
|
|
1915
1892
|
"kubb:warn"
|
|
1916
1893
|
]) on(type, ({ message, info }) => {
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
info
|
|
1922
|
-
}]
|
|
1923
|
-
});
|
|
1894
|
+
emitEvent(type, [{
|
|
1895
|
+
message,
|
|
1896
|
+
info
|
|
1897
|
+
}]);
|
|
1924
1898
|
});
|
|
1925
1899
|
on("kubb:generation:start", ({ config }) => {
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
plugins: config.plugins.length
|
|
1931
|
-
}]
|
|
1932
|
-
});
|
|
1900
|
+
emitEvent("kubb:generation:start", [{
|
|
1901
|
+
name: config.name,
|
|
1902
|
+
plugins: config.plugins.length
|
|
1903
|
+
}]);
|
|
1933
1904
|
});
|
|
1934
1905
|
on("kubb:generation:end", async ({ config, storage, diagnostics = [], status, hrStart, filesCreated }) => {
|
|
1935
1906
|
const { peerDependencies, missingDependencies } = await resolvePeerDependencies(config.plugins.map(({ name }) => name));
|
|
@@ -1942,29 +1913,32 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
1942
1913
|
peerDependencies,
|
|
1943
1914
|
missingDependencies
|
|
1944
1915
|
});
|
|
1945
|
-
|
|
1946
|
-
type: "kubb:generation:end",
|
|
1947
|
-
data: []
|
|
1948
|
-
});
|
|
1916
|
+
emitEvent("kubb:generation:end", []);
|
|
1949
1917
|
if (!hrStart) return;
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
status: status ?? "success"
|
|
1957
|
-
}]
|
|
1958
|
-
});
|
|
1918
|
+
emitEvent("kubb:generation:summary", [{
|
|
1919
|
+
duration: Math.round(getElapsedMs(hrStart)),
|
|
1920
|
+
fileCount: filesCreated ?? 0,
|
|
1921
|
+
failedPlugins: Diagnostics.failedPlugins(diagnostics).length,
|
|
1922
|
+
status: status ?? "success"
|
|
1923
|
+
}]);
|
|
1959
1924
|
});
|
|
1960
1925
|
on("kubb:error", ({ error }) => {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1926
|
+
emitEvent("kubb:error", [{
|
|
1927
|
+
message: error.message,
|
|
1928
|
+
stack: error.stack
|
|
1929
|
+
}]);
|
|
1930
|
+
});
|
|
1931
|
+
on("kubb:diagnostic", ({ diagnostic }) => {
|
|
1932
|
+
const cause = "cause" in diagnostic ? diagnostic.cause : void 0;
|
|
1933
|
+
emitEvent("kubb:diagnostic", [{
|
|
1934
|
+
code: diagnostic.code,
|
|
1935
|
+
message: diagnostic.message,
|
|
1936
|
+
severity: diagnostic.severity,
|
|
1937
|
+
location: "location" in diagnostic ? diagnostic.location : void 0,
|
|
1938
|
+
help: "help" in diagnostic ? diagnostic.help : void 0,
|
|
1939
|
+
plugin: "plugin" in diagnostic ? diagnostic.plugin : void 0,
|
|
1940
|
+
stack: cause?.stack
|
|
1941
|
+
}]);
|
|
1968
1942
|
});
|
|
1969
1943
|
for (const type of [
|
|
1970
1944
|
"kubb:lifecycle:start",
|
|
@@ -1976,62 +1950,144 @@ function setupEventsStream(ws, hooks, jobId, options = {}) {
|
|
|
1976
1950
|
"kubb:hooks:start",
|
|
1977
1951
|
"kubb:hooks:end"
|
|
1978
1952
|
]) on(type, () => {
|
|
1979
|
-
|
|
1980
|
-
type,
|
|
1981
|
-
data: []
|
|
1982
|
-
});
|
|
1953
|
+
emitEvent(type, []);
|
|
1983
1954
|
});
|
|
1984
1955
|
on("kubb:hook:start", ({ id, command, args }) => {
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
args: args ? [...args] : void 0
|
|
1991
|
-
}]
|
|
1992
|
-
});
|
|
1956
|
+
emitEvent("kubb:hook:start", [{
|
|
1957
|
+
id,
|
|
1958
|
+
command,
|
|
1959
|
+
args: args ? [...args] : void 0
|
|
1960
|
+
}]);
|
|
1993
1961
|
});
|
|
1994
1962
|
on("kubb:hook:line", ({ id, line }) => {
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
line
|
|
2000
|
-
}]
|
|
2001
|
-
});
|
|
1963
|
+
emitEvent("kubb:hook:line", [{
|
|
1964
|
+
id,
|
|
1965
|
+
line
|
|
1966
|
+
}]);
|
|
2002
1967
|
});
|
|
2003
1968
|
on("kubb:hook:end", ({ id, command, args, success, error }) => {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
} : void 0
|
|
2015
|
-
}]
|
|
2016
|
-
});
|
|
1969
|
+
emitEvent("kubb:hook:end", [{
|
|
1970
|
+
id,
|
|
1971
|
+
command,
|
|
1972
|
+
args: args ? [...args] : void 0,
|
|
1973
|
+
success,
|
|
1974
|
+
error: error ? {
|
|
1975
|
+
message: error.message,
|
|
1976
|
+
stack: error.stack
|
|
1977
|
+
} : void 0
|
|
1978
|
+
}]);
|
|
2017
1979
|
});
|
|
2018
|
-
|
|
1980
|
+
/**
|
|
1981
|
+
* Takes this generation's listeners off the session emitter. Safe to call twice.
|
|
1982
|
+
*/
|
|
1983
|
+
function detach() {
|
|
2019
1984
|
for (const unhook of unhooks) unhook();
|
|
1985
|
+
unhooks.length = 0;
|
|
1986
|
+
}
|
|
1987
|
+
async function close() {
|
|
1988
|
+
if (closed) return;
|
|
1989
|
+
closed = true;
|
|
1990
|
+
detach();
|
|
1991
|
+
await writes;
|
|
1992
|
+
if (streamError) return;
|
|
1993
|
+
await writer.close().catch(() => void 0);
|
|
1994
|
+
}
|
|
1995
|
+
function fail(error) {
|
|
1996
|
+
detach();
|
|
1997
|
+
if (closed) return;
|
|
1998
|
+
closed = true;
|
|
1999
|
+
writer.abort(error).catch(() => void 0);
|
|
2000
|
+
}
|
|
2001
|
+
return {
|
|
2002
|
+
stream: transform.readable,
|
|
2003
|
+
close,
|
|
2004
|
+
dispose: () => fail(),
|
|
2005
|
+
fail
|
|
2020
2006
|
};
|
|
2021
2007
|
}
|
|
2022
2008
|
//#endregion
|
|
2023
|
-
//#region src/
|
|
2009
|
+
//#region src/rpc.ts
|
|
2024
2010
|
/**
|
|
2025
|
-
*
|
|
2026
|
-
*
|
|
2011
|
+
* The only methods Studio may call on an agent. A `StudioSession` carries far more than
|
|
2012
|
+
* {@link AgentApi}, so it is wrapped rather than exposed: what Cap'n Web can reach is exactly what
|
|
2013
|
+
* this class re-declares.
|
|
2027
2014
|
*/
|
|
2028
|
-
|
|
2015
|
+
var AgentRpcTarget = class extends RpcTarget {
|
|
2016
|
+
api;
|
|
2017
|
+
constructor(api) {
|
|
2018
|
+
super();
|
|
2019
|
+
this.api = api;
|
|
2020
|
+
}
|
|
2021
|
+
connect() {
|
|
2022
|
+
return this.api.connect();
|
|
2023
|
+
}
|
|
2024
|
+
startGeneration(input) {
|
|
2025
|
+
return this.api.startGeneration(input);
|
|
2026
|
+
}
|
|
2027
|
+
saveConfig(input) {
|
|
2028
|
+
return this.api.saveConfig(input);
|
|
2029
|
+
}
|
|
2030
|
+
publishSnapshot(input) {
|
|
2031
|
+
return this.api.publishSnapshot(input);
|
|
2032
|
+
}
|
|
2033
|
+
readFiles(input) {
|
|
2034
|
+
return this.api.readFiles(input);
|
|
2035
|
+
}
|
|
2036
|
+
};
|
|
2037
|
+
/**
|
|
2038
|
+
* Opens an authenticated Cap'n Web session to Studio over a WebSocket. Rejects an unencrypted URL
|
|
2039
|
+
* before opening the socket, so a bearer token never reaches a plaintext host.
|
|
2040
|
+
*
|
|
2041
|
+
* @example
|
|
2042
|
+
* ```ts
|
|
2043
|
+
* const rpc = await connectWebSocketRpc({ url: 'wss://studio.kubb.dev/s/1', token, local: session })
|
|
2044
|
+
* await rpc.studio.ping()
|
|
2045
|
+
* ```
|
|
2046
|
+
*/
|
|
2047
|
+
const connectWebSocketRpc = async ({ url, token, local }) => {
|
|
2048
|
+
const { protocol, hostname, host } = new URL(url);
|
|
2049
|
+
if (protocol !== "wss:" && !(protocol === "ws:" && (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]"))) throw new Error(`Refusing unencrypted WebSocket to ${host}`);
|
|
2050
|
+
const socket = createWebsocket(url, { headers: { Authorization: `Bearer ${token}` } });
|
|
2051
|
+
const closed = new Promise((resolve) => socket.once("close", resolve));
|
|
2052
|
+
const studio = newWebSocketRpcSession(socket, new AgentRpcTarget(local));
|
|
2053
|
+
studio.onRpcBroken(() => socket.close());
|
|
2054
|
+
return {
|
|
2055
|
+
studio,
|
|
2056
|
+
closed,
|
|
2057
|
+
close: () => studio[Symbol.dispose]()
|
|
2058
|
+
};
|
|
2059
|
+
};
|
|
2060
|
+
//#endregion
|
|
2061
|
+
//#region src/StudioSession.ts
|
|
2029
2062
|
/**
|
|
2030
|
-
* How
|
|
2031
|
-
* on for this open. A Studio that predates the ack never sends one, so this only ever produces a
|
|
2032
|
-
* warning, not a reconnect.
|
|
2063
|
+
* How many files are read from storage at once when serving `readFiles` or packing a snapshot.
|
|
2033
2064
|
*/
|
|
2034
|
-
const
|
|
2065
|
+
const FILE_READ_CONCURRENCY = 50;
|
|
2066
|
+
var GenerationRunTarget = class extends RpcTarget {
|
|
2067
|
+
generationStream;
|
|
2068
|
+
generationResult;
|
|
2069
|
+
cancelGeneration;
|
|
2070
|
+
stopGeneration;
|
|
2071
|
+
constructor(generationStream, generationResult, cancelGeneration, stopGeneration) {
|
|
2072
|
+
super();
|
|
2073
|
+
this.generationStream = generationStream;
|
|
2074
|
+
this.generationResult = generationResult;
|
|
2075
|
+
this.cancelGeneration = cancelGeneration;
|
|
2076
|
+
this.stopGeneration = stopGeneration;
|
|
2077
|
+
}
|
|
2078
|
+
async events() {
|
|
2079
|
+
return this.generationStream;
|
|
2080
|
+
}
|
|
2081
|
+
result() {
|
|
2082
|
+
return this.generationResult;
|
|
2083
|
+
}
|
|
2084
|
+
cancel() {
|
|
2085
|
+
return this.cancelGeneration();
|
|
2086
|
+
}
|
|
2087
|
+
[Symbol.dispose]() {
|
|
2088
|
+
this.stopGeneration();
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2035
2091
|
/**
|
|
2036
2092
|
* Fills in a host's options: the hosted Studio URL, the current working directory, and every
|
|
2037
2093
|
* permission off unless granted. Idempotent, so a reconnect can pass an already-resolved bag
|
|
@@ -2071,7 +2127,7 @@ function reconnect(options) {
|
|
|
2071
2127
|
const timer = setTimeout(() => {
|
|
2072
2128
|
signal?.removeEventListener("abort", cancel);
|
|
2073
2129
|
if (signal?.aborted) return;
|
|
2074
|
-
new StudioSession(options).
|
|
2130
|
+
new StudioSession(options).start().catch((error) => {
|
|
2075
2131
|
if (logLevel$1 !== void 0 && logLevel$1 > logLevel.silent) console.error(styleText("red", `Reconnect attempt to Kubb Studio failed: ${getErrorMessage(error)}`));
|
|
2076
2132
|
if (error instanceof InvalidAgentTokenError) {
|
|
2077
2133
|
onTokenRejected?.(error);
|
|
@@ -2083,8 +2139,8 @@ function reconnect(options) {
|
|
|
2083
2139
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
2084
2140
|
}
|
|
2085
2141
|
/**
|
|
2086
|
-
* One
|
|
2087
|
-
*
|
|
2142
|
+
* One agent-to-Studio RPC transport: opening it, keeping it alive, and serving remote methods.
|
|
2143
|
+
* `createClient` opens one per pool slot and is the only caller.
|
|
2088
2144
|
*/
|
|
2089
2145
|
var StudioSession = class {
|
|
2090
2146
|
#options;
|
|
@@ -2099,17 +2155,20 @@ var StudioSession = class {
|
|
|
2099
2155
|
* Before it resolves there is nothing to disconnect and no sandbox flag to read.
|
|
2100
2156
|
*/
|
|
2101
2157
|
#session;
|
|
2102
|
-
#
|
|
2158
|
+
#rpc;
|
|
2103
2159
|
#studioVersion;
|
|
2104
|
-
#activeJobId = null;
|
|
2105
2160
|
#disposed = false;
|
|
2106
2161
|
#isGenerating = false;
|
|
2107
2162
|
#heartbeatTimer;
|
|
2108
|
-
#readyTimer;
|
|
2109
|
-
#lastPongAt = Date.now();
|
|
2110
2163
|
#lastGeneration;
|
|
2164
|
+
/**
|
|
2165
|
+
* Resolves when Studio calls {@link StudioSession.connect}. `studio:ready` waits on this so the
|
|
2166
|
+
* host does not queue jobs before the agent session is registered.
|
|
2167
|
+
*/
|
|
2168
|
+
#connectAck = Promise.withResolvers();
|
|
2111
2169
|
constructor(options) {
|
|
2112
2170
|
this.#options = applyStudioDefaults(options);
|
|
2171
|
+
this.#connectAck.promise.catch(() => {});
|
|
2113
2172
|
}
|
|
2114
2173
|
/**
|
|
2115
2174
|
* A sandbox agent runs on Studio's own infrastructure, so it has no user project to touch.
|
|
@@ -2136,7 +2195,7 @@ var StudioSession = class {
|
|
|
2136
2195
|
get #canRead() {
|
|
2137
2196
|
return this.#isSandbox || this.#options.permissions.allowRead;
|
|
2138
2197
|
}
|
|
2139
|
-
async
|
|
2198
|
+
async start() {
|
|
2140
2199
|
const { token, studioUrl, signal, heartbeatInterval, installLogger } = this.#options;
|
|
2141
2200
|
await installLogger?.(this.#hooks);
|
|
2142
2201
|
try {
|
|
@@ -2147,48 +2206,65 @@ var StudioSession = class {
|
|
|
2147
2206
|
});
|
|
2148
2207
|
this.#session = session;
|
|
2149
2208
|
this.#studioVersion = session.version;
|
|
2150
|
-
const
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
this.#
|
|
2209
|
+
const rpc = await (this.#options.connector ?? connectWebSocketRpc)({
|
|
2210
|
+
url: session.url,
|
|
2211
|
+
token,
|
|
2212
|
+
local: this
|
|
2213
|
+
});
|
|
2214
|
+
this.#rpc = rpc;
|
|
2215
|
+
rpc.closed.then(this.#onClose);
|
|
2156
2216
|
signal?.addEventListener("abort", this.#onAbort, { once: true });
|
|
2157
2217
|
this.#unhooks.push(() => signal?.removeEventListener("abort", this.#onAbort));
|
|
2158
|
-
this.#
|
|
2159
|
-
this.#
|
|
2218
|
+
this.#scheduleHeartbeat(heartbeatInterval);
|
|
2219
|
+
await this.#hooks.callHook("studio:connected", {
|
|
2220
|
+
url: studioUrl,
|
|
2221
|
+
versions: {
|
|
2222
|
+
studio: this.#studioVersion,
|
|
2223
|
+
kubb: version,
|
|
2224
|
+
agent: this.#options.version
|
|
2225
|
+
}
|
|
2226
|
+
});
|
|
2227
|
+
await this.#connectAck.promise;
|
|
2228
|
+
await this.#hooks.callHook("studio:ready", {});
|
|
2160
2229
|
} catch (error) {
|
|
2230
|
+
this.#disposed = true;
|
|
2231
|
+
this.dispose();
|
|
2161
2232
|
await this.#hooks.callHook("studio:error", { error: toError(error) });
|
|
2162
2233
|
if (error instanceof InvalidAgentTokenError) throw error;
|
|
2163
2234
|
reconnect(this.#options);
|
|
2164
2235
|
}
|
|
2165
2236
|
}
|
|
2166
|
-
/**
|
|
2167
|
-
* Adds a socket listener and tracks its remover, so `dispose` detaches every listener at once.
|
|
2168
|
-
*/
|
|
2169
|
-
#listen(ws, event, listener) {
|
|
2170
|
-
ws.addEventListener(event, listener);
|
|
2171
|
-
this.#unhooks.push(() => ws.removeEventListener(event, listener));
|
|
2172
|
-
}
|
|
2173
2237
|
#warn(message) {
|
|
2174
2238
|
return this.#hooks.callHook("studio:warn", { message });
|
|
2175
2239
|
}
|
|
2176
2240
|
/**
|
|
2177
|
-
*
|
|
2178
|
-
*
|
|
2241
|
+
* Declines a request: logs why locally, then tells Studio. The two wordings differ on purpose,
|
|
2242
|
+
* since the log names the request that was ignored and the error names what the caller can do.
|
|
2179
2243
|
*/
|
|
2180
|
-
#
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2244
|
+
async #refuse(reason, message) {
|
|
2245
|
+
await this.#warn(reason);
|
|
2246
|
+
throw new Error(message);
|
|
2247
|
+
}
|
|
2248
|
+
#scheduleHeartbeat(interval) {
|
|
2249
|
+
const rpc = this.#rpc;
|
|
2250
|
+
if (!rpc) return;
|
|
2251
|
+
this.#heartbeatTimer = setTimeout(async () => {
|
|
2252
|
+
try {
|
|
2253
|
+
await this.#ping(rpc);
|
|
2254
|
+
} catch {
|
|
2255
|
+
if (this.#rpc === rpc) rpc.close();
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
if (this.#rpc === rpc) this.#scheduleHeartbeat(interval);
|
|
2259
|
+
}, interval);
|
|
2260
|
+
}
|
|
2261
|
+
/**
|
|
2262
|
+
* Races `studio.ping()` against a deadline, so a half-open socket can't hang it forever.
|
|
2263
|
+
* */
|
|
2264
|
+
#ping(rpc) {
|
|
2265
|
+
const { promise: timedOut, reject: onTimeout } = Promise.withResolvers();
|
|
2266
|
+
const timer = setTimeout(() => onTimeout(/* @__PURE__ */ new Error("Heartbeat ping timed out")), agentDefaults.heartbeatTimeoutMs);
|
|
2267
|
+
return Promise.race([rpc.studio.ping(), timedOut]).finally(() => clearTimeout(timer));
|
|
2192
2268
|
}
|
|
2193
2269
|
/**
|
|
2194
2270
|
* Reads `kubb.config.ts` and reports which plugin options Studio may edit.
|
|
@@ -2205,90 +2281,48 @@ var StudioSession = class {
|
|
|
2205
2281
|
return;
|
|
2206
2282
|
}
|
|
2207
2283
|
}
|
|
2208
|
-
async
|
|
2284
|
+
async connect() {
|
|
2209
2285
|
const { configPath, root, version: version$1, loadConfig, permissions } = this.#options;
|
|
2210
|
-
|
|
2211
|
-
const
|
|
2212
|
-
sendAgentMessage(this.#ws, {
|
|
2213
|
-
type: "agent:connect",
|
|
2214
|
-
payload: {
|
|
2215
|
-
versions: {
|
|
2216
|
-
kubb: version,
|
|
2217
|
-
agent: version$1
|
|
2218
|
-
},
|
|
2219
|
-
root,
|
|
2220
|
-
config: {
|
|
2221
|
-
path: configPath,
|
|
2222
|
-
file: await this.#readConfigFileView(),
|
|
2223
|
-
plugins: config.plugins.map((plugin) => ({
|
|
2224
|
-
name: toPackageName(plugin.name),
|
|
2225
|
-
options: plugin.options ?? {}
|
|
2226
|
-
}))
|
|
2227
|
-
},
|
|
2228
|
-
permissions: {
|
|
2229
|
-
...permissions,
|
|
2230
|
-
allowWrite: this.#canWrite,
|
|
2231
|
-
allowInput: this.#canUseInput,
|
|
2232
|
-
allowConfigEdit: this.#canEditConfig,
|
|
2233
|
-
allowRead: this.#canRead
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
});
|
|
2237
|
-
}
|
|
2238
|
-
async #handleOpen() {
|
|
2239
|
-
this.#lastPongAt = Date.now();
|
|
2240
|
-
await this.#hooks.callHook("studio:connected", {
|
|
2241
|
-
url: this.#options.studioUrl,
|
|
2286
|
+
const [config, file] = await Promise.all([loadConfig(), this.#readConfigFileView()]);
|
|
2287
|
+
const payload = {
|
|
2242
2288
|
versions: {
|
|
2243
|
-
studio: this.#studioVersion,
|
|
2244
2289
|
kubb: version,
|
|
2245
|
-
agent:
|
|
2290
|
+
agent: version$1
|
|
2291
|
+
},
|
|
2292
|
+
root,
|
|
2293
|
+
config: {
|
|
2294
|
+
path: configPath,
|
|
2295
|
+
file,
|
|
2296
|
+
plugins: config.plugins.map((plugin) => ({
|
|
2297
|
+
name: toPackageName(plugin.name),
|
|
2298
|
+
options: plugin.options ?? {}
|
|
2299
|
+
}))
|
|
2300
|
+
},
|
|
2301
|
+
permissions: {
|
|
2302
|
+
...permissions,
|
|
2303
|
+
allowWrite: this.#canWrite,
|
|
2304
|
+
allowInput: this.#canUseInput,
|
|
2305
|
+
allowConfigEdit: this.#canEditConfig,
|
|
2306
|
+
allowRead: this.#canRead
|
|
2246
2307
|
}
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
this.#waitForReady();
|
|
2251
|
-
} catch (error) {
|
|
2252
|
-
await this.#warn(`Failed to send the connect payload: ${getErrorMessage(error)}`);
|
|
2253
|
-
}
|
|
2308
|
+
};
|
|
2309
|
+
this.#connectAck.resolve();
|
|
2310
|
+
return payload;
|
|
2254
2311
|
}
|
|
2255
|
-
|
|
2256
|
-
* Arms the timeout for Studio's `studio:ready` acknowledgement. Re-armed on every open, since a
|
|
2257
|
-
* command sent while the agent is not attached is lost either way (see `#sendConnectedPayload`),
|
|
2258
|
-
* so each reconnect needs its own fresh wait.
|
|
2259
|
-
*/
|
|
2260
|
-
#waitForReady() {
|
|
2261
|
-
if (this.#disposed) return;
|
|
2262
|
-
clearTimeout(this.#readyTimer);
|
|
2263
|
-
this.#readyTimer = setTimeout(() => {
|
|
2264
|
-
this.#readyTimer = void 0;
|
|
2265
|
-
Promise.resolve(this.#warn(`Kubb Studio did not confirm the connection was ready within ${READY_TIMEOUT_MS}ms`)).catch(() => {});
|
|
2266
|
-
}, READY_TIMEOUT_MS);
|
|
2267
|
-
}
|
|
2268
|
-
#onOpen = () => void this.#handleOpen().catch(() => {});
|
|
2269
|
-
#onAbort = () => void this.#end({
|
|
2270
|
-
reason: "shutdown",
|
|
2271
|
-
retry: false
|
|
2272
|
-
});
|
|
2312
|
+
#onAbort = () => void this.#end({ retry: false });
|
|
2273
2313
|
#onClose = () => void this.#end({ retry: true });
|
|
2274
|
-
#onError = () => {
|
|
2275
|
-
this.#hooks.callHook("studio:error", { error: /* @__PURE__ */ new Error("Failed to connect to Kubb Studio") });
|
|
2276
|
-
this.#onClose();
|
|
2277
|
-
};
|
|
2278
2314
|
/**
|
|
2279
2315
|
* Drops the socket and detaches every listener and timer this session added. Idempotent, and
|
|
2280
2316
|
* safe before `connect` opened anything.
|
|
2281
2317
|
*
|
|
2282
2318
|
* @internal
|
|
2283
2319
|
*/
|
|
2284
|
-
dispose(
|
|
2285
|
-
|
|
2320
|
+
dispose() {
|
|
2321
|
+
clearTimeout(this.#heartbeatTimer);
|
|
2286
2322
|
this.#heartbeatTimer = void 0;
|
|
2287
|
-
|
|
2288
|
-
this.#
|
|
2289
|
-
|
|
2290
|
-
this.#ws?.close(1e3, reason);
|
|
2291
|
-
} catch {}
|
|
2323
|
+
this.#rpc?.close();
|
|
2324
|
+
this.#rpc = void 0;
|
|
2325
|
+
this.#connectAck.reject(/* @__PURE__ */ new Error("Session ended before Studio called connect()"));
|
|
2292
2326
|
for (const unhook of this.#unhooks) unhook();
|
|
2293
2327
|
this.#unhooks.length = 0;
|
|
2294
2328
|
}
|
|
@@ -2296,15 +2330,12 @@ var StudioSession = class {
|
|
|
2296
2330
|
* Ends the session: tells Studio it is over, drops the socket, and optionally reconnects.
|
|
2297
2331
|
* `#disposed` keeps the close event from running this twice, and a shutdown from reconnecting.
|
|
2298
2332
|
*/
|
|
2299
|
-
async #end({
|
|
2333
|
+
async #end({ retry }) {
|
|
2300
2334
|
const { studioUrl, token, logLevel } = this.#options;
|
|
2301
2335
|
if (this.#disposed) return;
|
|
2302
2336
|
this.#disposed = true;
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
reason: "shutdown"
|
|
2306
|
-
});
|
|
2307
|
-
this.dispose(reason);
|
|
2337
|
+
this.dispose();
|
|
2338
|
+
await this.#hooks.callHook("studio:disconnected", { reason: retry ? "connection closed" : "shutdown" });
|
|
2308
2339
|
if (this.#session) await disconnect({
|
|
2309
2340
|
sessionId: this.#session.sessionId,
|
|
2310
2341
|
studioUrl,
|
|
@@ -2314,81 +2345,35 @@ var StudioSession = class {
|
|
|
2314
2345
|
}).catch(() => {});
|
|
2315
2346
|
if (retry) reconnect(this.#options);
|
|
2316
2347
|
}
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
return;
|
|
2337
|
-
}
|
|
2338
|
-
await this.#warn(`Ignored an unknown message from Kubb Studio: ${data.type}`);
|
|
2339
|
-
} catch (error) {
|
|
2340
|
-
await this.#hooks.callHook("studio:error", { error: toError(error) });
|
|
2341
|
-
await this.#emitError(toError(error));
|
|
2342
|
-
}
|
|
2343
|
-
};
|
|
2344
|
-
/**
|
|
2345
|
-
* Studio ended the session itself. A revoked one stays ended, an expired one gets a fresh
|
|
2346
|
-
* session, and anything else is left to the socket's own close event.
|
|
2347
|
-
*/
|
|
2348
|
-
async #handleDisconnect(reason) {
|
|
2349
|
-
await this.#hooks.callHook("studio:disconnected", { reason });
|
|
2350
|
-
if (reason !== "revoked" && reason !== "expired") return;
|
|
2351
|
-
this.#disposed = true;
|
|
2352
|
-
this.dispose(`session_${reason}`);
|
|
2353
|
-
if (reason === "expired") reconnect(this.#options);
|
|
2354
|
-
}
|
|
2355
|
-
async #handleCommand(data) {
|
|
2356
|
-
const ws = this.#ws;
|
|
2357
|
-
if (!ws) return;
|
|
2358
|
-
const command = data.type.slice(7);
|
|
2359
|
-
await this.#hooks.callHook("studio:command:start", { command });
|
|
2360
|
-
switch (data.type) {
|
|
2361
|
-
case "studio:generate":
|
|
2362
|
-
await this.#handleGenerate(ws, data, command);
|
|
2363
|
-
return;
|
|
2364
|
-
case "studio:connect":
|
|
2365
|
-
this.#studioVersion = data.version ?? this.#studioVersion;
|
|
2366
|
-
await this.#sendConnectedPayload();
|
|
2367
|
-
await this.#hooks.callHook("studio:command:end", { command });
|
|
2368
|
-
return;
|
|
2369
|
-
case "studio:save":
|
|
2370
|
-
await this.#handleSave(ws, data, command);
|
|
2371
|
-
return;
|
|
2372
|
-
case "studio:snapshot":
|
|
2373
|
-
await this.#handleSnapshot(ws, data, command);
|
|
2374
|
-
return;
|
|
2375
|
-
case "studio:files":
|
|
2376
|
-
await this.#handleFiles(ws, data, command);
|
|
2377
|
-
return;
|
|
2378
|
-
}
|
|
2348
|
+
startGeneration(data) {
|
|
2349
|
+
const generationStream = createGenerationStream(this.#hooks, data.jobId, { onGenerationEnd: (result) => {
|
|
2350
|
+
this.#lastGeneration = result;
|
|
2351
|
+
} });
|
|
2352
|
+
const controller = new AbortController();
|
|
2353
|
+
const result = this.#runGeneration(data, controller).then(async (value) => {
|
|
2354
|
+
await generationStream.close();
|
|
2355
|
+
return value;
|
|
2356
|
+
}).catch((error) => {
|
|
2357
|
+
generationStream.fail(error);
|
|
2358
|
+
throw error;
|
|
2359
|
+
});
|
|
2360
|
+
result.catch(() => {});
|
|
2361
|
+
return new GenerationRunTarget(generationStream.stream, result, async () => {
|
|
2362
|
+
controller.abort(/* @__PURE__ */ new Error("Generation canceled"));
|
|
2363
|
+
}, () => {
|
|
2364
|
+
controller.abort(/* @__PURE__ */ new Error("Generation canceled"));
|
|
2365
|
+
generationStream.dispose();
|
|
2366
|
+
});
|
|
2379
2367
|
}
|
|
2380
|
-
async #
|
|
2381
|
-
|
|
2382
|
-
if (this.#isGenerating) {
|
|
2383
|
-
await this.#warn("Ignored generate: a generation is already in progress");
|
|
2384
|
-
await this.#emitError(/* @__PURE__ */ new Error("A generation is already in progress, please wait for it to finish"));
|
|
2385
|
-
return;
|
|
2386
|
-
}
|
|
2368
|
+
async #runGeneration(data, controller) {
|
|
2369
|
+
if (this.#isGenerating) return this.#refuse("Ignored generate: a generation is already in progress", "A generation is already in progress, please wait for it to finish");
|
|
2387
2370
|
this.#isGenerating = true;
|
|
2388
|
-
|
|
2371
|
+
const command = "generate";
|
|
2372
|
+
const { root, loadConfig, permissions, client } = this.#options;
|
|
2389
2373
|
try {
|
|
2374
|
+
await this.#hooks.callHook("studio:command:start", { command });
|
|
2390
2375
|
const config = await loadConfig();
|
|
2391
|
-
const patch = data.
|
|
2376
|
+
const patch = data.config;
|
|
2392
2377
|
const plugins = await mergePlugins(config.plugins, patch?.plugins);
|
|
2393
2378
|
const adapter = await mergeAdapter(config.adapter, patch?.adapter);
|
|
2394
2379
|
const inputOverride = this.#isSandbox ? patch?.input ?? "" : permissions.allowInput && patch?.input || void 0;
|
|
@@ -2399,9 +2384,7 @@ var StudioSession = class {
|
|
|
2399
2384
|
}
|
|
2400
2385
|
const resolvedPlugins = plugins ?? config.plugins;
|
|
2401
2386
|
this.#lastGeneration = void 0;
|
|
2402
|
-
const detach = [setupHookListener(this.#hooks, root
|
|
2403
|
-
this.#lastGeneration = result;
|
|
2404
|
-
} })];
|
|
2387
|
+
const detach = [setupHookListener(this.#hooks, root, controller.signal)];
|
|
2405
2388
|
try {
|
|
2406
2389
|
await generate({
|
|
2407
2390
|
config: {
|
|
@@ -2418,7 +2401,8 @@ var StudioSession = class {
|
|
|
2418
2401
|
plugins: resolvedPlugins,
|
|
2419
2402
|
adapter
|
|
2420
2403
|
},
|
|
2421
|
-
hooks: this.#hooks
|
|
2404
|
+
hooks: this.#hooks,
|
|
2405
|
+
signal: controller.signal
|
|
2422
2406
|
});
|
|
2423
2407
|
} finally {
|
|
2424
2408
|
for (const remove of detach) remove();
|
|
@@ -2427,102 +2411,70 @@ var StudioSession = class {
|
|
|
2427
2411
|
command,
|
|
2428
2412
|
info: `${resolvedPlugins.length} plugin${resolvedPlugins.length === 1 ? "" : "s"}, ${this.#canWrite ? "written to disk" : "in memory"}${inputOverride !== void 0 ? ", from a Studio spec" : ""}`
|
|
2429
2413
|
});
|
|
2414
|
+
const files = [...this.#lastGeneration?.paths ?? []];
|
|
2415
|
+
return {
|
|
2416
|
+
status: "success",
|
|
2417
|
+
files,
|
|
2418
|
+
fileCount: files.length
|
|
2419
|
+
};
|
|
2430
2420
|
} finally {
|
|
2431
2421
|
this.#isGenerating = false;
|
|
2432
|
-
this.#activeJobId = null;
|
|
2433
2422
|
}
|
|
2434
2423
|
}
|
|
2435
|
-
async
|
|
2424
|
+
async saveConfig(data) {
|
|
2425
|
+
const command = "saveConfig";
|
|
2426
|
+
await this.#hooks.callHook("studio:command:start", { command });
|
|
2436
2427
|
const { configPath, configFile } = this.#options;
|
|
2437
2428
|
if (!Array.isArray(data.edits)) {
|
|
2438
2429
|
await this.#warn("Ignored save: the message carried no edits");
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
outcomes: [],
|
|
2444
|
-
changed: false
|
|
2445
|
-
}
|
|
2446
|
-
});
|
|
2447
|
-
return;
|
|
2430
|
+
return {
|
|
2431
|
+
outcomes: [],
|
|
2432
|
+
changed: false
|
|
2433
|
+
};
|
|
2448
2434
|
}
|
|
2449
2435
|
const edits = data.edits;
|
|
2450
|
-
const refuse = (reason) =>
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
reason
|
|
2458
|
-
})),
|
|
2459
|
-
changed: false
|
|
2460
|
-
}
|
|
2436
|
+
const refuse = (reason) => ({
|
|
2437
|
+
outcomes: edits.map((edit) => ({
|
|
2438
|
+
edit,
|
|
2439
|
+
applied: false,
|
|
2440
|
+
reason
|
|
2441
|
+
})),
|
|
2442
|
+
changed: false
|
|
2461
2443
|
});
|
|
2462
2444
|
if (!this.#canEditConfig) {
|
|
2463
2445
|
await this.#warn("Ignored save: editing kubb.config.ts was not granted");
|
|
2464
|
-
refuse("the agent was not granted permission to edit kubb.config.ts");
|
|
2465
|
-
return;
|
|
2466
|
-
}
|
|
2467
|
-
if (this.#isGenerating) {
|
|
2468
|
-
refuse("a generation is in progress");
|
|
2469
|
-
return;
|
|
2446
|
+
return refuse("the agent was not granted permission to edit kubb.config.ts");
|
|
2470
2447
|
}
|
|
2448
|
+
if (this.#isGenerating) return refuse("a generation is in progress");
|
|
2471
2449
|
try {
|
|
2472
2450
|
const { source: patched, outcomes, changed } = applyConfigEdits(await read(configFile), edits);
|
|
2473
2451
|
if (changed) await writeFile(configFile, patched, "utf-8");
|
|
2474
|
-
sendAgentMessage(ws, {
|
|
2475
|
-
type: "agent:save",
|
|
2476
|
-
jobId: data.jobId,
|
|
2477
|
-
payload: {
|
|
2478
|
-
outcomes,
|
|
2479
|
-
changed,
|
|
2480
|
-
file: changed ? await this.#readConfigFileView(patched) : void 0
|
|
2481
|
-
}
|
|
2482
|
-
});
|
|
2483
2452
|
const applied = outcomes.filter((outcome) => outcome.applied).length;
|
|
2484
2453
|
await this.#hooks.callHook("studio:command:end", {
|
|
2485
2454
|
command,
|
|
2486
2455
|
info: `applied ${applied}/${outcomes.length} edits to ${configPath}`
|
|
2487
2456
|
});
|
|
2457
|
+
return {
|
|
2458
|
+
outcomes,
|
|
2459
|
+
changed,
|
|
2460
|
+
file: changed ? await this.#readConfigFileView(patched) : void 0
|
|
2461
|
+
};
|
|
2488
2462
|
} catch (error) {
|
|
2489
2463
|
await this.#hooks.callHook("studio:error", { error: toError(error) });
|
|
2490
|
-
refuse(getErrorMessage(error));
|
|
2464
|
+
return refuse(getErrorMessage(error));
|
|
2491
2465
|
}
|
|
2492
2466
|
}
|
|
2493
|
-
async
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
message
|
|
2500
|
-
}
|
|
2501
|
-
});
|
|
2502
|
-
if (this.#isSandbox) {
|
|
2503
|
-
await this.#warn("Ignored snapshot: a sandbox agent has no project to build a package from");
|
|
2504
|
-
refuse("a sandbox agent has no project to build a package from");
|
|
2505
|
-
return;
|
|
2506
|
-
}
|
|
2507
|
-
const { name, version, bundledDependencies, uploadPath } = data.payload;
|
|
2508
|
-
if (!name || !version || !uploadPath) {
|
|
2509
|
-
await this.#warn("Ignored snapshot: the message was missing required fields");
|
|
2510
|
-
refuse("the message was missing required fields");
|
|
2511
|
-
return;
|
|
2512
|
-
}
|
|
2467
|
+
async publishSnapshot(data) {
|
|
2468
|
+
const command = "snapshot";
|
|
2469
|
+
await this.#hooks.callHook("studio:command:start", { command });
|
|
2470
|
+
if (this.#isSandbox) return this.#refuse("Ignored snapshot: a sandbox agent has no project to build a package from", "A sandbox agent has no project to build a package from");
|
|
2471
|
+
const { name, version, bundledDependencies, uploadPath } = data;
|
|
2472
|
+
if (!name || !version || !uploadPath) return this.#refuse("Ignored snapshot: the message was missing required fields", "The request was missing required fields");
|
|
2513
2473
|
const generation = this.#lastGeneration;
|
|
2514
|
-
if (!generation)
|
|
2515
|
-
await this.#warn("Ignored snapshot: no prior generation to pack");
|
|
2516
|
-
refuse("no prior generation exists to pack, run a generation first");
|
|
2517
|
-
return;
|
|
2518
|
-
}
|
|
2474
|
+
if (!generation) return this.#refuse("Ignored snapshot: no prior generation to pack", "No prior generation exists to pack, run a generation first");
|
|
2519
2475
|
const bundled = new Set(bundledDependencies ?? []);
|
|
2520
2476
|
const missing = generation.missingDependencies.filter((dependency) => !bundled.has(dependency));
|
|
2521
|
-
if (missing.length) {
|
|
2522
|
-
await this.#warn(`Ignored snapshot: missing dependencies: ${missing.join(", ")}`);
|
|
2523
|
-
refuse(`missing dependencies: ${missing.join(", ")}`);
|
|
2524
|
-
return;
|
|
2525
|
-
}
|
|
2477
|
+
if (missing.length) return this.#refuse(`Ignored snapshot: missing dependencies: ${missing.join(", ")}`, `Missing dependencies: ${missing.join(", ")}`);
|
|
2526
2478
|
try {
|
|
2527
2479
|
const files = {};
|
|
2528
2480
|
await inParallel({
|
|
@@ -2539,68 +2491,50 @@ var StudioSession = class {
|
|
|
2539
2491
|
peerDependencies: generation.peerDependencies
|
|
2540
2492
|
});
|
|
2541
2493
|
const { token, studioUrl } = this.#options;
|
|
2542
|
-
const
|
|
2494
|
+
const uploadUrl = new URL(uploadPath, studioUrl);
|
|
2495
|
+
if (uploadUrl.origin !== new URL(studioUrl).origin) throw new Error("Snapshot upload path must stay on the Studio origin");
|
|
2496
|
+
const redirect = await fetch(uploadUrl, {
|
|
2543
2497
|
method: "PUT",
|
|
2544
2498
|
headers: { Authorization: `Bearer ${token}` },
|
|
2545
2499
|
redirect: "manual"
|
|
2546
2500
|
});
|
|
2547
2501
|
const storageUrl = redirect.headers.get("location");
|
|
2548
2502
|
if (redirect.status !== 307 || !storageUrl) throw new Error(`Studio did not provide a storage URL (status ${redirect.status})`);
|
|
2549
|
-
const
|
|
2503
|
+
const storage = new URL(storageUrl);
|
|
2504
|
+
if (storage.protocol !== "https:" && storage.hostname !== "localhost" && storage.hostname !== "127.0.0.1") throw new Error(`Refusing snapshot upload to ${storage.origin}`);
|
|
2505
|
+
const response = await fetch(storage, {
|
|
2550
2506
|
method: "PUT",
|
|
2551
|
-
body: new Uint8Array(bytes)
|
|
2507
|
+
body: new Uint8Array(bytes),
|
|
2508
|
+
redirect: "error"
|
|
2552
2509
|
});
|
|
2553
2510
|
if (!response.ok) throw new Error(`Snapshot upload failed with status ${response.status}`);
|
|
2554
|
-
sendAgentMessage(ws, {
|
|
2555
|
-
type: "agent:snapshot",
|
|
2556
|
-
jobId: data.jobId,
|
|
2557
|
-
payload: {
|
|
2558
|
-
status: "ok",
|
|
2559
|
-
integrity,
|
|
2560
|
-
peerDependencies: generation.peerDependencies
|
|
2561
|
-
}
|
|
2562
|
-
});
|
|
2563
2511
|
await this.#hooks.callHook("studio:command:end", {
|
|
2564
2512
|
command,
|
|
2565
2513
|
info: `packed ${Object.keys(files).length} file${Object.keys(files).length === 1 ? "" : "s"}`
|
|
2566
2514
|
});
|
|
2515
|
+
return {
|
|
2516
|
+
integrity,
|
|
2517
|
+
peerDependencies: generation.peerDependencies
|
|
2518
|
+
};
|
|
2567
2519
|
} catch (error) {
|
|
2568
2520
|
await this.#hooks.callHook("studio:error", { error: toError(error) });
|
|
2569
|
-
|
|
2521
|
+
throw error;
|
|
2570
2522
|
}
|
|
2571
2523
|
}
|
|
2572
|
-
async
|
|
2524
|
+
async readFiles(data) {
|
|
2525
|
+
const command = "readFiles";
|
|
2526
|
+
await this.#hooks.callHook("studio:command:start", { command });
|
|
2573
2527
|
const { client } = this.#options;
|
|
2574
|
-
const refuse = (message) => sendAgentMessage(ws, {
|
|
2575
|
-
type: "agent:files",
|
|
2576
|
-
jobId: data.jobId,
|
|
2577
|
-
payload: {
|
|
2578
|
-
status: "error",
|
|
2579
|
-
message
|
|
2580
|
-
}
|
|
2581
|
-
});
|
|
2582
2528
|
if (!this.#canRead) {
|
|
2583
2529
|
await this.#warn("Ignored files: reading generated files was not granted");
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
}
|
|
2587
|
-
if (!Array.isArray(data.payload?.paths)) {
|
|
2588
|
-
await this.#warn("Ignored files: the message carried no paths");
|
|
2589
|
-
refuse("the message carried no paths");
|
|
2590
|
-
return;
|
|
2591
|
-
}
|
|
2592
|
-
const { paths } = data.payload;
|
|
2593
|
-
if (paths.length > 50) {
|
|
2594
|
-
await this.#warn(`Ignored files: requested ${paths.length} paths, more than the 50 allowed per request`);
|
|
2595
|
-
refuse(`at most 50 paths may be requested at once`);
|
|
2596
|
-
return;
|
|
2530
|
+
const remedy = client?.kind === "cli" ? "--allow-read, or answer yes when kubb studio asks," : "KUBB_AGENT_ALLOW_READ=true";
|
|
2531
|
+
throw new Error(`The agent was not granted permission to read generated files; set ${remedy} to allow it`);
|
|
2597
2532
|
}
|
|
2533
|
+
if (!Array.isArray(data.paths)) return this.#refuse("Ignored files: the message carried no paths", "The request carried no paths");
|
|
2534
|
+
const { paths } = data;
|
|
2535
|
+
if (paths.length > 50) return this.#refuse(`Ignored files: requested ${paths.length} paths, more than the 50 allowed per request`, `At most 50 paths may be requested at once`);
|
|
2598
2536
|
const generation = this.#lastGeneration;
|
|
2599
|
-
if (!generation)
|
|
2600
|
-
await this.#warn("Ignored files: no prior generation to read from");
|
|
2601
|
-
refuse("no prior generation to read from, run a generation first");
|
|
2602
|
-
return;
|
|
2603
|
-
}
|
|
2537
|
+
if (!generation) return this.#refuse("Ignored files: no prior generation to read from", "No prior generation to read from, run a generation first");
|
|
2604
2538
|
const requested = paths.filter((path) => generation.paths.has(path));
|
|
2605
2539
|
const files = {};
|
|
2606
2540
|
await inParallel({
|
|
@@ -2611,18 +2545,11 @@ var StudioSession = class {
|
|
|
2611
2545
|
if (content !== null) files[path] = content;
|
|
2612
2546
|
}
|
|
2613
2547
|
});
|
|
2614
|
-
sendAgentMessage(ws, {
|
|
2615
|
-
type: "agent:files",
|
|
2616
|
-
jobId: data.jobId,
|
|
2617
|
-
payload: {
|
|
2618
|
-
status: "ok",
|
|
2619
|
-
files
|
|
2620
|
-
}
|
|
2621
|
-
});
|
|
2622
2548
|
await this.#hooks.callHook("studio:command:end", {
|
|
2623
2549
|
command,
|
|
2624
2550
|
info: `read ${Object.keys(files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`
|
|
2625
2551
|
});
|
|
2552
|
+
return { files };
|
|
2626
2553
|
}
|
|
2627
2554
|
};
|
|
2628
2555
|
//#endregion
|
|
@@ -2659,7 +2586,7 @@ function createClient({ storage, onAuthRequired, ...options }) {
|
|
|
2659
2586
|
...options,
|
|
2660
2587
|
signal: controller.signal,
|
|
2661
2588
|
onTokenRejected: notifyAuthRequired
|
|
2662
|
-
}).
|
|
2589
|
+
}).start()));
|
|
2663
2590
|
},
|
|
2664
2591
|
disconnect() {
|
|
2665
2592
|
controller.abort();
|
|
@@ -2829,6 +2756,6 @@ async function pollForPairingToken({ studioUrl = agentDefaults.studioUrl, sessio
|
|
|
2829
2756
|
throw new Error("The pairing code expired, pair again");
|
|
2830
2757
|
}
|
|
2831
2758
|
//#endregion
|
|
2832
|
-
export { InvalidAgentTokenError, PairingCanceledError, createAgent, createClient, createFileStorage, createJob,
|
|
2759
|
+
export { InvalidAgentTokenError, PairingCanceledError, connectWebSocketRpc, createAgent, createClient, createFileStorage, createJob, defaultStudioUrl, generationEventTypes, machineTokenFrom, pollForPairingToken, runConnection, setStorage, startPairing, waitForJob };
|
|
2833
2760
|
|
|
2834
2761
|
//# sourceMappingURL=index.js.map
|