@tickernelz/paperclip-pro-plugin-daytona 2026.925.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.
Files changed (58) hide show
  1. package/README.md +49 -0
  2. package/dist/duplex-command-stream.d.ts +97 -0
  3. package/dist/duplex-command-stream.d.ts.map +1 -0
  4. package/dist/duplex-command-stream.js +205 -0
  5. package/dist/duplex-command-stream.js.map +1 -0
  6. package/dist/duplex-command-stream.live.test.d.ts +2 -0
  7. package/dist/duplex-command-stream.live.test.d.ts.map +1 -0
  8. package/dist/duplex-command-stream.live.test.js +324 -0
  9. package/dist/duplex-command-stream.live.test.js.map +1 -0
  10. package/dist/duplex-command-stream.test.d.ts +2 -0
  11. package/dist/duplex-command-stream.test.d.ts.map +1 -0
  12. package/dist/duplex-command-stream.test.js +519 -0
  13. package/dist/duplex-command-stream.test.js.map +1 -0
  14. package/dist/file-sync.d.ts +77 -0
  15. package/dist/file-sync.d.ts.map +1 -0
  16. package/dist/file-sync.js +1055 -0
  17. package/dist/file-sync.js.map +1 -0
  18. package/dist/file-sync.test.d.ts +2 -0
  19. package/dist/file-sync.test.d.ts.map +1 -0
  20. package/dist/file-sync.test.js +974 -0
  21. package/dist/file-sync.test.js.map +1 -0
  22. package/dist/index.d.ts +3 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +3 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/login-pty.d.ts +162 -0
  27. package/dist/login-pty.d.ts.map +1 -0
  28. package/dist/login-pty.js +258 -0
  29. package/dist/login-pty.js.map +1 -0
  30. package/dist/login-pty.test.d.ts +2 -0
  31. package/dist/login-pty.test.d.ts.map +1 -0
  32. package/dist/login-pty.test.js +319 -0
  33. package/dist/login-pty.test.js.map +1 -0
  34. package/dist/manifest.d.ts +4 -0
  35. package/dist/manifest.d.ts.map +1 -0
  36. package/dist/manifest.js +179 -0
  37. package/dist/manifest.js.map +1 -0
  38. package/dist/plugin.d.ts +49 -0
  39. package/dist/plugin.d.ts.map +1 -0
  40. package/dist/plugin.js +2563 -0
  41. package/dist/plugin.js.map +1 -0
  42. package/dist/plugin.test.d.ts +2 -0
  43. package/dist/plugin.test.d.ts.map +1 -0
  44. package/dist/plugin.test.js +4701 -0
  45. package/dist/plugin.test.js.map +1 -0
  46. package/dist/pty-chunked-input.d.ts +48 -0
  47. package/dist/pty-chunked-input.d.ts.map +1 -0
  48. package/dist/pty-chunked-input.js +74 -0
  49. package/dist/pty-chunked-input.js.map +1 -0
  50. package/dist/pty-chunked-input.test.d.ts +2 -0
  51. package/dist/pty-chunked-input.test.d.ts.map +1 -0
  52. package/dist/pty-chunked-input.test.js +115 -0
  53. package/dist/pty-chunked-input.test.js.map +1 -0
  54. package/dist/worker.d.ts +3 -0
  55. package/dist/worker.d.ts.map +1 -0
  56. package/dist/worker.js +5 -0
  57. package/dist/worker.js.map +1 -0
  58. package/package.json +44 -0
