@kubb/studio 5.9.3 → 5.9.5
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 +2 -2
- package/dist/index.cjs +131 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +134 -82
- package/dist/index.js.map +1 -1
- package/dist/{protocol-gXoAXvT3.d.ts → protocol-ixLkhvRJ.d.ts} +21 -2
- package/dist/protocol.cjs +4 -0
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.ts +2 -2
- package/dist/protocol.js +3 -1
- package/dist/protocol.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ running in a developer's project and a container running a fixed plugin set.
|
|
|
74
74
|
| `connect.ts` | `connectAgent`: the socket loop and the `Agent` that serves Studio's calls. |
|
|
75
75
|
| `pair.ts` | `pairAgent`: RFC 8628 device pairing. |
|
|
76
76
|
| `snapshot.ts` | `createSnapshot` for CI, and packing a generation into an npm tarball. |
|
|
77
|
-
| `generations.ts` | Running a generation and keeping its files for `readFiles`.
|
|
77
|
+
| `generations.ts` | Running a generation and keeping its files and AST for `readFiles`/`readInput`. |
|
|
78
78
|
| `configFile.ts` | Reading and patching plugin options in `kubb.config.ts`. |
|
|
79
79
|
|
|
80
80
|
## Connection
|
|
@@ -107,7 +107,7 @@ Every permission is off by default, and each covers one trust boundary:
|
|
|
107
107
|
| `allowWrite` | Generated files are written to disk. Off means they exist only in memory and stream to Studio. |
|
|
108
108
|
| `allowExec` | The formatter, the linter, and `output.postGenerate` run as child processes. |
|
|
109
109
|
| `allowConfigEdit` | Studio may change plugin options in `kubb.config.ts`. |
|
|
110
|
-
| `allowRead` | Studio may read back the files a generation produced.
|
|
110
|
+
| `allowRead` | Studio may read back the files a generation produced and its parsed spec. |
|
|
111
111
|
|
|
112
112
|
A sandbox never writes or edits config, and always allows reads.
|
|
113
113
|
|
package/dist/index.cjs
CHANGED
|
@@ -37,6 +37,7 @@ let capnweb = require("capnweb");
|
|
|
37
37
|
let magicast = require("magicast");
|
|
38
38
|
let node_module = require("node:module");
|
|
39
39
|
let node_util = require("node:util");
|
|
40
|
+
let _kubb_ast = require("@kubb/ast");
|
|
40
41
|
let ofetch = require("ofetch");
|
|
41
42
|
let node_zlib = require("node:zlib");
|
|
42
43
|
//#region ../../internals/utils/src/casing.ts
|
|
@@ -176,10 +177,18 @@ function getElapsedMs(hrStart) {
|
|
|
176
177
|
}
|
|
177
178
|
//#endregion
|
|
178
179
|
//#region package.json
|
|
179
|
-
var version = "5.9.
|
|
180
|
+
var version = "5.9.5";
|
|
180
181
|
//#endregion
|
|
181
182
|
//#region src/resolveConfig.ts
|
|
182
183
|
/**
|
|
184
|
+
* Turns the JSON config Studio sends back into live Kubb objects.
|
|
185
|
+
*
|
|
186
|
+
* A plugin or adapter instance carries closures (`parse`, `getImports`, ...) that cannot survive
|
|
187
|
+
* JSON, so both sides pass options over the wire and the factory is re-invoked here with the merged
|
|
188
|
+
* result. Only packages {@link isAllowedPlugin} accepts are resolved this way, never an arbitrary
|
|
189
|
+
* module the payload names.
|
|
190
|
+
*/
|
|
191
|
+
/**
|
|
183
192
|
* Imports a package, falling back to how the user's project would resolve it.
|
|
184
193
|
*
|
|
185
194
|
* `import()` resolves from this file, so a linked or globally installed Studio (`pnpm link`,
|
|
@@ -258,8 +267,9 @@ function isAllowedPlugin({ packageName, extensions }) {
|
|
|
258
267
|
return KUBB_PLUGIN_SPECIFIER.test(packageName) || extensions.get(packageName)?.manifest.kind === "plugin";
|
|
259
268
|
}
|
|
260
269
|
/**
|
|
261
|
-
* Dynamically imports
|
|
262
|
-
* {@link isAllowedPlugin}
|
|
270
|
+
* Dynamically imports an extension package and returns its factory. Callers check
|
|
271
|
+
* {@link isAllowedPlugin} or derive the package from a disk instance first, so an arbitrary module
|
|
272
|
+
* never reaches `import()`.
|
|
263
273
|
*
|
|
264
274
|
* Packages must be pre-installed in the Docker image at build time via the `KUBB_PACKAGES`
|
|
265
275
|
* build ARG, no runtime installation is possible in the distroless container.
|
|
@@ -267,13 +277,9 @@ function isAllowedPlugin({ packageName, extensions }) {
|
|
|
267
277
|
* Resolution order: the manifest's `export`, else the camelCase named export the package name
|
|
268
278
|
* implies (e.g. `pluginTs`), then the default export.
|
|
269
279
|
*
|
|
270
|
-
* @throws if the package
|
|
280
|
+
* @throws if the package cannot be imported or exports no callable factory.
|
|
271
281
|
*/
|
|
272
|
-
async function
|
|
273
|
-
if (!isAllowedPlugin({
|
|
274
|
-
packageName,
|
|
275
|
-
extensions
|
|
276
|
-
})) throw new Error(`Plugin "${packageName}" is not an installed Kubb plugin. Studio loads @kubb/plugin-* packages and installed packages that ship a kubb-extension.json with kind "plugin".`);
|
|
282
|
+
async function loadFactory({ packageName, extensions }) {
|
|
277
283
|
let mod;
|
|
278
284
|
try {
|
|
279
285
|
mod = await importFromProject(packageName);
|
|
@@ -292,7 +298,7 @@ async function loadPluginFactory({ packageName, extensions }) {
|
|
|
292
298
|
}
|
|
293
299
|
/**
|
|
294
300
|
* Resolves each plugin entry by dynamically importing its package and calling its factory with the
|
|
295
|
-
* provided options.
|
|
301
|
+
* provided options. Only packages {@link isAllowedPlugin} accepts are loaded.
|
|
296
302
|
*
|
|
297
303
|
* @example
|
|
298
304
|
* ```ts
|
|
@@ -306,7 +312,11 @@ async function loadPluginFactory({ packageName, extensions }) {
|
|
|
306
312
|
*/
|
|
307
313
|
async function resolvePlugins({ plugins, extensions = /* @__PURE__ */ new Map() }) {
|
|
308
314
|
return Promise.all(plugins.map(async ({ name, options }) => {
|
|
309
|
-
|
|
315
|
+
if (!isAllowedPlugin({
|
|
316
|
+
packageName: name,
|
|
317
|
+
extensions
|
|
318
|
+
})) throw new Error(`Plugin "${name}" is not an installed Kubb plugin. Studio loads @kubb/plugin-* packages and installed packages that ship a kubb-extension.json with kind "plugin".`);
|
|
319
|
+
return (await loadFactory({
|
|
310
320
|
packageName: name,
|
|
311
321
|
extensions
|
|
312
322
|
}))(options ?? {});
|
|
@@ -454,13 +464,12 @@ async function mergePlugins({ diskPlugins, studioPlugins, extensions = /* @__PUR
|
|
|
454
464
|
async function rebuildExtension({ disk, studioOptions, kind, extensions }) {
|
|
455
465
|
if (!studioOptions || !Object.keys(studioOptions).length) return disk;
|
|
456
466
|
const { name } = (0, _kubb_core.resolveMeta)(disk);
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
467
|
+
return (await loadFactory({
|
|
468
|
+
packageName: [...extensions.values()].find(({ manifest }) => manifest.kind === kind && manifest.name === name)?.package ?? `@kubb/${kind}-${name}`,
|
|
469
|
+
extensions
|
|
470
|
+
}).catch((cause) => {
|
|
460
471
|
throw new Error(`The ${kind} "${name}" is not an installed Kubb extension, so Studio cannot change its options. Edit it in the config file.`, { cause });
|
|
461
|
-
});
|
|
462
|
-
if (typeof factory !== "function") throw new Error(`The ${kind} "${name}" is not an installed Kubb extension, so Studio cannot change its options. Edit it in the config file.`);
|
|
463
|
-
return factory(mergeOptions(disk.options ?? {}, studioOptions));
|
|
472
|
+
}))(mergeOptions(disk.options ?? {}, studioOptions));
|
|
464
473
|
}
|
|
465
474
|
/**
|
|
466
475
|
* Merges Studio-provided adapter option overrides into the disk config's adapter, see {@link rebuildExtension}.
|
|
@@ -1099,7 +1108,10 @@ function relativeStoragePath({ root, filePath }) {
|
|
|
1099
1108
|
* The content fingerprint Studio compares snapshots by. Agent runs and CI uploads must agree on it.
|
|
1100
1109
|
*/
|
|
1101
1110
|
const hashOf = (content) => (0, node_crypto.createHash)("sha1").update(content).digest("hex").slice(0, 16);
|
|
1102
|
-
|
|
1111
|
+
/**
|
|
1112
|
+
* The bytes `entries` keep on disk: output, written files, and kept input.
|
|
1113
|
+
*/
|
|
1114
|
+
const totalBytes = (entries) => entries.reduce((sum, { output, disk, inputBytes }) => sum + output.bytes + (disk?.bytes ?? 0) + (inputBytes ?? 0), 0);
|
|
1103
1115
|
function positiveNumber(value) {
|
|
1104
1116
|
const parsed = Number(value);
|
|
1105
1117
|
return value && Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
@@ -1142,7 +1154,11 @@ async function generate({ config, hooks, signal, allowExec }) {
|
|
|
1142
1154
|
hooks,
|
|
1143
1155
|
signal
|
|
1144
1156
|
});
|
|
1145
|
-
|
|
1157
|
+
let input = null;
|
|
1158
|
+
const unhook = kubb.hooks.hook("kubb:build:end", (ctx) => {
|
|
1159
|
+
if (ctx.config === kubb.config) input = kubb.driver.inputNode;
|
|
1160
|
+
});
|
|
1161
|
+
const result = await kubb.generate().finally(unhook);
|
|
1146
1162
|
if (!result.success) throw generationFailure(result.diagnostics);
|
|
1147
1163
|
const { storage, root } = kubb.config;
|
|
1148
1164
|
return {
|
|
@@ -1151,7 +1167,8 @@ async function generate({ config, hooks, signal, allowExec }) {
|
|
|
1151
1167
|
paths: new Set(result.files.map((file) => relativeStoragePath({
|
|
1152
1168
|
root,
|
|
1153
1169
|
filePath: file.path
|
|
1154
|
-
})))
|
|
1170
|
+
}))),
|
|
1171
|
+
input
|
|
1155
1172
|
};
|
|
1156
1173
|
}
|
|
1157
1174
|
/**
|
|
@@ -1232,6 +1249,33 @@ function createGenerationStore({ storage, maxCount, maxMb, ttlMs, now = Date.now
|
|
|
1232
1249
|
return {
|
|
1233
1250
|
keep,
|
|
1234
1251
|
drop,
|
|
1252
|
+
/**
|
|
1253
|
+
* Keeps the `InputNode` of `jobId` and returns its size in bytes, or 0 when it keeps none. A ref schema loses
|
|
1254
|
+
* the copy of its target, which `input.schemas` holds: with it, a large spec outgrows what `JSON.stringify` can return.
|
|
1255
|
+
*/
|
|
1256
|
+
async keepInput({ jobId, input, maxSetMb }) {
|
|
1257
|
+
if (!input) return 0;
|
|
1258
|
+
let json;
|
|
1259
|
+
try {
|
|
1260
|
+
json = JSON.stringify(_kubb_ast.ast.transform(input, { schema: (node) => node.type === "ref" && node.schema ? {
|
|
1261
|
+
...node,
|
|
1262
|
+
schema: void 0
|
|
1263
|
+
} : void 0 }));
|
|
1264
|
+
} catch {
|
|
1265
|
+
return 0;
|
|
1266
|
+
}
|
|
1267
|
+
const bytes = Buffer.byteLength(json);
|
|
1268
|
+
if (bytes > maxSetMb * MB) return 0;
|
|
1269
|
+
await storage.writeItem(`${dirOf(jobId)}input.json`, json);
|
|
1270
|
+
return bytes;
|
|
1271
|
+
},
|
|
1272
|
+
/**
|
|
1273
|
+
* The input `keepInput` kept for `jobId`, or `undefined` when there is none.
|
|
1274
|
+
*/
|
|
1275
|
+
async readInput(jobId) {
|
|
1276
|
+
const stored = await storage.readItem(`${dirOf(jobId)}input.json`);
|
|
1277
|
+
return stored === null ? void 0 : JSON.parse(stored);
|
|
1278
|
+
},
|
|
1235
1279
|
get: async (jobId) => (await load()).find((generation) => generation.jobId === jobId && isLive(generation)),
|
|
1236
1280
|
async add(generation) {
|
|
1237
1281
|
const current = await load();
|
|
@@ -1455,7 +1499,6 @@ function createGenerationStream(hooks, jobId) {
|
|
|
1455
1499
|
return {
|
|
1456
1500
|
stream,
|
|
1457
1501
|
close,
|
|
1458
|
-
dispose: () => fail(),
|
|
1459
1502
|
fail
|
|
1460
1503
|
};
|
|
1461
1504
|
}
|
|
@@ -1588,29 +1631,29 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1588
1631
|
});
|
|
1589
1632
|
throw new Error(message);
|
|
1590
1633
|
}
|
|
1591
|
-
|
|
1634
|
+
#assertConnected() {
|
|
1592
1635
|
if (!this.#session.info) throw new Error("Studio must call connect() before any other command");
|
|
1593
|
-
|
|
1594
|
-
|
|
1636
|
+
}
|
|
1637
|
+
#logCommand(command, info) {
|
|
1638
|
+
return emit(this.#options.hooks, "studio:command", {
|
|
1595
1639
|
command,
|
|
1596
1640
|
info
|
|
1597
1641
|
});
|
|
1598
|
-
return value;
|
|
1599
1642
|
}
|
|
1600
|
-
async #readConfigFileView() {
|
|
1643
|
+
async #readConfigFileView(source) {
|
|
1601
1644
|
if (!this.#permissions.allowConfigEdit) return void 0;
|
|
1602
1645
|
try {
|
|
1603
|
-
return readConfig(await
|
|
1646
|
+
return readConfig(await source);
|
|
1604
1647
|
} catch (error) {
|
|
1605
1648
|
await this.#warn({ message: `Could not read ${this.#options.configFile}: ${getErrorMessage(error)}` });
|
|
1606
1649
|
return;
|
|
1607
1650
|
}
|
|
1608
1651
|
}
|
|
1609
|
-
async #installedExtensions() {
|
|
1610
|
-
const
|
|
1652
|
+
async #installedExtensions(source = read(this.#options.configFile)) {
|
|
1653
|
+
const imports = await source.then(readImports, () => []);
|
|
1611
1654
|
const installed = await (0, _kubb_core.readInstalledPackages)({
|
|
1612
1655
|
root: this.#options.root,
|
|
1613
|
-
packages: [...
|
|
1656
|
+
packages: [...imports, ...BUILT_IN_PACKAGES],
|
|
1614
1657
|
resolveFrom: ["kubb"]
|
|
1615
1658
|
});
|
|
1616
1659
|
return new Map(installed.flatMap(({ package: name, version, manifest }) => version !== void 0 && manifest ? [[name, {
|
|
@@ -1621,11 +1664,12 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1621
1664
|
}
|
|
1622
1665
|
async connect(info) {
|
|
1623
1666
|
this.#session.info = info;
|
|
1624
|
-
const { configPath, root, version: version$1, loadConfig } = this.#options;
|
|
1667
|
+
const { configPath, root, version: version$1, loadConfig, configFile } = this.#options;
|
|
1668
|
+
const source = read(configFile);
|
|
1625
1669
|
const [config, file, extensions] = await Promise.all([
|
|
1626
1670
|
loadConfig(),
|
|
1627
|
-
this.#readConfigFileView(),
|
|
1628
|
-
this.#installedExtensions()
|
|
1671
|
+
this.#readConfigFileView(source),
|
|
1672
|
+
this.#installedExtensions(source)
|
|
1629
1673
|
]);
|
|
1630
1674
|
this.#session.connected.resolve(info);
|
|
1631
1675
|
return {
|
|
@@ -1678,11 +1722,13 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1678
1722
|
result,
|
|
1679
1723
|
stop: () => {
|
|
1680
1724
|
controller.abort(/* @__PURE__ */ new Error("Generation canceled"));
|
|
1681
|
-
stream.
|
|
1725
|
+
stream.fail();
|
|
1682
1726
|
}
|
|
1683
1727
|
});
|
|
1684
1728
|
}
|
|
1729
|
+
/** Runs one job and keeps its output, and its input with `allowRead`, for later reads. */
|
|
1685
1730
|
async #runGeneration(input, controller) {
|
|
1731
|
+
this.#assertConnected();
|
|
1686
1732
|
if (this.#session.closed) throw new Error("Connection is closed");
|
|
1687
1733
|
if (this.#session.active) return this.#refuse({ message: "A generation is already in progress, please wait for it to finish" });
|
|
1688
1734
|
const settled = Promise.withResolvers();
|
|
@@ -1694,13 +1740,10 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1694
1740
|
let root = this.#options.root;
|
|
1695
1741
|
try {
|
|
1696
1742
|
if (this.#sandbox) root = await (0, node_fs_promises.mkdtemp)(node_path.default.join((0, node_os.tmpdir)(), "kubb-job-"));
|
|
1697
|
-
return await this.#
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
root,
|
|
1702
|
-
signal: controller.signal
|
|
1703
|
-
})
|
|
1743
|
+
return await this.#generate({
|
|
1744
|
+
input,
|
|
1745
|
+
root,
|
|
1746
|
+
signal: controller.signal
|
|
1704
1747
|
});
|
|
1705
1748
|
} finally {
|
|
1706
1749
|
if (root !== this.#options.root) await Promise.all([(0, node_fs_promises.rm)(root, {
|
|
@@ -1771,37 +1814,36 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1771
1814
|
files,
|
|
1772
1815
|
maxSetMb: this.#limits.maxMb
|
|
1773
1816
|
});
|
|
1817
|
+
const inputBytes = await this.#generations.keepInput({
|
|
1818
|
+
jobId,
|
|
1819
|
+
input: this.#permissions.allowRead ? files.input : null,
|
|
1820
|
+
maxSetMb: this.#limits.maxMb
|
|
1821
|
+
});
|
|
1774
1822
|
if (!output.paths.length && Object.keys(output.hashes).length) await this.#warn({ message: "Kept only the hashes of this generation: its output is too large to keep" });
|
|
1775
1823
|
await this.#generations.add({
|
|
1776
1824
|
jobId,
|
|
1777
1825
|
output,
|
|
1778
|
-
disk
|
|
1826
|
+
disk,
|
|
1827
|
+
inputBytes
|
|
1779
1828
|
});
|
|
1829
|
+
await this.#logCommand("generate", `${activePlugins.length} plugin${activePlugins.length === 1 ? "" : "s"}, ${this.#permissions.allowWrite ? "written to disk" : "in memory"}${studioInput !== void 0 ? ", from a Studio spec" : ""}`);
|
|
1780
1830
|
return {
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
disk: disk ? { hashes: disk.hashes } : void 0
|
|
1787
|
-
},
|
|
1788
|
-
info: `${activePlugins.length} plugin${activePlugins.length === 1 ? "" : "s"}, ${this.#permissions.allowWrite ? "written to disk" : "in memory"}${studioInput !== void 0 ? ", from a Studio spec" : ""}`
|
|
1831
|
+
status: "success",
|
|
1832
|
+
files: [...files.paths],
|
|
1833
|
+
fileCount: files.paths.size,
|
|
1834
|
+
hashes: output.hashes,
|
|
1835
|
+
disk: disk ? { hashes: disk.hashes } : void 0
|
|
1789
1836
|
};
|
|
1790
1837
|
}
|
|
1791
|
-
saveConfig(input) {
|
|
1792
|
-
|
|
1793
|
-
command: "saveConfig",
|
|
1794
|
-
run: () => this.#saveConfig(input)
|
|
1795
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
async #saveConfig(input) {
|
|
1838
|
+
async saveConfig(input) {
|
|
1839
|
+
this.#assertConnected();
|
|
1798
1840
|
const { configPath, configFile } = this.#options;
|
|
1799
1841
|
if (!Array.isArray(input.edits)) {
|
|
1800
1842
|
await this.#warn({ message: "Ignored save: the message carried no edits" });
|
|
1801
|
-
return {
|
|
1843
|
+
return {
|
|
1802
1844
|
outcomes: [],
|
|
1803
1845
|
changed: false
|
|
1804
|
-
}
|
|
1846
|
+
};
|
|
1805
1847
|
}
|
|
1806
1848
|
const edits = input.edits;
|
|
1807
1849
|
const refuse = (reason) => ({
|
|
@@ -1814,9 +1856,9 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1814
1856
|
});
|
|
1815
1857
|
if (!this.#permissions.allowConfigEdit) {
|
|
1816
1858
|
await this.#warn({ message: "Ignored save: editing kubb.config.ts was not granted" });
|
|
1817
|
-
return
|
|
1859
|
+
return refuse("the agent was not granted permission to edit kubb.config.ts");
|
|
1818
1860
|
}
|
|
1819
|
-
if (this.#session.active) return
|
|
1861
|
+
if (this.#session.active) return refuse("a generation is in progress");
|
|
1820
1862
|
try {
|
|
1821
1863
|
const { outcomes, changed } = await writeConfigEdits({
|
|
1822
1864
|
filePath: configFile,
|
|
@@ -1824,27 +1866,40 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1824
1866
|
extensions: await this.#installedExtensions()
|
|
1825
1867
|
});
|
|
1826
1868
|
const applied = outcomes.filter((outcome) => outcome.applied).length;
|
|
1869
|
+
await this.#logCommand("saveConfig", `applied ${applied}/${outcomes.length} edits to ${configPath}`);
|
|
1827
1870
|
return {
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
changed
|
|
1831
|
-
},
|
|
1832
|
-
info: `applied ${applied}/${outcomes.length} edits to ${configPath}`
|
|
1871
|
+
outcomes,
|
|
1872
|
+
changed
|
|
1833
1873
|
};
|
|
1834
1874
|
} catch (error) {
|
|
1835
1875
|
await emit(this.#options.hooks, "studio:error", { error: toError(error) });
|
|
1836
|
-
return
|
|
1876
|
+
return refuse(getErrorMessage(error));
|
|
1837
1877
|
}
|
|
1838
1878
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1879
|
+
/**
|
|
1880
|
+
* Returns the Kubb AST a kept job parsed its spec into. Refused without `allowRead`.
|
|
1881
|
+
*/
|
|
1882
|
+
async readInput(input) {
|
|
1883
|
+
this.#assertConnected();
|
|
1884
|
+
if (!this.#permissions.allowRead) return this.#refuse({
|
|
1885
|
+
message: require_protocol.READ_NOT_ALLOWED_MESSAGE,
|
|
1886
|
+
permission: "allowRead"
|
|
1843
1887
|
});
|
|
1888
|
+
if (typeof input.jobId !== "string" || !input.jobId) return this.#refuse({ message: "The request must name a job" });
|
|
1889
|
+
const generation = await this.#generations.get(input.jobId);
|
|
1890
|
+
if (!generation) return this.#refuse({ message: require_protocol.GENERATION_GONE_MESSAGE });
|
|
1891
|
+
const kept = generation.inputBytes ? await this.#generations.readInput(input.jobId) : void 0;
|
|
1892
|
+
if (!kept) return this.#refuse({ message: require_protocol.NO_INPUT_MESSAGE });
|
|
1893
|
+
await this.#logCommand("readInput", "read the API input");
|
|
1894
|
+
return { input: kept };
|
|
1844
1895
|
}
|
|
1845
|
-
|
|
1896
|
+
/**
|
|
1897
|
+
* Returns the requested files of a kept job, one page of lines at a time. Refused without `allowRead`.
|
|
1898
|
+
*/
|
|
1899
|
+
async readFiles(input) {
|
|
1900
|
+
this.#assertConnected();
|
|
1846
1901
|
if (!this.#permissions.allowRead) return this.#refuse({
|
|
1847
|
-
message:
|
|
1902
|
+
message: require_protocol.READ_NOT_ALLOWED_MESSAGE,
|
|
1848
1903
|
permission: "allowRead"
|
|
1849
1904
|
});
|
|
1850
1905
|
if (!Array.isArray(input.paths) || typeof input.jobId !== "string" || !input.jobId) return this.#refuse({ message: "The request must name a job and carry paths" });
|
|
@@ -1862,10 +1917,8 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
1862
1917
|
cursor: input.cursor,
|
|
1863
1918
|
limit: input.limit
|
|
1864
1919
|
});
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
info: `read ${Object.keys(result.files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`
|
|
1868
|
-
};
|
|
1920
|
+
await this.#logCommand("readFiles", `read ${Object.keys(result.files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`);
|
|
1921
|
+
return result;
|
|
1869
1922
|
}
|
|
1870
1923
|
};
|
|
1871
1924
|
const openWebSocketRpc = ({ url, token, instanceId, agent }) => {
|
|
@@ -2156,7 +2209,6 @@ async function pairAgent({ onCode, onRetry, maxAttempts = 1, ...options }) {
|
|
|
2156
2209
|
}
|
|
2157
2210
|
//#endregion
|
|
2158
2211
|
//#region src/snapshot.ts
|
|
2159
|
-
const gzipAsync = (0, node_util.promisify)(node_zlib.gzip);
|
|
2160
2212
|
const UPLOAD_TIMEOUT_MS = 12e4;
|
|
2161
2213
|
const BUNDLED_DEPENDENCIES = ["@kubb/plugin-barrel"];
|
|
2162
2214
|
/**
|
|
@@ -2276,7 +2328,7 @@ async function createSnapshotPackage({ files, packageInfo }) {
|
|
|
2276
2328
|
Buffer.alloc((512 - bytes.length % 512) % 512)
|
|
2277
2329
|
];
|
|
2278
2330
|
});
|
|
2279
|
-
return
|
|
2331
|
+
return (0, node_zlib.gzipSync)(Buffer.concat([...chunks, Buffer.alloc(1024)]));
|
|
2280
2332
|
} finally {
|
|
2281
2333
|
await (0, node_fs_promises.rm)(root, {
|
|
2282
2334
|
recursive: true,
|