@ricsam/r5d-worker 0.0.68 → 0.0.69

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/dist/cjs/main.cjs CHANGED
@@ -62,6 +62,7 @@ var import_cli_update = require("./cli-update.cjs");
62
62
  var import_git_identity = require("./git-identity.cjs");
63
63
  var import_heartbeat = require("./heartbeat.cjs");
64
64
  var import_process_tree = require("./process-tree.cjs");
65
+ var import_pty_output_coalescer = require("./pty-output-coalescer.cjs");
65
66
  var import_port_forward_client = require("./port-forward-client.cjs");
66
67
  var import_workspace_incident_state = require("./workspace-incident-state.cjs");
67
68
  var import_workspace_command_sync_policy = require("./workspace-command-sync-policy.cjs");
@@ -2308,6 +2309,15 @@ async function openPty(input) {
2308
2309
  }) : {};
2309
2310
  const targetProcessEnv = targetIdentityEnv(input.resolvedTarget.target);
2310
2311
  const shell = resolveHostShell(input.message.command);
2312
+ const outputCoalescer = (0, import_pty_output_coalescer.createPtyOutputCoalescer)({
2313
+ send: (data) => {
2314
+ sendWorkerMessage(input.ws, {
2315
+ type: "pty_output",
2316
+ ptyId: input.message.ptyId,
2317
+ data
2318
+ });
2319
+ }
2320
+ });
2311
2321
  const ptyProcess = createNodePtyBridge({
2312
2322
  file: shell.file,
2313
2323
  args: shell.args,
@@ -2332,13 +2342,10 @@ async function openPty(input) {
2332
2342
  });
2333
2343
  },
2334
2344
  onOutput: (data) => {
2335
- sendWorkerMessage(input.ws, {
2336
- type: "pty_output",
2337
- ptyId: input.message.ptyId,
2338
- data
2339
- });
2345
+ outputCoalescer.push(data);
2340
2346
  },
2341
2347
  onExit: (event) => {
2348
+ outputCoalescer.flush();
2342
2349
  input.releaseWorkspaceMutation?.();
2343
2350
  activePtys.delete(input.message.ptyId);
2344
2351
  input.onTerminal?.();
@@ -2350,6 +2357,7 @@ async function openPty(input) {
2350
2357
  });
2351
2358
  },
2352
2359
  onError: (error) => {
2360
+ outputCoalescer.flush();
2353
2361
  input.releaseWorkspaceMutation?.();
2354
2362
  activePtys.delete(input.message.ptyId);
2355
2363
  input.onTerminal?.();
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "type": "commonjs"
5
5
  }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var pty_output_coalescer_exports = {};
20
+ __export(pty_output_coalescer_exports, {
21
+ DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS: () => DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS,
22
+ DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES: () => DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES,
23
+ createPtyOutputCoalescer: () => createPtyOutputCoalescer
24
+ });
25
+ module.exports = __toCommonJS(pty_output_coalescer_exports);
26
+ const DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS = 8;
27
+ const DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES = 64 * 1024;
28
+ function defaultSchedule(callback, delayMs) {
29
+ const timer = setTimeout(callback, delayMs);
30
+ timer.unref();
31
+ return () => clearTimeout(timer);
32
+ }
33
+ function createPtyOutputCoalescer(options) {
34
+ const flushIntervalMs = options.flushIntervalMs ?? DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS;
35
+ const maxBufferBytes = options.maxBufferBytes ?? DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES;
36
+ const now = options.now ?? Date.now;
37
+ const schedule = options.schedule ?? defaultSchedule;
38
+ let buffer = "";
39
+ let bufferedBytes = 0;
40
+ let cancelScheduledFlush = null;
41
+ let lastSendAt = Number.NEGATIVE_INFINITY;
42
+ const sendNow = (data) => {
43
+ lastSendAt = now();
44
+ options.send(data);
45
+ };
46
+ const flush = () => {
47
+ if (cancelScheduledFlush) {
48
+ cancelScheduledFlush();
49
+ cancelScheduledFlush = null;
50
+ }
51
+ if (bufferedBytes === 0) {
52
+ return;
53
+ }
54
+ const data = buffer;
55
+ buffer = "";
56
+ bufferedBytes = 0;
57
+ sendNow(data);
58
+ };
59
+ const push = (data) => {
60
+ if (data.length === 0) {
61
+ return;
62
+ }
63
+ if (bufferedBytes === 0 && now() - lastSendAt >= flushIntervalMs) {
64
+ sendNow(data);
65
+ return;
66
+ }
67
+ buffer += data;
68
+ bufferedBytes += Buffer.byteLength(data, "utf8");
69
+ if (bufferedBytes >= maxBufferBytes) {
70
+ flush();
71
+ return;
72
+ }
73
+ if (!cancelScheduledFlush) {
74
+ cancelScheduledFlush = schedule(() => {
75
+ cancelScheduledFlush = null;
76
+ flush();
77
+ }, flushIntervalMs);
78
+ }
79
+ };
80
+ return { push, flush };
81
+ }
82
+ // Annotate the CommonJS export names for ESM import in node:
83
+ 0 && (module.exports = {
84
+ DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS,
85
+ DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES,
86
+ createPtyOutputCoalescer
87
+ });
package/dist/mjs/main.mjs CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  WORKER_HEARTBEAT_INTERVAL_MS
13
13
  } from "./heartbeat.mjs";
14
14
  import { terminateProcessTree } from "./process-tree.mjs";
15
+ import { createPtyOutputCoalescer } from "./pty-output-coalescer.mjs";
15
16
  import { openWorkerPortForwardRelay } from "./port-forward-client.mjs";
