@volter/supercode-teams 0.2.178 → 0.2.179

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 (2) hide show
  1. package/machine.mjs +20 -5
  2. package/package.json +1 -1
package/machine.mjs CHANGED
@@ -1080,6 +1080,16 @@ export class TeamsMachine extends MachineCore {
1080
1080
 
1081
1081
  listPorts() { return listAnnouncedPorts(this.portsDir); }
1082
1082
 
1083
+ /** The attach clients on each pane's tmux session, by its label; null where panes are not tmux's (a ConPTY host). */
1084
+ #viewerCounts() {
1085
+ if (process.platform === 'win32') return null;
1086
+ const listed = spawnSync('tmux', [...(this.tmuxSocket ? ['-L', this.tmuxSocket] : []), 'list-clients', '-F', '#{session_name}'], { timeout: 3_000, encoding: 'utf8' });
1087
+ const counts = new Map();
1088
+ if (listed.status !== 0) return counts; // no server, or no clients: nothing shows any pane
1089
+ for (const name of listed.stdout.split('\n').filter(Boolean)) counts.set(name, (counts.get(name) ?? 0) + 1);
1090
+ return counts;
1091
+ }
1092
+
1083
1093
  /**
1084
1094
  * A terminal window on this machine's screen showing the pane, so the people at the machine see what runs on it:
1085
1095
  * in whichever capable terminal is installed (popup-terminals.mjs), running an attach client. The pane ending ends
@@ -1087,7 +1097,7 @@ export class TeamsMachine extends MachineCore {
1087
1097
  */
1088
1098
  #popupWindow(contextKey, session) {
1089
1099
  const terminal = popupTerminal(this.env);
1090
- if (!terminal) return;
1100
+ if (!terminal) return false;
1091
1101
  const attach = process.platform === 'win32'
1092
1102
  ? [process.execPath, fileURLToPath(new URL('./bin/fleet.mjs', import.meta.url)), 'attach', contextKey, '--mode', 'control']
1093
1103
  // Presented as `supercode open`'s own client is (crates/cli/src/open.rs `attach`): the terminal sets its own
@@ -1095,7 +1105,7 @@ export class TeamsMachine extends MachineCore {
1095
1105
  // screen override, so the window's own scrollback, wheel and selection work instead of the wheel arriving
1096
1106
  // in the pane as up/down arrows.
1097
1107
  : ['/usr/bin/env', 'TERM=screen-256color', spawnSync('which', ['tmux'], { encoding: 'utf8' }).stdout.trim() || 'tmux', ...(this.tmuxSocket ? ['-L', this.tmuxSocket] : []), '-T', 'RGB', 'attach-session', '-t', session.label ?? contextKey];
1098
- openPopup(terminal, attach, { ...this.env, TMUX: '' });
1108
+ return openPopup(terminal, attach, { ...this.env, TMUX: '' });
1099
1109
  }
1100
1110
 
1101
1111
  /**
@@ -1445,9 +1455,12 @@ export class TeamsMachine extends MachineCore {
1445
1455
  // hand here: a list that made a caller ask a second door "and is anything
1446
1456
  // wrong with these?" would be a list that knows and will not say.
1447
1457
  const rows = await this.attentionEvidence();
1458
+ // how many clients each pane has now (a popped window, a `supercode open`, a relay streaming it elsewhere): what a caller must
1459
+ // know before it opens another view of a pane on the same screen
1460
+ const viewers = this.#viewerCounts();
1448
1461
  // `title` is what a viewer should show: the override if one is set, else the tmux label.
1449
1462
  return rows.filter((row) => row.session.id && shown(row.session.contextKey)).map(({ session: s, attention, rank, notify, evidence }) => ({
1450
- pane: s.contextKey, dead: Boolean(s.dead), label: s.label, title: titles[`pane:${s.contextKey}`] ?? s.label, cwd: s.cwd, owned: s.owned, activeCommand: s.activeCommand, pid: s.rootPid ?? null, size: s.size, suspended: this.suspended.has(s.contextKey),
1463
+ pane: s.contextKey, dead: Boolean(s.dead), label: s.label, title: titles[`pane:${s.contextKey}`] ?? s.label, cwd: s.cwd, owned: s.owned, activeCommand: s.activeCommand, pid: s.rootPid ?? null, size: s.size, suspended: this.suspended.has(s.contextKey), viewers: viewers ? (viewers.get(s.label) ?? 0) : null,
1451
1464
  attention, rank, notify, evidence, input_cleanup: this.inputRepairs[s.contextKey] ? {pending:true,at:this.inputRepairs[s.contextKey].at,reason:this.inputRepairs[s.contextKey].reason} : null,
1452
1465
  }));
1453
1466
  }
@@ -1541,9 +1554,11 @@ export class TeamsMachine extends MachineCore {
1541
1554
  this.launches.record({ key, pane: contextKey, kind: params.kind ?? null, by, ...executable, status, error: exit?.failed ? exit.how : undefined });
1542
1555
  return { key, pane: contextKey, reused: false, status, exit: exit?.how ?? null };
1543
1556
  }
1544
- if (params.popup !== false) this.#popupWindow(contextKey, session);
1557
+ // the window this machine's screen shows the pane in, when one opened: the caller is told, so it does not open a
1558
+ // second view of a pane that is already in front of the person at the machine
1559
+ const window = params.popup !== false && this.#popupWindow(contextKey, session);
1545
1560
  if (params.notify) this.#noticeWhenEnded({ key, pane: contextKey, notify: params.notify, label: params.name ?? params.kind ?? params.program });
1546
- return { key, pane: contextKey, reused: false, status: 'ready' };
1561
+ return { key, pane: contextKey, reused: false, status: 'ready', window };
1547
1562
  }
1548
1563
  case 'launches.get': {
1549
1564
  if (!operator) throw Object.assign(new Error('operators only'), { code: 403 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.2.178",
3
+ "version": "0.2.179",
4
4
  "type": "module",
5
5
  "description": "A machine daemon serving this machine's doors, and a local or remote Teams server for shared session discovery, OpenTelemetry ingestion and access to enrolled machines.",
6
6
  "license": "MIT",