@kubb/studio 5.11.2 → 5.11.3
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 +3 -3
- package/dist/index.cjs +58 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +59 -30
- package/dist/index.js.map +1 -1
- package/dist/{protocol-BUZ91Gd4.d.ts → protocol-DpxvWX9E.d.cts} +20 -13
- package/dist/{protocol-BUZ91Gd4.d.cts → protocol-DpxvWX9E.d.ts} +20 -13
- package/dist/protocol.cjs +12 -2
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +1 -1
- package/dist/protocol.d.ts +1 -1
- package/dist/protocol.js +12 -2
- package/dist/protocol.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -85,9 +85,9 @@ running in a developer's project and a container running a fixed plugin set.
|
|
|
85
85
|
the agent is a sandbox and carries its slug and Studio's version. From then on the agent pings every
|
|
86
86
|
30 seconds, and Studio schedules one job at a time on the socket.
|
|
87
87
|
|
|
88
|
-
With `allowRead`, the connect payload
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
With `allowRead`, the connect payload includes the current `output.path` file list. Studio reads
|
|
89
|
+
file contents from disk through `readFiles` when a user opens a file. Sandbox agents have no disk
|
|
90
|
+
output, and output paths outside the project root are excluded.
|
|
91
91
|
|
|
92
92
|
Studio ends a socket on purpose with one of these close codes:
|
|
93
93
|
|
package/dist/index.cjs
CHANGED
|
@@ -199,7 +199,7 @@ function getElapsedMs(hrStart) {
|
|
|
199
199
|
}
|
|
200
200
|
//#endregion
|
|
201
201
|
//#region package.json
|
|
202
|
-
var version = "5.11.
|
|
202
|
+
var version = "5.11.3";
|
|
203
203
|
//#endregion
|
|
204
204
|
//#region src/resolveConfig.ts
|
|
205
205
|
/**
|
|
@@ -1473,6 +1473,33 @@ async function listDisk({ root, outputPath, maxFiles }) {
|
|
|
1473
1473
|
paths: new Set(paths)
|
|
1474
1474
|
};
|
|
1475
1475
|
}
|
|
1476
|
+
async function readDiskFiles({ files: disk, paths, cursor = 0, limit }) {
|
|
1477
|
+
const files = {};
|
|
1478
|
+
const pages = {};
|
|
1479
|
+
await inParallel({
|
|
1480
|
+
items: paths.filter((path) => disk.paths.has(path)),
|
|
1481
|
+
limit: READ_CONCURRENCY,
|
|
1482
|
+
run: async (path) => {
|
|
1483
|
+
const content = await disk.storage.readItem((0, node_path.resolve)(disk.root, path));
|
|
1484
|
+
if (content === null) return;
|
|
1485
|
+
if (limit === void 0) {
|
|
1486
|
+
files[path] = content;
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
const lines = content.split(/\r\n|\r|\n/);
|
|
1490
|
+
const end = Math.min(cursor + limit, lines.length);
|
|
1491
|
+
files[path] = lines.slice(cursor, end).join("\n");
|
|
1492
|
+
pages[path] = {
|
|
1493
|
+
nextCursor: end < lines.length ? end : null,
|
|
1494
|
+
totalLines: lines.length
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
});
|
|
1498
|
+
return limit === void 0 ? { files } : {
|
|
1499
|
+
files,
|
|
1500
|
+
pages
|
|
1501
|
+
};
|
|
1502
|
+
}
|
|
1476
1503
|
/**
|
|
1477
1504
|
* Keeps recent generations by job id in `storage`, with an index next to them so a store on disk
|
|
1478
1505
|
* survives a restart. A generation is only ever looked up by its job id, so on a pooled sandbox one
|
|
@@ -2022,23 +2049,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2022
2049
|
maxFiles: DISK_SNAPSHOT_MAX_FILES
|
|
2023
2050
|
});
|
|
2024
2051
|
if (!files) return void 0;
|
|
2025
|
-
|
|
2026
|
-
const output = await this.#generations.keep({
|
|
2027
|
-
jobId,
|
|
2028
|
-
source: require_protocol.FileSource.OUTPUT,
|
|
2029
|
-
files,
|
|
2030
|
-
maxSetMb: this.#limits.maxSnapshotMb,
|
|
2031
|
-
normalize: false
|
|
2032
|
-
});
|
|
2033
|
-
await this.#generations.add({
|
|
2034
|
-
jobId,
|
|
2035
|
-
output
|
|
2036
|
-
});
|
|
2037
|
-
return {
|
|
2038
|
-
jobId,
|
|
2039
|
-
files: output.paths,
|
|
2040
|
-
hashes: output.hashes
|
|
2041
|
-
};
|
|
2052
|
+
return { files: [...files.paths] };
|
|
2042
2053
|
}
|
|
2043
2054
|
startGeneration(input) {
|
|
2044
2055
|
const stream = createGenerationStream(this.#options.hooks, input.jobId);
|
|
@@ -2065,7 +2076,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2065
2076
|
severity: "error",
|
|
2066
2077
|
message: "Connection is closed"
|
|
2067
2078
|
});
|
|
2068
|
-
if (this.#session.active) return this.#refuse({ message:
|
|
2079
|
+
if (this.#session.active) return this.#refuse({ message: require_protocol.AgentMessage.GENERATION_IN_PROGRESS });
|
|
2069
2080
|
const settled = Promise.withResolvers();
|
|
2070
2081
|
this.#session.active = {
|
|
2071
2082
|
jobId: input.jobId,
|
|
@@ -2096,7 +2107,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2096
2107
|
async #generate({ input, root, signal }) {
|
|
2097
2108
|
const { jobId, config: patch } = input;
|
|
2098
2109
|
const studioInput = this.#sandbox ? patch?.input : void 0;
|
|
2099
|
-
if (this.#sandbox && (typeof studioInput !== "string" || !studioInput.trim() || (0, _kubb_core.getInputKind)(studioInput) !== "inline")) return this.#refuse({ message:
|
|
2110
|
+
if (this.#sandbox && (typeof studioInput !== "string" || !studioInput.trim() || (0, _kubb_core.getInputKind)(studioInput) !== "inline")) return this.#refuse({ message: require_protocol.AgentMessage.SANDBOX_INPUT_ONLY });
|
|
2100
2111
|
const [config, extensions] = await Promise.all([this.#options.loadConfig(), this.#installedExtensions()]);
|
|
2101
2112
|
const [plugins, adapter, parsers] = await Promise.all([
|
|
2102
2113
|
mergePlugins({
|
|
@@ -2229,7 +2240,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2229
2240
|
message: require_protocol.AgentMessage.READ_AST_NOT_ALLOWED,
|
|
2230
2241
|
permission: require_protocol.AgentPermission.READ_AST
|
|
2231
2242
|
});
|
|
2232
|
-
if (typeof input.jobId !== "string" || !input.jobId) return this.#refuse({ message:
|
|
2243
|
+
if (typeof input.jobId !== "string" || !input.jobId) return this.#refuse({ message: require_protocol.AgentMessage.JOB_REQUIRED });
|
|
2233
2244
|
const generation = await this.#generations.get(input.jobId);
|
|
2234
2245
|
if (!generation) return this.#refuse({ message: require_protocol.AgentMessage.GENERATION_GONE });
|
|
2235
2246
|
const kept = generation.inputBytes ? await this.#generations.readInput(input.jobId) : void 0;
|
|
@@ -2246,14 +2257,32 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2246
2257
|
message: require_protocol.AgentMessage.READ_NOT_ALLOWED,
|
|
2247
2258
|
permission: require_protocol.AgentPermission.READ
|
|
2248
2259
|
});
|
|
2249
|
-
if (!Array.isArray(input.paths) || typeof input.jobId !== "string" || !input.jobId) return this.#refuse({ message:
|
|
2260
|
+
if (!Array.isArray(input.paths) || typeof input.jobId !== "string" || !input.jobId || !["generation", "currentDisk"].includes(input.jobType)) return this.#refuse({ message: require_protocol.AgentMessage.FILE_REQUEST_INVALID });
|
|
2250
2261
|
const { paths } = input;
|
|
2251
|
-
if (paths.length > 50) return this.#refuse({ message:
|
|
2262
|
+
if (paths.length > 50) return this.#refuse({ message: require_protocol.AgentMessage.TOO_MANY_FILES });
|
|
2263
|
+
if (!isValidPage(input)) return this.#refuse({ message: require_protocol.AgentMessage.INVALID_PAGE });
|
|
2264
|
+
if (input.jobType === "currentDisk") {
|
|
2265
|
+
if (this.#sandbox) return this.#refuse({ message: require_protocol.AgentMessage.SANDBOX_DISK_UNAVAILABLE });
|
|
2266
|
+
const config = await this.#options.loadConfig();
|
|
2267
|
+
const disk = await listDisk({
|
|
2268
|
+
root: this.#options.root,
|
|
2269
|
+
outputPath: config.output.path,
|
|
2270
|
+
maxFiles: DISK_SNAPSHOT_MAX_FILES
|
|
2271
|
+
});
|
|
2272
|
+
if (!disk) return this.#refuse({ message: require_protocol.AgentMessage.GENERATION_GONE });
|
|
2273
|
+
const result = await readDiskFiles({
|
|
2274
|
+
files: disk,
|
|
2275
|
+
paths,
|
|
2276
|
+
cursor: input.cursor,
|
|
2277
|
+
limit: input.limit
|
|
2278
|
+
});
|
|
2279
|
+
await this.#logCommand("readFiles", `read ${Object.keys(result.files).length}/${paths.length} requested file${paths.length === 1 ? "" : "s"}`);
|
|
2280
|
+
return result;
|
|
2281
|
+
}
|
|
2252
2282
|
const generation = await this.#generations.get(input.jobId);
|
|
2253
2283
|
if (!generation) return this.#refuse({ message: require_protocol.AgentMessage.GENERATION_GONE });
|
|
2254
|
-
if (!isValidPage(input)) return this.#refuse({ message: `The cursor must be a whole line from 0 and come with a limit of 1 to ${require_protocol.MAX_PAGE_LINES} lines` });
|
|
2255
2284
|
const source = input.source === require_protocol.FileSource.DISK ? require_protocol.FileSource.DISK : require_protocol.FileSource.OUTPUT;
|
|
2256
|
-
if (!generation[source]) return this.#refuse({ message:
|
|
2285
|
+
if (!generation[source]) return this.#refuse({ message: require_protocol.AgentMessage.NO_DISK_SNAPSHOT });
|
|
2257
2286
|
const result = await this.#generations.read({
|
|
2258
2287
|
generation,
|
|
2259
2288
|
source,
|
|
@@ -2274,15 +2303,15 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2274
2303
|
permission: require_protocol.AgentPermission.READ
|
|
2275
2304
|
});
|
|
2276
2305
|
const { jobId, path, base, cursor = 0, limit } = input;
|
|
2277
|
-
if (typeof jobId !== "string" || !jobId || typeof path !== "string" || !path || typeof base?.jobId !== "string" || !base.jobId) return this.#refuse({ message:
|
|
2306
|
+
if (typeof jobId !== "string" || !jobId || typeof path !== "string" || !path || typeof base?.jobId !== "string" || !base.jobId) return this.#refuse({ message: require_protocol.AgentMessage.DIFF_REQUEST_INVALID });
|
|
2278
2307
|
if (!isValidPage({
|
|
2279
2308
|
cursor,
|
|
2280
2309
|
limit
|
|
2281
|
-
})) return this.#refuse({ message:
|
|
2310
|
+
})) return this.#refuse({ message: require_protocol.AgentMessage.INVALID_PAGE });
|
|
2282
2311
|
const [generation, baseGeneration] = await Promise.all([this.#generations.get(jobId), this.#generations.get(base.jobId)]);
|
|
2283
2312
|
if (!generation || !baseGeneration) return this.#refuse({ message: require_protocol.AgentMessage.GENERATION_GONE });
|
|
2284
2313
|
const baseSource = base.source === require_protocol.FileSource.DISK ? require_protocol.FileSource.DISK : require_protocol.FileSource.OUTPUT;
|
|
2285
|
-
if (!baseGeneration[baseSource]) return this.#refuse({ message:
|
|
2314
|
+
if (!baseGeneration[baseSource]) return this.#refuse({ message: require_protocol.AgentMessage.NO_DISK_SNAPSHOT });
|
|
2286
2315
|
const [after, before] = await Promise.all([this.#generations.read({
|
|
2287
2316
|
generation,
|
|
2288
2317
|
source: require_protocol.FileSource.OUTPUT,
|
|
@@ -2293,7 +2322,7 @@ var Agent = class extends capnweb.RpcTarget {
|
|
|
2293
2322
|
paths: [path]
|
|
2294
2323
|
})]);
|
|
2295
2324
|
const afterContent = after.files[path];
|
|
2296
|
-
if (afterContent === void 0 && path in generation.output.hashes) return this.#refuse({ message:
|
|
2325
|
+
if (afterContent === void 0 && path in generation.output.hashes) return this.#refuse({ message: require_protocol.AgentMessage.HASHES_ONLY });
|
|
2297
2326
|
const beforeContent = before.files[path];
|
|
2298
2327
|
if (beforeContent === void 0) return {
|
|
2299
2328
|
hunks: [],
|