@@ -0,0 +1,519 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildDuplexChannelLaunchWrapper, createDaytonaDuplexChannelSessionOpener, openDaytonaDuplexChannelSession, } from "./duplex-command-stream.js";
3
+ import { PTY_INPUT_CHUNK_BYTES, PTY_MESSAGE_CAP_BYTES } from "./pty-chunked-input.js";
4
+ // The carriage return that submits the launch wrapper line to the terminal.
5
+ const ENTER = "\r";
6
+ // The gateway command the tests run on the channel. The command is an argument
7
+ // vector: element 0 is the program and the rest are its arguments. The real
8
+ // command is the generated duplex gateway; the tests use a placeholder, because
9
+ // the fake PTY runs no process.
10
+ const GATEWAY = ["node", "/paperclip/gateway.mjs"];
11
+ // The gateway argument vector after the wrapper quotes each element. The wrapper
12
+ // quotes every argument as a single-quoted shell word.
13
+ const GATEWAY_QUOTED = "'node' '/paperclip/gateway.mjs'";
14
+ /**
15
+ * A fake Daytona PTY handle. It records each input write, drives the output
16
+ * stream on demand, and records the kill and the disconnect. The tests use it in
17
+ * place of the real SDK `PtyHandle`, so the session runs with no sandbox. The fake
18
+ * never echoes input back as output, so a test proves the channel adds no echo.
19
+ */
20
+ function createFakePtyHandle(onData) {
21
+ const encoder = new TextEncoder();
22
+ const inputs = [];
23
+ let resolveWait = null;
24
+ const waitPromise = new Promise((resolve) => {
25
+ resolveWait = resolve;
26
+ });
27
+ let killed = 0;
28
+ let disconnected = 0;
29
+ return {
30
+ async waitForConnection() { },
31
+ async sendInput(data) {
32
+ inputs.push(typeof data === "string" ? data : new TextDecoder().decode(data));
33
+ },
34
+ wait() {
35
+ return waitPromise;
36
+ },
37
+ async kill() {
38
+ killed += 1;
39
+ },
40
+ async disconnect() {
41
+ disconnected += 1;
42
+ },
43
+ // Test control below. The session never reads these fields.
44
+ emitText(text) {
45
+ onData(encoder.encode(text));
46
+ },
47
+ emitBytes(bytes) {
48
+ onData(bytes);
49
+ },
50
+ finish(exitCode) {
51
+ resolveWait?.({ exitCode });
52
+ },
53
+ get inputs() {
54
+ return inputs;
55
+ },
56
+ get killed() {
57
+ return killed;
58
+ },
59
+ get disconnected() {
60
+ return disconnected;
61
+ },
62
+ };
63
+ }
64
+ /**
65
+ * A fake Daytona process. It opens one fake PTY handle and records the create
66
+ * options, so a test asserts the terminal size and the launch wrapper.
67
+ */
68
+ function createFakeProcess() {
69
+ const state = { handle: null, createOptions: null };
70
+ return {
71
+ get handle() {
72
+ return state.handle;
73
+ },
74
+ get createOptions() {
75
+ return state.createOptions;
76
+ },
77
+ async createPty(options) {
78
+ state.createOptions = options;
79
+ const handle = createFakePtyHandle(options.onData);
80
+ state.handle = handle;
81
+ return handle;
82
+ },
83
+ };
84
+ }
85
+ describe("duplex primitive characterization", () => {
86
+ it("the PTY surface carries host input and process output on one connection", () => {
87
+ const process = createFakeProcess();
88
+ // The PTY surface exposes both directions: `sendInput` for host bytes and the
89
+ // `onData` create option for process bytes. This is the bidirectional shape a
90
+ // duplex channel needs.
91
+ expect(typeof process.createPty).toBe("function");
92
+ const handleKeys = [
93
+ "waitForConnection",
94
+ "sendInput",
95
+ "wait",
96
+ "kill",
97
+ "disconnect",
98
+ ];
99
+ // `sendInput` is the host-to-process direction; the create option `onData` is
100
+ // the process-to-host direction. Both exist on the PTY primitive.
101
+ expect(handleKeys).toContain("sendInput");
102
+ });
103
+ it("the session command surface exposes no write to a running command's stdin", () => {
104
+ // The session surface dispatches a command and tails split stdout/stderr. No
105
+ // member writes bytes to a running command's stdin, so it cannot carry the
106
+ // host input on the output channel. The duplex channel therefore uses the PTY,
107
+ // not a session.
108
+ const sessionMembers = [
109
+ "createSession",
110
+ "executeSessionCommand",
111
+ "getSessionCommandLogs",
112
+ "deleteSession",
113
+ ];
114
+ expect(sessionMembers).not.toContain("sendInput");
115
+ expect(sessionMembers).not.toContain("write");
116
+ // The log tail splits the output into two streams; a single framed duplex
117
+ // stream must stay on one path, which the PTY `onData` provides.
118
+ expect(sessionMembers).toContain("getSessionCommandLogs");
119
+ });
120
+ });
121
+ // ---------------------------------------------------------------------------
122
+ // Launch wrapper: raw mode, echo off, diagnostics to a file, direct exec.
123
+ // ---------------------------------------------------------------------------
124
+ describe("buildDuplexChannelLaunchWrapper", () => {
125
+ it("sets raw mode with echo off, redirects diagnostics to a file, and execs the gateway", () => {
126
+ const wrapper = buildDuplexChannelLaunchWrapper(GATEWAY, "/tmp/diag.log");
127
+ // Raw mode with echo off stops the terminal echoing host input as data and
128
+ // stops newline translation, so the newline-delimited frames stay intact.
129
+ expect(wrapper).toContain("stty raw -echo");
130
+ // The diagnostics go to the file, never to the stdout frame stream. The
131
+ // wrapper quotes the path as a single-quoted shell word.
132
+ expect(wrapper).toContain("2>'/tmp/diag.log'");
133
+ // The gateway starts with `exec`, so the PTY runs it directly and its exit
134
+ // code becomes the PTY exit code.
135
+ expect(wrapper).toContain(`exec ${GATEWAY_QUOTED}`);
136
+ // The line ends with the Enter byte, so the shell runs it.
137
+ expect(wrapper.endsWith(ENTER)).toBe(true);
138
+ // Raw mode is set before the gateway starts.
139
+ expect(wrapper.indexOf("stty raw -echo")).toBeLessThan(wrapper.indexOf(`exec ${GATEWAY_QUOTED}`));
140
+ });
141
+ it("quotes a diagnostics path that holds shell metacharacters, so it cannot inject a command", () => {
142
+ const wrapper = buildDuplexChannelLaunchWrapper(GATEWAY, "/tmp/x; rm -rf ~");
143
+ // The path is one single-quoted word, so the shell reads the metacharacters as
144
+ // literal text. The dangerous `rm` never becomes its own command.
145
+ expect(wrapper).toContain("2>'/tmp/x; rm -rf ~'");
146
+ expect(wrapper).not.toContain("2>/tmp/x; rm -rf ~");
147
+ });
148
+ it("quotes a single quote in the diagnostics path", () => {
149
+ const wrapper = buildDuplexChannelLaunchWrapper(GATEWAY, "/tmp/o'clock.log");
150
+ // A single quote closes the word, adds an escaped quote, and reopens the word.
151
+ expect(wrapper).toContain(`2>'/tmp/o'"'"'clock.log'`);
152
+ });
153
+ it("quotes each command argument that holds shell metacharacters", () => {
154
+ const wrapper = buildDuplexChannelLaunchWrapper(["node", "/paperclip/gateway.mjs", "; rm -rf ~"], "/tmp/diag.log");
155
+ // Each argument is one single-quoted word, so the metacharacters stay literal
156
+ // text. The dangerous `rm` argument never becomes its own command.
157
+ expect(wrapper).toContain(`exec 'node' '/paperclip/gateway.mjs' '; rm -rf ~'`);
158
+ expect(wrapper).not.toContain("; rm -rf ~;");
159
+ expect(wrapper).not.toContain("exec node /paperclip/gateway.mjs ; rm -rf ~");
160
+ });
161
+ it("rejects an empty command argument vector", () => {
162
+ // An empty vector has no program to exec, so the wrapper fails loudly.
163
+ expect(() => buildDuplexChannelLaunchWrapper([], "/tmp/diag.log")).toThrow(/at least one argument/);
164
+ });
165
+ });
166
+ describe("openDaytonaDuplexChannelSession", () => {
167
+ it("starts the gateway through the raw-mode wrapper on a fixed-size terminal", async () => {
168
+ const process = createFakeProcess();
169
+ await openDaytonaDuplexChannelSession(process, GATEWAY);
170
+ expect(process.createOptions?.cols).toBe(120);
171
+ expect(process.createOptions?.rows).toBe(30);
172
+ const launch = process.handle?.inputs[0] ?? "";
173
+ expect(launch).toContain("stty raw -echo");
174
+ expect(launch).toContain(`exec ${GATEWAY_QUOTED}`);
175
+ expect(launch.endsWith(ENTER)).toBe(true);
176
+ });
177
+ it("redirects diagnostics to the given file path", async () => {
178
+ const process = createFakeProcess();
179
+ await openDaytonaDuplexChannelSession(process, GATEWAY, {
180
+ diagnosticsPath: "/tmp/paperclip-duplex-fixed.log",
181
+ });
182
+ expect(process.handle?.inputs[0]).toContain("2>'/tmp/paperclip-duplex-fixed.log'");
183
+ });
184
+ it("defaults the diagnostics path under /tmp when the caller gives none", async () => {
185
+ const process = createFakeProcess();
186
+ await openDaytonaDuplexChannelSession(process, GATEWAY);
187
+ expect(process.handle?.inputs[0]).toMatch(/2>'\/tmp\/paperclip-duplex-[0-9a-f-]+\.log'/);
188
+ });
189
+ it("delivers a host write to the process and does not echo it back as data", async () => {
190
+ const process = createFakeProcess();
191
+ const received = [];
192
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
193
+ session.onData((chunk) => received.push(new TextDecoder().decode(chunk)));
194
+ // The launch wrapper is the first input; the host frame write is the second.
195
+ session.write(new TextEncoder().encode('{"version":1,"type":"heartbeat"}\n'));
196
+ // The chunker awaits each send, so let the write settle before the assertion.
197
+ await new Promise((resolve) => setImmediate(resolve));
198
+ expect(process.handle?.inputs[1]).toBe('{"version":1,"type":"heartbeat"}\n');
199
+ // The fake terminal echoes nothing, so the write produces no data callback.
200
+ // A real terminal in raw mode with echo off behaves the same way.
201
+ expect(received).toEqual([]);
202
+ });
203
+ it("keeps the data order and the frame newlines intact", async () => {
204
+ const process = createFakeProcess();
205
+ const received = [];
206
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
207
+ session.onData((chunk) => received.push(new TextDecoder().decode(chunk)));
208
+ process.handle?.emitText('{"version":1,"type":"ready","address":"127.0.0.1:8080"}\n');
209
+ process.handle?.emitText('{"version":1,"type":"heartbeat"}\n');
210
+ // The order is preserved and no newline is translated (no stray "\r").
211
+ expect(received).toEqual([
212
+ '{"version":1,"type":"ready","address":"127.0.0.1:8080"}\n',
213
+ '{"version":1,"type":"heartbeat"}\n',
214
+ ]);
215
+ expect(received.join("")).not.toContain("\r");
216
+ });
217
+ it("reassembles a frame split across two output chunks", async () => {
218
+ const process = createFakeProcess();
219
+ const received = [];
220
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
221
+ session.onData((chunk) => received.push(new TextDecoder().decode(chunk)));
222
+ const frame = '{"version":1,"type":"response","id":"r1","status":200,"headers":{},"body":"","outcome":"completed"}\n';
223
+ process.handle?.emitText(frame.slice(0, 20));
224
+ process.handle?.emitText(frame.slice(20));
225
+ expect(received.join("")).toBe(frame);
226
+ });
227
+ it("carries a large frame without loss", async () => {
228
+ const process = createFakeProcess();
229
+ const received = [];
230
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
231
+ session.onData((chunk) => received.push(new TextDecoder().decode(chunk)));
232
+ const body = "x".repeat(500_000);
233
+ const frame = `{"version":1,"type":"response","id":"big","status":200,"headers":{},"body":"${body}","outcome":"completed"}\n`;
234
+ process.handle?.emitText(frame);
235
+ expect(received.join("")).toBe(frame);
236
+ expect(received.join("").length).toBe(frame.length);
237
+ });
238
+ it("test_stream_forwards_bytes_without_utf8_conversion", async () => {
239
+ const process = createFakeProcess();
240
+ const received = [];
241
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
242
+ session.onData((chunk) => received.push(chunk));
243
+ // The euro sign is three UTF-8 bytes. Split it across two chunks. The
244
+ // session no longer runs a UTF-8 stream decoder (see `duplex-command-stream.ts`),
245
+ // so it forwards each raw byte chunk unchanged; it does not wait for a whole
246
+ // character. The two chunks concatenate back to the exact original bytes —
247
+ // proof that no layer here converts the data to a string.
248
+ const euro = new TextEncoder().encode("€");
249
+ process.handle?.emitBytes(euro.subarray(0, 2));
250
+ process.handle?.emitBytes(euro.subarray(2));
251
+ expect(received).toHaveLength(2);
252
+ expect(received[0]).toEqual(euro.subarray(0, 2));
253
+ expect(received[1]).toEqual(euro.subarray(2));
254
+ const joined = Buffer.concat(received);
255
+ expect(joined).toEqual(Buffer.from(euro));
256
+ expect(new TextDecoder().decode(joined)).toBe("€");
257
+ });
258
+ it("sends the full 256-value byte corpus through the channel unchanged", async () => {
259
+ const process = createFakeProcess();
260
+ const received = [];
261
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
262
+ session.onData((chunk) => received.push(chunk));
263
+ const allByteValues = Uint8Array.from({ length: 256 }, (_, value) => value);
264
+ process.handle?.emitBytes(allByteValues);
265
+ expect(Buffer.concat(received)).toEqual(Buffer.from(allByteValues));
266
+ });
267
+ it("buffers early output until the listener registers", async () => {
268
+ const process = createFakeProcess();
269
+ const received = [];
270
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
271
+ // Output arrives before the transport registers the listener.
272
+ process.handle?.emitText('{"version":1,');
273
+ process.handle?.emitText('"type":"heartbeat"}\n');
274
+ expect(received).toEqual([]);
275
+ session.onData((chunk) => received.push(new TextDecoder().decode(chunk)));
276
+ // The session flushes the buffered output in order on registration.
277
+ expect(received).toEqual(['{"version":1,"type":"heartbeat"}\n']);
278
+ });
279
+ it("maps a numeric SDK exit code to a process exit", async () => {
280
+ const process = createFakeProcess();
281
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
282
+ process.handle?.finish(3);
283
+ // A numeric exit code is a real process exit, not a transport close.
284
+ await expect(session.wait()).resolves.toEqual({ exitCode: 3, transportClosed: false });
285
+ });
286
+ it("maps an absent SDK exit code to a transport close", async () => {
287
+ const process = createFakeProcess();
288
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
289
+ process.handle?.finish(undefined);
290
+ // A non-numeric SDK result marks a reason-less transport close with no exit
291
+ // data, so the seam reports a transport close, not a process exit.
292
+ await expect(session.wait()).resolves.toEqual({ exitCode: null, transportClosed: true });
293
+ });
294
+ it("splits a write above the provider cap into more than one send under the cap", async () => {
295
+ const process = createFakeProcess();
296
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
297
+ const handle = process.handle;
298
+ if (!handle)
299
+ throw new Error("The fake process opened no handle.");
300
+ // The launch wrapper is the first recorded input. Count only the write's sends.
301
+ const before = handle.inputs.length;
302
+ // A fixed payload well above the provider message cap. It stays fixed, so a
303
+ // raised chunk size above this size makes the write one send and fails this
304
+ // test. The payload is ASCII, so the fake's per-send decode keeps each byte.
305
+ const payload = "x".repeat(150_416);
306
+ session.write(new TextEncoder().encode(payload));
307
+ // The chunker awaits each send, so let the write settle before the assertion.
308
+ await new Promise((resolve) => setImmediate(resolve));
309
+ const sends = handle.inputs.slice(before);
310
+ expect(sends.length).toBeGreaterThan(1);
311
+ for (const chunk of sends) {
312
+ expect(Buffer.byteLength(chunk, "utf8")).toBeLessThanOrEqual(PTY_INPUT_CHUNK_BYTES);
313
+ expect(Buffer.byteLength(chunk, "utf8")).toBeLessThanOrEqual(PTY_MESSAGE_CAP_BYTES);
314
+ }
315
+ // The sends rejoin to the exact payload, so the chunker loses no byte.
316
+ expect(sends.join("")).toBe(payload);
317
+ });
318
+ it("sends a write below the provider cap as exactly one send", async () => {
319
+ const process = createFakeProcess();
320
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
321
+ const handle = process.handle;
322
+ if (!handle)
323
+ throw new Error("The fake process opened no handle.");
324
+ const before = handle.inputs.length;
325
+ session.write(new TextEncoder().encode('{"version":1,"type":"heartbeat"}\n'));
326
+ // The chunker awaits each send, so let the write settle before the assertion.
327
+ await new Promise((resolve) => setImmediate(resolve));
328
+ expect(handle.inputs.slice(before)).toHaveLength(1);
329
+ });
330
+ it("keeps two writes in order and never interleaves their chunks", async () => {
331
+ // A handle whose `sendInput` yields a microtask before it records the chunk.
332
+ // The yield opens a window where a second write could jump ahead. The session
333
+ // chains the writes, so the second write starts only after the first ends.
334
+ const inputs = [];
335
+ const handle = {
336
+ async waitForConnection() { },
337
+ async sendInput(data) {
338
+ await Promise.resolve();
339
+ inputs.push(typeof data === "string" ? data : new TextDecoder().decode(data));
340
+ },
341
+ wait() {
342
+ return new Promise(() => { });
343
+ },
344
+ async kill() { },
345
+ async disconnect() { },
346
+ };
347
+ const process = {
348
+ async createPty() {
349
+ return handle;
350
+ },
351
+ };
352
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
353
+ // Two payloads above the cap, so each write is more than one chunk. "a" marks
354
+ // the first write and "b" marks the second write.
355
+ const first = "a".repeat(150_416);
356
+ const second = "b".repeat(150_416);
357
+ session.write(new TextEncoder().encode(first));
358
+ session.write(new TextEncoder().encode(second));
359
+ // Let both writes settle.
360
+ await new Promise((resolve) => setImmediate(resolve));
361
+ // Drop the launch wrapper (the first input) and rejoin the chunks. The rejoin
362
+ // equals the first payload then the second payload, so no chunk of the second
363
+ // write lands between the chunks of the first write.
364
+ expect(inputs.slice(1).join("")).toBe(`${first}${second}`);
365
+ });
366
+ it("kills the child on stop and releases the socket on close", async () => {
367
+ const process = createFakeProcess();
368
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY);
369
+ session.kill();
370
+ expect(process.handle?.killed).toBe(1);
371
+ await session.close();
372
+ // Close stops the child and releases the socket, so a closed channel holds no
373
+ // live terminal.
374
+ expect(process.handle?.killed).toBeGreaterThanOrEqual(1);
375
+ expect(process.handle?.disconnected).toBe(1);
376
+ });
377
+ it("passes the working directory to the pseudo-terminal", async () => {
378
+ const process = createFakeProcess();
379
+ await openDaytonaDuplexChannelSession(process, GATEWAY, { cwd: "/paperclip-workspace" });
380
+ expect(process.createOptions?.cwd).toBe("/paperclip-workspace");
381
+ });
382
+ it("ends the channel with the typed write_error and no raw text when a write rejects", async () => {
383
+ // The fake handle accepts the launch wrapper write, then rejects every later
384
+ // write with a raw provider message. The seam must map the cause to the typed
385
+ // reason and never surface the raw text.
386
+ const rawProviderText = "RAW-PROVIDER-BROKEN-PIPE-9z8y7x";
387
+ let sends = 0;
388
+ let killed = 0;
389
+ const handle = {
390
+ async waitForConnection() { },
391
+ async sendInput() {
392
+ sends += 1;
393
+ if (sends === 1)
394
+ return; // the launch wrapper write succeeds.
395
+ throw new Error(rawProviderText);
396
+ },
397
+ wait() {
398
+ return new Promise(() => { });
399
+ },
400
+ async kill() {
401
+ killed += 1;
402
+ },
403
+ async disconnect() { },
404
+ };
405
+ const process = {
406
+ async createPty() {
407
+ return handle;
408
+ },
409
+ };
410
+ const writeErrors = [];
411
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY, {
412
+ onWriteError: (reason) => writeErrors.push(reason),
413
+ });
414
+ session.write(new TextEncoder().encode("frame\n"));
415
+ // Let the rejected write settle.
416
+ await new Promise((resolve) => setImmediate(resolve));
417
+ // The seam reported exactly the typed reason, ended the channel, and leaked no
418
+ // raw provider text.
419
+ expect(writeErrors).toEqual(["write_error"]);
420
+ expect(killed).toBe(1);
421
+ expect(JSON.stringify(writeErrors)).not.toContain(rawProviderText);
422
+ });
423
+ it("reports a write error one time even when more writes reject", async () => {
424
+ let sends = 0;
425
+ let killed = 0;
426
+ const handle = {
427
+ async waitForConnection() { },
428
+ async sendInput() {
429
+ sends += 1;
430
+ if (sends === 1)
431
+ return;
432
+ throw new Error("broken");
433
+ },
434
+ wait() {
435
+ return new Promise(() => { });
436
+ },
437
+ async kill() {
438
+ killed += 1;
439
+ },
440
+ async disconnect() { },
441
+ };
442
+ const process = {
443
+ async createPty() {
444
+ return handle;
445
+ },
446
+ };
447
+ const writeErrors = [];
448
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY, {
449
+ onWriteError: (reason) => writeErrors.push(reason),
450
+ });
451
+ session.write(new TextEncoder().encode("a"));
452
+ session.write(new TextEncoder().encode("b"));
453
+ await new Promise((resolve) => setImmediate(resolve));
454
+ expect(writeErrors).toEqual(["write_error"]);
455
+ expect(killed).toBe(1);
456
+ });
457
+ it("does not send a queued write after a first-send rejection terminalizes the channel", async () => {
458
+ // Two writes queue on the chain. The launch wrapper write succeeds, then the
459
+ // first frame write rejects and terminalizes the channel. The second queued
460
+ // write must not reach `sendInput`, so no write runs on the closed transport.
461
+ const sends = [];
462
+ let killed = 0;
463
+ const handle = {
464
+ async waitForConnection() { },
465
+ async sendInput(data) {
466
+ const text = typeof data === "string" ? data : new TextDecoder().decode(data);
467
+ sends.push(text);
468
+ // The launch wrapper write (the first send) succeeds. The first frame
469
+ // write (the second send) rejects and terminalizes the channel.
470
+ if (sends.length === 1)
471
+ return;
472
+ throw new Error("broken");
473
+ },
474
+ wait() {
475
+ return new Promise(() => { });
476
+ },
477
+ async kill() {
478
+ killed += 1;
479
+ },
480
+ async disconnect() { },
481
+ };
482
+ const process = {
483
+ async createPty() {
484
+ return handle;
485
+ },
486
+ };
487
+ const writeErrors = [];
488
+ const session = await openDaytonaDuplexChannelSession(process, GATEWAY, {
489
+ onWriteError: (reason) => writeErrors.push(reason),
490
+ });
491
+ session.write(new TextEncoder().encode("first\n"));
492
+ session.write(new TextEncoder().encode("second\n"));
493
+ // Let the write chain settle both queued writes.
494
+ await new Promise((resolve) => setImmediate(resolve));
495
+ await new Promise((resolve) => setImmediate(resolve));
496
+ // The channel terminalized one time on the first frame write. The second
497
+ // queued write sent no chunk, so exactly two sends ran: the launch wrapper and
498
+ // the first frame write. The second frame never reached the transport.
499
+ expect(writeErrors).toEqual(["write_error"]);
500
+ expect(killed).toBe(1);
501
+ expect(sends).toHaveLength(2);
502
+ expect(sends.some((text) => text.includes("second"))).toBe(false);
503
+ });
504
+ });
505
+ describe("createDaytonaDuplexChannelSessionOpener", () => {
506
+ it("opens a session for the command on each call", async () => {
507
+ const process = createFakeProcess();
508
+ const opener = createDaytonaDuplexChannelSessionOpener(process, { cwd: "/paperclip-workspace" });
509
+ const session = await opener(GATEWAY);
510
+ expect(process.createOptions?.cwd).toBe("/paperclip-workspace");
511
+ expect(process.handle?.inputs[0]).toContain(`exec ${GATEWAY_QUOTED}`);
512
+ expect(typeof session.onData).toBe("function");
513
+ expect(typeof session.write).toBe("function");
514
+ expect(typeof session.wait).toBe("function");
515
+ expect(typeof session.kill).toBe("function");
516
+ expect(typeof session.close).toBe("function");
517
+ });
518
+ });
519
+ //# sourceMappingURL=duplex-command-stream.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplex-command-stream.test.js","sourceRoot":"","sources":["../src/duplex-command-stream.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import type { Sandbox } from "@daytonaio/sdk";
2
+ import type { PluginEnvironmentSyncResult, PluginSpan, PluginSyncOperation } from "@tickernelz/paperclip-pro-plugin-sdk";
3
+ /**
4
+ * Run one span-wrapped step through the plugin tracer. The pack step, the
5
+ * transfer step, and each command round trip share this helper. It seeds the
6
+ * provider family, runs the step, marks a thrown step failed, and always ends
7
+ * the span. The host records the span with its true wall-clock width from the
8
+ * worker timestamps, so the span shows real time in the trace. The tracer is a
9
+ * no-op until the host injects a live tracer, so the span never changes the sync
10
+ * control flow.
11
+ *
12
+ * `wallMsAttr` is optional. The `pack` and `transfer` spans pass it to keep
13
+ * their existing `*.wall_ms` attribute. A per-round-trip span omits it, so it
14
+ * carries no `*.wall_ms` attribute and relies on the native span width.
15
+ *
16
+ * `run` receives the live span, so a caller that needs to record an attribute
17
+ * only known after the step completes (for example the compression byte
18
+ * counts) can call `span.setAttribute` directly, without a second span.
19
+ */
20
+ export declare function withProviderSpan<T>(input: {
21
+ name: string;
22
+ wallMsAttr?: string;
23
+ attributes?: Record<string, string | number | boolean>;
24
+ run: (span: PluginSpan) => Promise<T>;
25
+ }): Promise<T>;
26
+ /**
27
+ * Host-side complete-mediation guard applied as defense-in-depth below the
28
+ * orchestrator's own confinement. Every sandbox-side path (the sync target for
29
+ * inbound, the sync source for outbound) MUST canonicalize inside the workspace
30
+ * remote dir; absolute escapes and `..` traversal are rejected fail-closed before
31
+ * any bytes move. Sandbox paths on the server are POSIX.
32
+ */
33
+ export declare function assertConfinedSandboxPath(remoteDir: string, candidate: string, label: string): void;
34
+ /**
35
+ * Parse one `tar -tvf` verbose listing line into its leading type flag and the
36
+ * trailing name-and-link-target field. The listing dialect depends on which tar
37
+ * the host ships: GNU/busybox emit
38
+ * `<perms> <owner>/<group> <size> <date> <time> <rest>`, while bsdtar
39
+ * (libarchive — the system tar on macOS) emits the ls-style
40
+ * `<perms> <links> <user> <group> <size> <Mon> <day> <time|year> <rest>`.
41
+ * The second field disambiguates: GNU always slash-joins owner/group, bsdtar
42
+ * puts a pure-digit link count there, so no line satisfies both shapes — the
43
+ * slash requirement is load-bearing, since a bsdtar line with numeric uid/gid
44
+ * would otherwise match the GNU shape shifted, hiding traversal in `<rest>`.
45
+ * Entries whose size column is not a plain byte count (e.g. a device node's
46
+ * `major,minor`) match neither shape. Returns null when nothing matches so
47
+ * callers can fail closed.
48
+ */
49
+ export declare function parseTarVerboseListingLine(line: string): {
50
+ typeFlag: string;
51
+ rest: string;
52
+ } | null;
53
+ /**
54
+ * Split a verbose-listing link field (`<name><delimiter><target>`) exactly
55
+ * once. The sandbox controls both halves, so a field with zero or multiple
56
+ * delimiter occurrences is unresolvable: a link name that itself contains the
57
+ * delimiter shifts the split point, and taking the first (or last) occurrence
58
+ * would let a crafted name or target hide an escaping link target from the
59
+ * confinement check. Returns null so callers fail closed.
60
+ */
61
+ export declare function splitLinkEntryOnce(field: string, delimiter: string): {
62
+ name: string;
63
+ target: string;
64
+ } | null;
65
+ export declare function performSyncIn(input: {
66
+ sandbox: Sandbox;
67
+ operations: PluginSyncOperation[];
68
+ remoteDir: string;
69
+ timeoutSeconds: number;
70
+ }): Promise<PluginEnvironmentSyncResult>;
71
+ export declare function performSyncOut(input: {
72
+ sandbox: Sandbox;
73
+ operations: PluginSyncOperation[];
74
+ remoteDir: string;
75
+ timeoutSeconds: number;
76
+ }): Promise<PluginEnvironmentSyncResult>;
77
+ //# sourceMappingURL=file-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-sync.d.ts","sourceRoot":"","sources":["../src/file-sync.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAyD,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACrG,OAAO,KAAK,EACV,2BAA2B,EAE3B,UAAU,EAEV,mBAAmB,EACpB,MAAM,sCAAsC,CAAC;AAgC9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,KAAK,EAAE;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACvD,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACvC,GAAG,OAAO,CAAC,CAAC,CAAC,CAcb;AAoCD;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAenG;AA8DD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAMlG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAK5G;AAqvBD,wBAAsB,aAAa,CAAC,KAAK,EAAE;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAyCvC;AA4LD,wBAAsB,cAAc,CAAC,KAAK,EAAE;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAgCvC"}