@volter/supercode-teams 0.2.101 → 0.2.103

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/bin/fleet.mjs CHANGED
@@ -16,6 +16,7 @@
16
16
  // `--on` names an enrolled machine of the selected context, reached through
17
17
  // its Teams server. Omitted, every verb acts on this OS user's own daemon, so
18
18
  // the remote arm and the local arm are one code path.
19
+ import { spawnSync } from 'node:child_process';
19
20
  import { attachPane, attachRuntime } from '../client.mjs';
20
21
  import { openOn, harnessRequest, nodeRequest, fleetDiscover } from '../fleet.mjs';
21
22
 
@@ -45,6 +46,17 @@ function channelForOn() {
45
46
  return openOn(onName(), { context: contextName() });
46
47
  }
47
48
 
49
+ // Who starts a pane, as supercode answers it (`teams caller`); a launch nothing answers for records no starter, so no
50
+ // session may act on the pane.
51
+ let launcherAnswer;
52
+ function launcher() {
53
+ if (launcherAnswer === undefined) {
54
+ const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'caller'], { encoding: 'utf8', shell: process.platform === 'win32', windowsHide: true });
55
+ try { launcherAnswer = JSON.parse(r.stdout.trim().split('\n').pop()).caller ?? null; } catch { launcherAnswer = null; }
56
+ }
57
+ return launcherAnswer;
58
+ }
59
+
48
60
  async function main() {
49
61
  switch (verb) {
50
62
  case 'discover': {
@@ -87,7 +99,7 @@ async function main() {
87
99
  ...(flags.popup === 'false' ? { popup: false } : {}),
88
100
  // Who starts the pane (the session or board supercode resolved; unset for a person), and whether the pane
89
101
  // runs its one verb as that caller (`close`/`stop` on another machine).
90
- ...(process.env.SUPERCODE_CALLER ? { caller: process.env.SUPERCODE_CALLER } : {}),
102
+ ...(launcher() ? { caller: launcher() } : {}),
91
103
  ...(flags['act-for-caller'] ? { actForCaller: true } : {}),
92
104
  // `open --notify-when-ended`: the launching session's address, told once when the pane ends, and the
93
105
  // machine's mail name the notice comes from.
package/bin/teams.mjs CHANGED
@@ -26,6 +26,7 @@
26
26
  // members, groups, sources, sync, machines, sessions) is the Teams workspace
27
27
  // CLI in ./workspace.mjs.
28
28
  import { createWriteStream, readFileSync, writeFileSync } from 'node:fs';
29
+ import { spawnSync } from 'node:child_process';
29
30
  import { SupercodeHarnessClient } from '@volter/supercode-harness-sdk';
30
31
 
31
32
  // A Windows scheduled task keeps no output (launchd and systemd name log files), so its unit names one here.
@@ -106,9 +107,20 @@ async function machineChannel() {
106
107
  return openMachine({ machine: flags.machine ? String(flags.machine) : null, context: flags.context ? String(flags.context) : null, home });
107
108
  }
108
109
 
109
- // The session (or board) this command acts for, as supercode resolved it: the pane doors refuse a session acting on
110
- // a pane it did not start. Unset for a person.
111
- const caller = process.env.SUPERCODE_CALLER || undefined;
110
+ // The session (or board) this command acts for: the pane doors refuse a session acting on a pane it did not start.
111
+ // supercode answers it (`teams caller`: the session in this process's ancestry first, a named caller only as supercode
112
+ // itself takes one), so this bin holds the same rule as \`supercode teams\`; a command nothing answers for is refused.
113
+ let callerAnswer;
114
+ function caller() {
115
+ if (callerAnswer === undefined) {
116
+ const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'caller'], { encoding: 'utf8', shell: process.platform === 'win32', windowsHide: true });
117
+ let answer;
118
+ try { answer = JSON.parse(r.stdout.trim().split('\n').pop()); } catch { /* no answer */ }
119
+ if (!answer || !('caller' in answer)) die(`cannot tell which session runs this, so the pane doors cannot hold it to the panes it started; nothing was done (${(r.error?.message ?? r.stderr ?? '').trim().slice(0, 200)})`, 1);
120
+ callerAnswer = answer.caller ?? null;
121
+ }
122
+ return callerAnswer ?? undefined;
123
+ }
112
124
 
113
125
  async function withNode(fn) {
114
126
  const channel = await machineChannel();
@@ -153,7 +165,7 @@ async function main() {
153
165
  case 'panes': {
154
166
  if (sub === 'ls') return withNode(async (rpc) => out(await rpc.call('panes.list')));
155
167
  if (sub === 'capture') return withNode(async (rpc) => out((await rpc.call('panes.capture', { pane: rest[0], lines: Number(flags.lines ?? 200) })).text));
156
- if (sub === 'close') return withNode(async (rpc) => out(await rpc.call('panes.close', { pane: rest[0], caller })));
168
+ if (sub === 'close') return withNode(async (rpc) => out(await rpc.call('panes.close', { pane: rest[0], caller: caller() })));
157
169
  die('panes ls|capture|close');
158
170
  }
159
171
  case 'title': {
@@ -177,9 +189,9 @@ async function main() {
177
189
  die('attention ls | rank | request <pane> [--urgency now|soon|whenever] [--reason text] | clear <pane>');
178
190
  }
179
191
  case 'suspend':
180
- return withNode(async (rpc) => out(await rpc.call('panes.suspend', { pane: sub, interrupt: Boolean(flags.interrupt), caller })));
192
+ return withNode(async (rpc) => out(await rpc.call('panes.suspend', { pane: sub, interrupt: Boolean(flags.interrupt), caller: caller() })));
181
193
  case 'resume':
182
- return withNode(async (rpc) => out(await rpc.call('panes.resume', { pane: sub, input: flags.input ? String(flags.input) : null, caller })));
194
+ return withNode(async (rpc) => out(await rpc.call('panes.resume', { pane: sub, input: flags.input ? String(flags.input) : null, caller: caller() })));
183
195
  case 'files': {
184
196
  const path = rest[0];
185
197
  if (sub === 'ls') return withNode(async (rpc) => out(await rpc.call('files.list', { path })));
@@ -218,10 +230,10 @@ async function main() {
218
230
  // (answers `delivered: false` with the reason when it holds a draft).
219
231
  if (flags.key !== undefined) {
220
232
  if (flags.key === true || rest.length) die('input <pane> --key NAMES: keys by tmux name, space- or comma-separated (Escape, Enter, Tab, BTab, BSpace, Space, Up, Down, Left, Right, Home, End, PPage, NPage, DC, C-a … C-z)');
221
- return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: keyBytes(String(flags.key)), submit: false, keys: true, caller })));
233
+ return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: keyBytes(String(flags.key)), submit: false, keys: true, caller: caller() })));
222
234
  }
223
- if (flags['when-composer-empty']) return withNode(async (rpc) => out(await rpc.call('panes.turn', { pane: sub, text: rest.join(' '), caller })));
224
- return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: rest.join(' '), submit: !flags['no-enter'], caller })));
235
+ if (flags['when-composer-empty']) return withNode(async (rpc) => out(await rpc.call('panes.turn', { pane: sub, text: rest.join(' '), caller: caller() })));
236
+ return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: rest.join(' '), submit: !flags['no-enter'], caller: caller() })));
225
237
  case 'port': {
226
238
  // `port <pane>/<n> [--listen host:port]`: a machine port forwarded here. Stays up until ctrl-c.
227
239
  const spec = String(sub ?? '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/supercode-teams",
3
- "version": "0.2.101",
3
+ "version": "0.2.103",
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",