@paperclipai/plugin-daytona 2026.1005.0 → 2026.1006.0-beta.0
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 +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +40 -12
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.test.js +112 -6
- package/dist/plugin.test.js.map +1 -1
- package/package.json +2 -2
package/dist/plugin.test.js
CHANGED
|
@@ -2175,6 +2175,112 @@ describe("Daytona sandbox provider plugin", () => {
|
|
|
2175
2175
|
// stderr from the callback log form instead of the 50-ms poll, and falls
|
|
2176
2176
|
// back to the poll only when the stream fails.
|
|
2177
2177
|
const streamExecParams = (overrides = {}) => sessionExecParams(overrides);
|
|
2178
|
+
// Long-lived healthy streams keep producing output. Quiet streams now
|
|
2179
|
+
// switch to snapshots, which have separate lifetime coverage below.
|
|
2180
|
+
async function streamFor(ms, onStderr) {
|
|
2181
|
+
const timer = setInterval(() => onStderr?.("."), 10_000);
|
|
2182
|
+
try {
|
|
2183
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
2184
|
+
}
|
|
2185
|
+
finally {
|
|
2186
|
+
clearInterval(timer);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
it.each(["", "prefix;🙂;"])("recovers unseen output and cancellation from a stuck socket after prefix %j", async (prefix) => {
|
|
2190
|
+
process.env.DAYTONA_API_KEY = "host-key";
|
|
2191
|
+
const executionLog = vi.fn();
|
|
2192
|
+
const restore = __setDaytonaPluginContextForTest({ execution: { log: executionLog } });
|
|
2193
|
+
const sandbox = createMockSandbox();
|
|
2194
|
+
let lateOutput;
|
|
2195
|
+
let rejectStream;
|
|
2196
|
+
let snapshots = 0;
|
|
2197
|
+
let cancelled = false;
|
|
2198
|
+
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout) => {
|
|
2199
|
+
if (onStdout) {
|
|
2200
|
+
lateOutput = onStdout;
|
|
2201
|
+
onStdout(prefix);
|
|
2202
|
+
return new Promise((_resolve, reject) => { rejectStream = reject; });
|
|
2203
|
+
}
|
|
2204
|
+
snapshots += 1;
|
|
2205
|
+
return { stdout: prefix + "permission;" + (cancelled ? "cancelled;" : ""), stderr: "" };
|
|
2206
|
+
});
|
|
2207
|
+
sandbox.process.getSessionCommand.mockImplementation(async () => ({ exitCode: cancelled ? 0 : undefined }));
|
|
2208
|
+
mockGet.mockResolvedValue(sandbox);
|
|
2209
|
+
vi.useFakeTimers();
|
|
2210
|
+
try {
|
|
2211
|
+
const promise = plugin.definition.onEnvironmentExecute?.(streamExecParams({ timeoutMs: 400 }));
|
|
2212
|
+
let settled = false;
|
|
2213
|
+
void promise?.then(() => { settled = true; });
|
|
2214
|
+
await vi.advanceTimersByTimeAsync(14_999);
|
|
2215
|
+
expect(snapshots).toBe(0);
|
|
2216
|
+
expect(settled).toBe(false);
|
|
2217
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
2218
|
+
expect(snapshots).toBe(1);
|
|
2219
|
+
expect(executionLog).toHaveBeenCalledWith("stdout", "permission;");
|
|
2220
|
+
// The abandoned socket cannot corrupt snapshot cursors or publish
|
|
2221
|
+
// output after the timeout, including a delayed rejection.
|
|
2222
|
+
lateOutput("stale bytes");
|
|
2223
|
+
rejectStream(new Error("late socket failure"));
|
|
2224
|
+
await vi.advanceTimersByTimeAsync(2_000);
|
|
2225
|
+
expect(settled).toBe(false);
|
|
2226
|
+
expect(snapshots).toBe(3);
|
|
2227
|
+
cancelled = true;
|
|
2228
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
2229
|
+
expect(await promise).toMatchObject({ exitCode: 0, timedOut: false, stdout: prefix + "permission;cancelled;" });
|
|
2230
|
+
lateOutput("after settlement");
|
|
2231
|
+
expect(executionLog.mock.calls).toEqual([
|
|
2232
|
+
...(prefix ? [["stdout", prefix]] : []), ["stdout", "permission;"], ["stdout", "cancelled;"],
|
|
2233
|
+
]);
|
|
2234
|
+
expect(sandbox.process.executeSessionCommand).toHaveBeenCalledTimes(1);
|
|
2235
|
+
expect(sandbox.process.getSessionCommandLogs.mock.calls.filter((call) => call[2])).toHaveLength(1);
|
|
2236
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
2237
|
+
}
|
|
2238
|
+
finally {
|
|
2239
|
+
vi.useRealTimers();
|
|
2240
|
+
restore();
|
|
2241
|
+
}
|
|
2242
|
+
});
|
|
2243
|
+
it.each(["status", "snapshot"])("bounds a stuck %s read after an idle socket without claiming exit", async (stalledRead) => {
|
|
2244
|
+
process.env.DAYTONA_API_KEY = "host-key";
|
|
2245
|
+
const sandbox = createMockSandbox();
|
|
2246
|
+
let releaseRead;
|
|
2247
|
+
const stalled = new Promise((resolve) => { releaseRead = resolve; });
|
|
2248
|
+
const executionLog = vi.fn();
|
|
2249
|
+
const restore = __setDaytonaPluginContextForTest({ execution: { log: executionLog } });
|
|
2250
|
+
sandbox.process.getSessionCommand.mockImplementation(async () => {
|
|
2251
|
+
if (stalledRead === "status")
|
|
2252
|
+
await stalled;
|
|
2253
|
+
return { exitCode: undefined };
|
|
2254
|
+
});
|
|
2255
|
+
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout) => {
|
|
2256
|
+
if (onStdout) {
|
|
2257
|
+
onStdout("prefix;");
|
|
2258
|
+
return new Promise(() => { });
|
|
2259
|
+
}
|
|
2260
|
+
await stalled;
|
|
2261
|
+
return { stdout: "prefix;late;", stderr: "" };
|
|
2262
|
+
});
|
|
2263
|
+
mockGet.mockResolvedValue(sandbox);
|
|
2264
|
+
vi.useFakeTimers();
|
|
2265
|
+
try {
|
|
2266
|
+
const promise = plugin.definition.onEnvironmentExecute?.(streamExecParams({ timeoutMs: 400 }));
|
|
2267
|
+
await vi.advanceTimersByTimeAsync(15_400);
|
|
2268
|
+
expect(await promise).toMatchObject({
|
|
2269
|
+
exitCode: null, timedOut: true, stdout: "prefix;",
|
|
2270
|
+
metadata: { timeoutScope: "session_log_observation", commandExitConfirmed: false },
|
|
2271
|
+
});
|
|
2272
|
+
releaseRead();
|
|
2273
|
+
await vi.advanceTimersByTimeAsync(1_000);
|
|
2274
|
+
expect(executionLog.mock.calls).toEqual([["stdout", "prefix;"]]);
|
|
2275
|
+
expect(sandbox.process.executeSessionCommand).toHaveBeenCalledTimes(1);
|
|
2276
|
+
expect(vi.getTimerCount()).toBe(0);
|
|
2277
|
+
}
|
|
2278
|
+
finally {
|
|
2279
|
+
releaseRead();
|
|
2280
|
+
vi.useRealTimers();
|
|
2281
|
+
restore();
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2178
2284
|
it("streams ordered stdout and stderr from the callback log form (test_log_stream_delivers_ordered_chunks)", async () => {
|
|
2179
2285
|
process.env.DAYTONA_API_KEY = "host-key";
|
|
2180
2286
|
const sandbox = createMockSandbox();
|
|
@@ -2287,12 +2393,12 @@ describe("Daytona sandbox provider plugin", () => {
|
|
|
2287
2393
|
process.env.DAYTONA_API_KEY = "host-key";
|
|
2288
2394
|
const sandbox = createMockSandbox();
|
|
2289
2395
|
let streams = 0;
|
|
2290
|
-
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout) => {
|
|
2396
|
+
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout, onStderr) => {
|
|
2291
2397
|
if (!onStdout)
|
|
2292
2398
|
return { stdout: "first;last;", stderr: "" };
|
|
2293
2399
|
streams += 1;
|
|
2294
2400
|
onStdout("first;");
|
|
2295
|
-
await
|
|
2401
|
+
await streamFor(3_600_000, onStderr);
|
|
2296
2402
|
if (streams === 1 && ending === "reject")
|
|
2297
2403
|
throw new Error("socket error");
|
|
2298
2404
|
if (streams === 2)
|
|
@@ -2326,14 +2432,14 @@ describe("Daytona sandbox provider plugin", () => {
|
|
|
2326
2432
|
let streams = 0;
|
|
2327
2433
|
let snapshots = 0;
|
|
2328
2434
|
let exited = false;
|
|
2329
|
-
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout) => {
|
|
2435
|
+
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout, onStderr) => {
|
|
2330
2436
|
if (!onStdout) {
|
|
2331
2437
|
snapshots += 1;
|
|
2332
2438
|
return { stdout: "partial;", stderr: "" };
|
|
2333
2439
|
}
|
|
2334
2440
|
onStdout("partial;");
|
|
2335
2441
|
if (++streams === 1)
|
|
2336
|
-
await
|
|
2442
|
+
await streamFor(3_600_000, onStderr);
|
|
2337
2443
|
});
|
|
2338
2444
|
sandbox.process.getSessionCommand.mockImplementation(async () => ({ exitCode: exited ? 0 : undefined }));
|
|
2339
2445
|
mockGet.mockResolvedValue(sandbox);
|
|
@@ -2359,11 +2465,11 @@ describe("Daytona sandbox provider plugin", () => {
|
|
|
2359
2465
|
process.env.DAYTONA_API_KEY = "host-key";
|
|
2360
2466
|
const sandbox = createMockSandbox();
|
|
2361
2467
|
let streams = 0;
|
|
2362
|
-
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout) => {
|
|
2468
|
+
sandbox.process.getSessionCommandLogs.mockImplementation(async (_sid, _cmdId, onStdout, onStderr) => {
|
|
2363
2469
|
if (onStdout) {
|
|
2364
2470
|
onStdout("partial;");
|
|
2365
2471
|
if (++streams === 1 && streamLifetime > 0)
|
|
2366
|
-
await
|
|
2472
|
+
await streamFor(streamLifetime, onStderr);
|
|
2367
2473
|
throw new Error("socket error");
|
|
2368
2474
|
}
|
|
2369
2475
|
return { stdout: "partial;", stderr: "" };
|