@volter/supercode-teams 0.2.101 → 0.2.102

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,20 @@ function channelForOn() {
45
46
  return openOn(onName(), { context: contextName() });
46
47
  }
47
48
 
49
+ // Who starts a pane: supercode names it when it runs this CLI (empty: a person); run on its own, supercode is asked
50
+ // (`teams caller`), and a launch no supercode answers for records no starter (so no session may act on the pane).
51
+ let launcherAnswer;
52
+ function launcher() {
53
+ if (launcherAnswer === undefined) {
54
+ if (process.env.SUPERCODE_CALLER !== undefined) launcherAnswer = process.env.SUPERCODE_CALLER || null;
55
+ else {
56
+ const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'caller'], { encoding: 'utf8', shell: process.platform === 'win32', windowsHide: true });
57
+ try { launcherAnswer = JSON.parse(r.stdout.trim().split('\n').pop()).caller ?? null; } catch { launcherAnswer = null; }
58
+ }
59
+ }
60
+ return launcherAnswer;
61
+ }
62
+
48
63
  async function main() {
49
64
  switch (verb) {
50
65
  case 'discover': {
@@ -87,7 +102,7 @@ async function main() {
87
102
  ...(flags.popup === 'false' ? { popup: false } : {}),
88
103
  // Who starts the pane (the session or board supercode resolved; unset for a person), and whether the pane
89
104
  // runs its one verb as that caller (`close`/`stop` on another machine).
90
- ...(process.env.SUPERCODE_CALLER ? { caller: process.env.SUPERCODE_CALLER } : {}),
105
+ ...(launcher() ? { caller: launcher() } : {}),
91
106
  ...(flags['act-for-caller'] ? { actForCaller: true } : {}),
92
107
  // `open --notify-when-ended`: the launching session's address, told once when the pane ends, and the
93
108
  // 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,23 @@ 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 names it when it runs this CLI (empty: a person); run on its own (`supercode-teams`), this CLI asks
112
+ // supercode (`teams caller`), which reads the session from this process's ancestry, and refuses when none answers.
113
+ let callerAnswer;
114
+ function caller() {
115
+ if (callerAnswer === undefined) {
116
+ if (process.env.SUPERCODE_CALLER !== undefined) callerAnswer = process.env.SUPERCODE_CALLER || null;
117
+ else {
118
+ const r = spawnSync(process.env.SUPERCODE_BIN || 'supercode', ['teams', 'caller'], { encoding: 'utf8', shell: process.platform === 'win32', windowsHide: true });
119
+ let answer;
120
+ try { answer = JSON.parse(r.stdout.trim().split('\n').pop()); } catch { /* no answer */ }
121
+ 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. Run it as \`supercode teams …\` (${(r.error?.message ?? r.stderr ?? '').trim().slice(0, 200)})`, 1);
122
+ callerAnswer = answer.caller ?? null;
123
+ }
124
+ }
125
+ return callerAnswer ?? undefined;
126
+ }
112
127
 
113
128
  async function withNode(fn) {
114
129
  const channel = await machineChannel();
@@ -153,7 +168,7 @@ async function main() {
153
168
  case 'panes': {
154
169
  if (sub === 'ls') return withNode(async (rpc) => out(await rpc.call('panes.list')));
155
170
  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 })));
171
+ if (sub === 'close') return withNode(async (rpc) => out(await rpc.call('panes.close', { pane: rest[0], caller: caller() })));
157
172
  die('panes ls|capture|close');
158
173
  }
159
174
  case 'title': {
@@ -177,9 +192,9 @@ async function main() {
177
192
  die('attention ls | rank | request <pane> [--urgency now|soon|whenever] [--reason text] | clear <pane>');
178
193
  }
179
194
  case 'suspend':
180
- return withNode(async (rpc) => out(await rpc.call('panes.suspend', { pane: sub, interrupt: Boolean(flags.interrupt), caller })));
195
+ return withNode(async (rpc) => out(await rpc.call('panes.suspend', { pane: sub, interrupt: Boolean(flags.interrupt), caller: caller() })));
181
196
  case 'resume':
182
- return withNode(async (rpc) => out(await rpc.call('panes.resume', { pane: sub, input: flags.input ? String(flags.input) : null, caller })));
197
+ return withNode(async (rpc) => out(await rpc.call('panes.resume', { pane: sub, input: flags.input ? String(flags.input) : null, caller: caller() })));
183
198
  case 'files': {
184
199
  const path = rest[0];
185
200
  if (sub === 'ls') return withNode(async (rpc) => out(await rpc.call('files.list', { path })));
@@ -218,10 +233,10 @@ async function main() {
218
233
  // (answers `delivered: false` with the reason when it holds a draft).
219
234
  if (flags.key !== undefined) {
220
235
  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 })));
236
+ return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: keyBytes(String(flags.key)), submit: false, keys: true, caller: caller() })));
222
237
  }
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 })));
238
+ if (flags['when-composer-empty']) return withNode(async (rpc) => out(await rpc.call('panes.turn', { pane: sub, text: rest.join(' '), caller: caller() })));
239
+ return withNode(async (rpc) => out(await rpc.call('panes.input', { pane: sub, text: rest.join(' '), submit: !flags['no-enter'], caller: caller() })));
225
240
  case 'port': {
226
241
  // `port <pane>/<n> [--listen host:port]`: a machine port forwarded here. Stays up until ctrl-c.
227
242
  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.102",
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",