ragent-cli 1.11.8 → 1.11.9

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/index.js CHANGED
@@ -31,7 +31,7 @@ var require_package = __commonJS({
31
31
  "package.json"(exports2, module2) {
32
32
  module2.exports = {
33
33
  name: "ragent-cli",
34
- version: "1.11.8",
34
+ version: "1.11.9",
35
35
  description: "CLI agent for rAgent Live \u2014 browser-first terminal control plane for AI coding agents",
36
36
  main: "dist/index.js",
37
37
  bin: {
@@ -2934,6 +2934,24 @@ var ALT_SCREEN_MODE_RE = /\x1b\[\?(?=[0-9;]*(?:47|1047|1048|1049)(?:;|[hl]))[0-9
2934
2934
  function stripAlternateScreenModeSwitches(data) {
2935
2935
  return data.replace(ALT_SCREEN_MODE_RE, "");
2936
2936
  }
2937
+ var AlternateScreenModeStripper = class {
2938
+ pending = "";
2939
+ write(data) {
2940
+ const combined = this.pending + data;
2941
+ this.pending = "";
2942
+ const match = combined.match(/\x1b(?:\[(?:\?(?:[0-9;]*)?)?)?$/);
2943
+ if (!match || match.index === void 0) {
2944
+ return stripAlternateScreenModeSwitches(combined);
2945
+ }
2946
+ this.pending = combined.slice(match.index);
2947
+ return stripAlternateScreenModeSwitches(combined.slice(0, match.index));
2948
+ }
2949
+ end() {
2950
+ const pending = this.pending;
2951
+ this.pending = "";
2952
+ return stripAlternateScreenModeSwitches(pending);
2953
+ }
2954
+ };
2937
2955
  function parsePaneTarget(sessionId) {
2938
2956
  if (!sessionId.startsWith("tmux:")) return null;
2939
2957
  const rest = sessionId.slice("tmux:".length);
@@ -3172,6 +3190,11 @@ var SessionStreamer = class {
3172
3190
  isStreaming(sessionId) {
3173
3191
  return this.active.has(sessionId);
3174
3192
  }
3193
+ hasViewer(sessionId, viewerId) {
3194
+ const viewerKey = viewerId?.trim() || "";
3195
+ if (!viewerKey) return false;
3196
+ return this.active.get(sessionId)?.viewerIds.has(viewerKey) ?? false;
3197
+ }
3175
3198
  /**
3176
3199
  * Stop all active streams immediately (no debounce).
3177
3200
  */
@@ -3253,11 +3276,19 @@ var SessionStreamer = class {
3253
3276
  * Used when a viewer missed the initial burst (e.g. joinGroup race)
3254
3277
  * or reconnected ("Retry stream"). Does NOT restart pipe-pane.
3255
3278
  */
3256
- resyncStream(sessionId) {
3279
+ resyncStream(sessionId, viewerId) {
3257
3280
  const stream = this.active.get(sessionId);
3258
3281
  if (!stream || stream.stopped || stream.streamType !== "tmux-pipe") return false;
3259
3282
  const { paneTarget, cleanEnv } = stream;
3260
3283
  if (!paneTarget || !cleanEnv) return false;
3284
+ const targetViewerId = viewerId?.trim() || void 0;
3285
+ const send = (data) => {
3286
+ if (targetViewerId) {
3287
+ this.sendFn(sessionId, data, targetViewerId);
3288
+ } else {
3289
+ this.sendFn(sessionId, data);
3290
+ }
3291
+ };
3261
3292
  stream.initializing = true;
3262
3293
  try {
3263
3294
  let alternateScreen = false;
@@ -3277,11 +3308,11 @@ var SessionStreamer = class {
3277
3308
  { env: cleanEnv, timeout: 1e4, encoding: "utf-8" }
3278
3309
  );
3279
3310
  if (scrollback && scrollback.length > 0) {
3280
- this.sendFn(sessionId, scrollback.replace(/\n/g, "\r\n"));
3311
+ send(scrollback.replace(/\n/g, "\r\n"));
3281
3312
  }
3282
3313
  } catch {
3283
3314
  }
3284
- this.sendFn(sessionId, "\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
3315
+ send("\x1B[?1049l\x1B[0m\x1B[2J\x1B[H");
3285
3316
  try {
3286
3317
  const captureArgs = alternateScreen ? ["capture-pane", "-a", "-t", paneTarget, "-p", "-e"] : ["capture-pane", "-t", paneTarget, "-p", "-e"];
3287
3318
  const initial = (0, import_node_child_process2.execFileSync)(
@@ -3290,7 +3321,7 @@ var SessionStreamer = class {
3290
3321
  { env: cleanEnv, timeout: 5e3, encoding: "utf-8" }
3291
3322
  );
3292
3323
  if (initial) {
3293
- this.sendFn(sessionId, stripAlternateScreenModeSwitches(initial.replace(/\n/g, "\r\n").replace(/\r\n$/, "")));
3324
+ send(stripAlternateScreenModeSwitches(initial.replace(/\n/g, "\r\n").replace(/\r\n$/, "")));
3294
3325
  }
3295
3326
  } catch {
3296
3327
  }
@@ -3305,7 +3336,7 @@ var SessionStreamer = class {
3305
3336
  const x = parseInt(parts[0], 10);
3306
3337
  const y = parseInt(parts[1], 10);
3307
3338
  if (!isNaN(x) && !isNaN(y)) {
3308
- this.sendFn(sessionId, `\x1B[${y + 1};${x + 1}H`);
3339
+ send(`\x1B[${y + 1};${x + 1}H`);
3309
3340
  }
3310
3341
  }
3311
3342
  } catch {
@@ -3359,7 +3390,8 @@ var SessionStreamer = class {
3359
3390
  cleanEnv,
3360
3391
  ptyProc: null,
3361
3392
  straceProc: null,
3362
- utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
3393
+ utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
3394
+ altScreenStripper: new AlternateScreenModeStripper()
3363
3395
  };
3364
3396
  this.active.set(sessionId, stream);
3365
3397
  try {
@@ -3383,13 +3415,19 @@ var SessionStreamer = class {
3383
3415
  if (stream.initializing) {
3384
3416
  stream.initBuffer.push(data);
3385
3417
  } else {
3386
- this.sendFn(sessionId, stripAlternateScreenModeSwitches(data));
3418
+ const filtered = stream.altScreenStripper.write(data);
3419
+ if (filtered) this.sendFn(sessionId, filtered);
3387
3420
  }
3388
3421
  });
3389
3422
  catProc.on("exit", () => {
3390
3423
  if (!stream.stopped) {
3391
3424
  const remaining = stream.utf8Decoder.end();
3392
- if (remaining) this.sendFn(sessionId, stripAlternateScreenModeSwitches(remaining));
3425
+ if (remaining) {
3426
+ const filtered = stream.altScreenStripper.write(remaining);
3427
+ if (filtered) this.sendFn(sessionId, filtered);
3428
+ }
3429
+ const pending = stream.altScreenStripper.end();
3430
+ if (pending) this.sendFn(sessionId, pending);
3393
3431
  this.cleanupStream(stream);
3394
3432
  this.active.delete(sessionId);
3395
3433
  this.pendingStops.delete(sessionId);
@@ -3485,7 +3523,8 @@ var SessionStreamer = class {
3485
3523
  initBuffer: [],
3486
3524
  ptyProc: proc,
3487
3525
  straceProc: null,
3488
- utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
3526
+ utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
3527
+ altScreenStripper: new AlternateScreenModeStripper()
3489
3528
  };
3490
3529
  this.active.set(sessionId, stream);
3491
3530
  proc.onData((data) => {
@@ -3538,7 +3577,8 @@ var SessionStreamer = class {
3538
3577
  initBuffer: [],
3539
3578
  ptyProc: proc,
3540
3579
  straceProc: null,
3541
- utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
3580
+ utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
3581
+ altScreenStripper: new AlternateScreenModeStripper()
3542
3582
  };
3543
3583
  this.active.set(sessionId, stream);
3544
3584
  proc.onData((data) => {
@@ -3603,7 +3643,8 @@ var SessionStreamer = class {
3603
3643
  initBuffer: [],
3604
3644
  ptyProc: null,
3605
3645
  straceProc,
3606
- utf8Decoder: new import_node_string_decoder.StringDecoder("utf8")
3646
+ utf8Decoder: new import_node_string_decoder.StringDecoder("utf8"),
3647
+ altScreenStripper: new AlternateScreenModeStripper()
3607
3648
  };
3608
3649
  this.active.set(sessionId, stream);
3609
3650
  const writeRegex = /^write\(([12]),\s*"((?:[^"\\]|\\.)*)"/;
@@ -5558,13 +5599,15 @@ var ControlDispatcher = class {
5558
5599
  if (!sessionId) return;
5559
5600
  const ws = this.connection.activeSocket;
5560
5601
  const group = this.connection.activeGroups.privateGroup;
5602
+ const viewerKey = viewerId?.trim() || "";
5561
5603
  if (sessionId.startsWith("tmux:") || sessionId.startsWith("screen:") || sessionId.startsWith("zellij:") || sessionId.startsWith("process:")) {
5562
5604
  const alreadyStreaming = this.streamer.isStreaming(sessionId);
5605
+ const viewerAlreadyAttached = alreadyStreaming && this.streamer.hasViewer(sessionId, viewerKey);
5563
5606
  const started = this.streamer.startStream(sessionId, viewerId ?? void 0);
5564
5607
  if (ws && ws.readyState === import_ws4.default.OPEN && group) {
5565
5608
  if (started) {
5566
- if (alreadyStreaming) {
5567
- this.streamer.resyncStream(sessionId);
5609
+ if (alreadyStreaming && !viewerAlreadyAttached) {
5610
+ this.streamer.resyncStream(sessionId, viewerKey);
5568
5611
  }
5569
5612
  const dims = this.queryPaneDimensions(sessionId);
5570
5613
  sendToGroup(ws, group, { type: "stream-started", sessionId, ...dims });
@@ -6033,7 +6076,7 @@ async function runAgent(rawOptions) {
6033
6076
  const outputBuffer = new OutputBuffer();
6034
6077
  const ptySessionId = `pty:${options.hostId}`;
6035
6078
  const sessionStreamer = new SessionStreamer(
6036
- (sessionId, data) => {
6079
+ (sessionId, data, viewerId) => {
6037
6080
  conn.requestInventorySync();
6038
6081
  if (!conn.isReady()) {
6039
6082
  sessionStreamer.bufferOutput(sessionId, data);
@@ -6041,11 +6084,12 @@ async function runAgent(rawOptions) {
6041
6084
  }
6042
6085
  const redactedData = redactStreamingChunk(sessionId, data);
6043
6086
  for (const chunk of splitTransportChunks(redactedData)) {
6087
+ const outputPayload = viewerId ? { type: "output", sessionId, viewerId } : { type: "output", sessionId };
6044
6088
  if (conn.sessionKey) {
6045
6089
  const { enc, iv, seq } = encryptPayload(chunk, conn.sessionKey, conn.txSeq.next());
6046
- sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { type: "output", enc, iv, seq, sessionId });
6090
+ sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { ...outputPayload, enc, iv, seq });
6047
6091
  } else {
6048
- sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { type: "output", data: chunk, sessionId });
6092
+ sendToGroup(conn.activeSocket, conn.activeGroups.privateGroup, { ...outputPayload, data: chunk });
6049
6093
  }
6050
6094
  }
6051
6095
  },
package/dist/sbom.json CHANGED
@@ -1166,8 +1166,8 @@
1166
1166
  {
1167
1167
  "type": "library",
1168
1168
  "name": "ragent-cli",
1169
- "version": "1.11.8",
1170
- "bom-ref": "ragent-live|ragent-cli@1.11.8",
1169
+ "version": "1.11.9",
1170
+ "bom-ref": "ragent-live|ragent-cli@1.11.9",
1171
1171
  "author": "Intellimetrics",
1172
1172
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
1173
1173
  "licenses": [
@@ -1178,7 +1178,7 @@
1178
1178
  }
1179
1179
  }
1180
1180
  ],
1181
- "purl": "pkg:npm/ragent-cli@1.11.8",
1181
+ "purl": "pkg:npm/ragent-cli@1.11.9",
1182
1182
  "externalReferences": [
1183
1183
  {
1184
1184
  "url": "https://github.com/chadlindell/ragent-live/issues",
@@ -1303,7 +1303,7 @@
1303
1303
  "ragent-live|@emnapi/wasi-threads@1.2.1",
1304
1304
  "ragent-live|@pkgjs/parseargs@0.11.0",
1305
1305
  "ragent-live|@tybys/wasm-util@0.10.1",
1306
- "ragent-live|ragent-cli@1.11.8"
1306
+ "ragent-live|ragent-cli@1.11.9"
1307
1307
  ]
1308
1308
  },
1309
1309
  {
@@ -1436,7 +1436,7 @@
1436
1436
  ]
1437
1437
  },
1438
1438
  {
1439
- "ref": "ragent-live|ragent-cli@1.11.8",
1439
+ "ref": "ragent-live|ragent-cli@1.11.9",
1440
1440
  "dependsOn": [
1441
1441
  "ragent-live|@azure/web-pubsub-client@1.0.4",
1442
1442
  "ragent-live|commander@14.0.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ragent-cli",
3
- "version": "1.11.8",
3
+ "version": "1.11.9",
4
4
  "description": "CLI agent for rAgent Live — browser-first terminal control plane for AI coding agents",
5
5
  "main": "dist/index.js",
6
6
  "bin": {