16
17
  import { applyWorkspaceIncidentUpdate, releasePendingWorkspaceHead, WorkspaceIncidentOrderingFence } from "./workspace-incident-state.mjs";
17
18
  import { acquireWorkspaceCommandMutation, runWorkspaceCommand } from "./workspace-command-sync-policy.mjs";
@@ -2279,6 +2280,15 @@ async function openPty(input) {
2279
2280
  }) : {};
2280
2281
  const targetProcessEnv = targetIdentityEnv(input.resolvedTarget.target);
2281
2282
  const shell = resolveHostShell(input.message.command);
2283
+ const outputCoalescer = createPtyOutputCoalescer({
2284
+ send: (data) => {
2285
+ sendWorkerMessage(input.ws, {
2286
+ type: "pty_output",
2287
+ ptyId: input.message.ptyId,
2288
+ data
2289
+ });
2290
+ }
2291
+ });
2282
2292
  const ptyProcess = createNodePtyBridge({
2283
2293
  file: shell.file,
2284
2294
  args: shell.args,
@@ -2303,13 +2313,10 @@ async function openPty(input) {
2303
2313
  });
2304
2314
  },
2305
2315
  onOutput: (data) => {
2306
- sendWorkerMessage(input.ws, {
2307
- type: "pty_output",
2308
- ptyId: input.message.ptyId,
2309
- data
2310
- });
2316
+ outputCoalescer.push(data);
2311
2317
  },
2312
2318
  onExit: (event) => {
2319
+ outputCoalescer.flush();
2313
2320
  input.releaseWorkspaceMutation?.();
2314
2321
  activePtys.delete(input.message.ptyId);
2315
2322
  input.onTerminal?.();
@@ -2321,6 +2328,7 @@ async function openPty(input) {
2321
2328
  });
2322
2329
  },
2323
2330
  onError: (error) => {
2331
+ outputCoalescer.flush();
2324
2332
  input.releaseWorkspaceMutation?.();
2325
2333
  activePtys.delete(input.message.ptyId);
2326
2334
  input.onTerminal?.();
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,61 @@
1
+ const DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS = 8;
2
+ const DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES = 64 * 1024;
3
+ function defaultSchedule(callback, delayMs) {
4
+ const timer = setTimeout(callback, delayMs);
5
+ timer.unref();
6
+ return () => clearTimeout(timer);
7
+ }
8
+ function createPtyOutputCoalescer(options) {
9
+ const flushIntervalMs = options.flushIntervalMs ?? DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS;
10
+ const maxBufferBytes = options.maxBufferBytes ?? DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES;
11
+ const now = options.now ?? Date.now;
12
+ const schedule = options.schedule ?? defaultSchedule;
13
+ let buffer = "";
14
+ let bufferedBytes = 0;
15
+ let cancelScheduledFlush = null;
16
+ let lastSendAt = Number.NEGATIVE_INFINITY;
17
+ const sendNow = (data) => {
18
+ lastSendAt = now();
19
+ options.send(data);
20
+ };
21
+ const flush = () => {
22
+ if (cancelScheduledFlush) {
23
+ cancelScheduledFlush();
24
+ cancelScheduledFlush = null;
25
+ }
26
+ if (bufferedBytes === 0) {
27
+ return;
28
+ }
29
+ const data = buffer;
30
+ buffer = "";
31
+ bufferedBytes = 0;
32
+ sendNow(data);
33
+ };
34
+ const push = (data) => {
35
+ if (data.length === 0) {
36
+ return;
37
+ }
38
+ if (bufferedBytes === 0 && now() - lastSendAt >= flushIntervalMs) {
39
+ sendNow(data);
40
+ return;
41
+ }
42
+ buffer += data;
43
+ bufferedBytes += Buffer.byteLength(data, "utf8");
44
+ if (bufferedBytes >= maxBufferBytes) {
45
+ flush();
46
+ return;
47
+ }
48
+ if (!cancelScheduledFlush) {
49
+ cancelScheduledFlush = schedule(() => {
50
+ cancelScheduledFlush = null;
51
+ flush();
52
+ }, flushIntervalMs);
53
+ }
54
+ };
55
+ return { push, flush };
56
+ }
57
+ export {
58
+ DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS,
59
+ DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES,
60
+ createPtyOutputCoalescer
61
+ };
@@ -0,0 +1,20 @@
1
+ export interface PtyOutputCoalescerOptions {
2
+ send: (data: string) => void;
3
+ flushIntervalMs?: number;
4
+ maxBufferBytes?: number;
5
+ now?: () => number;
6
+ schedule?: (callback: () => void, delayMs: number) => () => void;
7
+ }
8
+ export interface PtyOutputCoalescer {
9
+ push(data: string): void;
10
+ flush(): void;
11
+ }
12
+ export declare const DEFAULT_PTY_OUTPUT_FLUSH_INTERVAL_MS = 8;
13
+ export declare const DEFAULT_PTY_OUTPUT_MAX_BUFFER_BYTES: number;
14
+ /**
15
+ * Batches PTY output into fewer, larger relay messages. An isolated chunk
16
+ * (an interactive keystroke echo) is sent immediately so coalescing adds no
17
+ * input latency; only sustained bursts (build logs, `cat` of a large file)
18
+ * are buffered, bounded by the flush interval and the buffer byte cap.
19
+ */
20
+ export declare function createPtyOutputCoalescer(options: PtyOutputCoalescerOptions): PtyOutputCoalescer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5d-worker",
3
- "version": "0.0.68",
3
+ "version": "0.0.69",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/main.cjs",
6
6
  "module": "./dist/mjs/main.mjs",