@volter/supercode-teams 0.3.128 → 0.3.129

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/live-sessions.mjs CHANGED
@@ -147,7 +147,7 @@ export class LiveSessions extends EventEmitter {
147
147
  // session is live while its process is; the daemon pane it runs in, when it does, from the panes the records read
148
148
  this.grokHome = env.GROK_HOME || join(homedir(), '.grok');
149
149
  this.grok = new Map(); // session id → { pid, ppid, cwd, openedAt, pane }
150
- this.grokPaneRoots = [];
150
+ this.paneRoots = [];
151
151
  this.projectHooks = new Map(); // directory → whether its .codex/hooks.json carries the hook
152
152
  this.userHook = false;
153
153
  this.watchers = [];
@@ -468,15 +468,23 @@ export class LiveSessions extends EventEmitter {
468
468
 
469
469
  /** The daemon pane a Grok session runs in: the pane whose process it is, or whose process started it. */
470
470
  #grokPane(entry) {
471
- entry.pane = this.grokPaneRoots.find((root) => root.pid === entry.pid || (entry.ppid != null && root.pid === entry.ppid))?.pane ?? null;
471
+ entry.pane = this.paneRoots.find((root) => root.pid === entry.pid || (entry.ppid != null && root.pid === entry.ppid))?.pane ?? null;
472
472
  }
473
473
 
474
- /** The daemon's live panes and their root processes (`[{ pane, pid }]`), as the records read them: Grok's panes. */
475
- grokPanes(roots) {
476
- this.grokPaneRoots = (roots ?? []).filter((root) => root?.pane && Number.isInteger(root.pid));
477
- const before = JSON.stringify([...this.grok].map(([id, entry]) => [id, entry.pane]));
474
+ /** The daemon pane a Claude session runs in: the tmux pane its registry names, else the pane whose process it is.
475
+ * Claude names its pane only from tmux's environment, so on Windows (no tmux) only the pane's process says it. */
476
+ #claudePane(entry) {
477
+ return entry.tmux ?? this.paneRoots.find((root) => root.pid === entry.pid)?.pane ?? null;
478
+ }
479
+
480
+ /** The daemon's live panes and their root processes (`[{ pane, pid }]`), as the records read them: the panes of the
481
+ * sessions whose own records name none (Grok's; Claude's on Windows). */
482
+ panes(roots) {
483
+ const placed = () => JSON.stringify([...[...this.grok].map(([id, entry]) => [id, entry.pane]), ...[...this.claude.values()].map((entry) => [entry.sessionId, this.#claudePane(entry)])]);
484
+ const before = placed();
485
+ this.paneRoots = (roots ?? []).filter((root) => root?.pane && Number.isInteger(root.pid));
478
486
  for (const entry of this.grok.values()) this.#grokPane(entry);
479
- if (JSON.stringify([...this.grok].map(([id, entry]) => [id, entry.pane])) !== before) this.#changed();
487
+ if (placed() !== before) this.#changed();
480
488
  }
481
489
 
482
490
  /** The messaging tools' markers kept here, and those servers from before wrote into the mail folder, taken in once
@@ -647,7 +655,7 @@ export class LiveSessions extends EventEmitter {
647
655
  if (entry.name.startsWith(RELAY_PREFIX) || !alive(entry.pid)) continue;
648
656
  const address = this.#address('claude-code', entry.sessionId);
649
657
  if (seen.has(address)) continue; seen.add(address);
650
- out.push({ address, name: `${entry.name}@${this.machine}`, status: entry.status, door: 'native', pid: entry.pid, cwd: entry.cwd, pane: entry.tmux, tools: this.hasTools(entry.pid), socket: entry.socket ?? null });
658
+ out.push({ address, name: `${entry.name}@${this.machine}`, status: entry.status, door: 'native', pid: entry.pid, cwd: entry.cwd, pane: this.#claudePane(entry), tools: this.hasTools(entry.pid), socket: entry.socket ?? null });
651
659
  }
652
660
  const codex = new Set([...this.codexRunning.keys(), ...this.codexPaneOf.keys()]);
653
661
  for (const sessionId of codex) {
package/machine.mjs CHANGED
@@ -922,8 +922,8 @@ export class TeamsMachine extends MachineCore {
922
922
  const titles = readJson(this.p.titles, { v: 1, titles: {} }).titles;
923
923
  const panes = rows.filter((row) => row.session?.id).map(({ session: s }) => ({ pane: s.contextKey, dead: Boolean(s.dead), pid: s.rootPid ?? null, label: s.label ?? null, title: titles[`pane:${s.contextKey}`] ?? s.label ?? null, cwd: s.cwd ?? null }));
924
924
  const live = panes.filter((pane) => !pane.dead && pane.pid);
925
- // the pane each Grok session runs in (Grok's registry names its process; its pane is the one rooted at it)
926
- this.live?.grokPanes(live.map((pane) => ({ pane: pane.pane, pid: pane.pid })));
925
+ // the pane each session whose record names none runs in (Grok's; Claude's on Windows): the one rooted at it
926
+ this.live?.panes(live.map((pane) => ({ pane: pane.pane, pid: pane.pid })));
927
927
  // the harness session under each live pane, as the board reads it: unread is said, never no session
928
928
  let activities = [], sessions_unread = null;
929
929
  if (live.length) {
@@ -8,7 +8,10 @@ import { basename, dirname, join } from 'node:path';
8
8
 
9
9
  const path = process.argv[2];
10
10
  const seed = JSON.parse(readFileSync(path, 'utf8'));
11
- const directory = mkdtempSync(join(tmpdir(), 'sc-codex-'));
11
+ // A local socket's path holds at most 104 bytes (macOS; 108 on Linux): under a long TMPDIR (a scratch home's) the
12
+ // endpoint goes under /tmp, private to this user as mkdtemp makes it, or neither side could open it.
13
+ const base = process.platform !== 'win32' && Buffer.byteLength(join(tmpdir(), 'sc-codex-XXXXXX', 'native.sock')) > 103 ? '/tmp' : tmpdir();
14
+ const directory = mkdtempSync(join(base, 'sc-codex-'));
12
15
  const socketPath = join(directory, 'native.sock');
13
16
  const endpoint = `unix://${socketPath}`;
14
17
  // Configuration is evaluated by the native server. Remote resume/fork refuses
@@ -26,7 +26,9 @@ export async function codexQuestions({ threadId, threadIds, answer, socketPath,
26
26
  const targets = new Set(threadIds ?? (threadId ? [threadId] : []));
27
27
  if ((!targets.size && !(discover && socketPath)) || [...targets].some((id) => typeof id !== 'string' || !id) || (answer && targets.size !== 1)) throw failure('invalid-request', 'Native thread ids are required; answer only one thread at a time.');
28
28
  if (!socketPath) throw failure('native-question-unavailable', 'This launch has no native question endpoint.');
29
- const socket = new WebSocket('ws://localhost/', { perMessageDeflate: false, createConnection: () => connect(socketPath), handshakeTimeout: timeoutMs });
29
+ // The connection's own failure (a gone or unopenable endpoint) can be emitted before the request listens to it: it
30
+ // is the WebSocket's error then (the handshake times out), never an unhandled one that ends the machine daemon.
31
+ const socket = new WebSocket('ws://localhost/', { perMessageDeflate: false, createConnection: () => connect(socketPath).on('error', () => {}), handshakeTimeout: timeoutMs });
30
32
  let nextId = 0;
31
33
  const pending = new Map();
32
34
  const questions = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.3.128",
3
+ "version": "0.3.129",
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",