@fnndsc/brasa 0.23.2 → 0.24.0

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 (89) hide show
  1. package/dist/buildinfo.json +1 -1
  2. package/dist/builtins/fs/pull.args.d.ts +1 -1
  3. package/dist/builtins/fs/pull.args.js +5 -3
  4. package/dist/builtins/fs/pull.args.js.map +1 -1
  5. package/dist/builtins/fs/pull.js +2 -2
  6. package/dist/builtins/fs/touch.js +1 -1
  7. package/dist/builtins/fs/touch.js.map +1 -1
  8. package/dist/builtins/help.js +128 -10
  9. package/dist/builtins/help.js.map +1 -1
  10. package/dist/builtins/index.d.ts +5 -0
  11. package/dist/builtins/index.js +5 -0
  12. package/dist/builtins/index.js.map +1 -1
  13. package/dist/builtins/net/query.csv.js +14 -0
  14. package/dist/builtins/net/query.csv.js.map +1 -1
  15. package/dist/builtins/net/query.js +3 -0
  16. package/dist/builtins/net/query.js.map +1 -1
  17. package/dist/builtins/pluginExecute.js +28 -3
  18. package/dist/builtins/pluginExecute.js.map +1 -1
  19. package/dist/builtins/proc.js +32 -2
  20. package/dist/builtins/proc.js.map +1 -1
  21. package/dist/builtins/res/dicom.d.ts +35 -1
  22. package/dist/builtins/res/dicom.js +15 -2
  23. package/dist/builtins/res/dicom.js.map +1 -1
  24. package/dist/builtins/res/expect.d.ts +40 -0
  25. package/dist/builtins/res/expect.js +447 -0
  26. package/dist/builtins/res/expect.js.map +1 -0
  27. package/dist/builtins/res/gather.d.ts +10 -0
  28. package/dist/builtins/res/gather.js +224 -0
  29. package/dist/builtins/res/gather.js.map +1 -0
  30. package/dist/builtins/res/gather.store.d.ts +131 -0
  31. package/dist/builtins/res/gather.store.js +225 -0
  32. package/dist/builtins/res/gather.store.js.map +1 -0
  33. package/dist/builtins/res/pipeline.args.d.ts +5 -2
  34. package/dist/builtins/res/pipeline.args.js +8 -3
  35. package/dist/builtins/res/pipeline.args.js.map +1 -1
  36. package/dist/builtins/res/pipeline.js +23 -0
  37. package/dist/builtins/res/pipeline.js.map +1 -1
  38. package/dist/builtins/res/play.d.ts +70 -0
  39. package/dist/builtins/res/play.js +334 -0
  40. package/dist/builtins/res/play.js.map +1 -0
  41. package/dist/builtins/res/plugin.js +21 -6
  42. package/dist/builtins/res/plugin.js.map +1 -1
  43. package/dist/builtins/res/record.d.ts +49 -0
  44. package/dist/builtins/res/record.js +150 -0
  45. package/dist/builtins/res/record.js.map +1 -0
  46. package/dist/builtins/res/runWait.d.ts +49 -0
  47. package/dist/builtins/res/runWait.js +193 -0
  48. package/dist/builtins/res/runWait.js.map +1 -0
  49. package/dist/builtins/sys/weather.d.ts +132 -0
  50. package/dist/builtins/sys/weather.js +297 -0
  51. package/dist/builtins/sys/weather.js.map +1 -0
  52. package/dist/command-keys.js +4 -0
  53. package/dist/command-keys.js.map +1 -1
  54. package/dist/core/connect.d.ts +31 -1
  55. package/dist/core/connect.js +32 -1
  56. package/dist/core/connect.js.map +1 -1
  57. package/dist/core/dispatch.d.ts +0 -14
  58. package/dist/core/dispatch.js +83 -27
  59. package/dist/core/dispatch.js.map +1 -1
  60. package/dist/core/engine.d.ts +10 -0
  61. package/dist/core/engine.js +9 -0
  62. package/dist/core/engine.js.map +1 -1
  63. package/dist/core/expansion.d.ts +70 -0
  64. package/dist/core/expansion.js +289 -0
  65. package/dist/core/expansion.js.map +1 -0
  66. package/dist/core/promptContext.js +5 -1
  67. package/dist/core/promptContext.js.map +1 -1
  68. package/dist/lib/duration.d.ts +17 -0
  69. package/dist/lib/duration.js +25 -0
  70. package/dist/lib/duration.js.map +1 -0
  71. package/dist/lib/parser.d.ts +50 -0
  72. package/dist/lib/parser.js +119 -0
  73. package/dist/lib/parser.js.map +1 -1
  74. package/dist/logo/brain.d.ts +6 -27
  75. package/dist/logo/brain.js +5 -250
  76. package/dist/logo/brain.js.map +1 -1
  77. package/dist/session/answer.d.ts +141 -0
  78. package/dist/session/answer.js +163 -0
  79. package/dist/session/answer.js.map +1 -0
  80. package/dist/session/answerAdapters.d.ts +4 -0
  81. package/dist/session/answerAdapters.js +106 -0
  82. package/dist/session/answerAdapters.js.map +1 -0
  83. package/dist/session/recent.d.ts +68 -0
  84. package/dist/session/recent.js +99 -0
  85. package/dist/session/recent.js.map +1 -0
  86. package/dist/session/recorder.d.ts +61 -0
  87. package/dist/session/recorder.js +95 -0
  88. package/dist/session/recorder.js.map +1 -0
  89. package/package.json +5 -5
@@ -0,0 +1,297 @@
1
+ /**
2
+ * @file Builtin weather.
3
+ *
4
+ * Reports the weather at a place — the current conditions and the next few
5
+ * days — from Open-Meteo, which needs no key and no account. A place is
6
+ * geocoded by name through the same service, so `weather Boston` and
7
+ * `weather Cape Town` both work, and a bare `weather` asks about Boston,
8
+ * where the lab is — in metric, as the lab reads, unless `--units imperial`.
9
+ *
10
+ * The report is a kernel fact like `date` or `fortune`: it renders once and
11
+ * every surface shows the same text, and the typed model beneath it carries
12
+ * the numbers for a surface that wants to draw them differently. The
13
+ * network reached is the daemon's, so a host that cannot see the internet
14
+ * says so in one line rather than hanging.
15
+ *
16
+ * @module
17
+ */
18
+ import { envelope_error, envelope_ok } from '@fnndsc/cumin';
19
+ /** Where `weather` looks when given no place. */
20
+ export const WEATHER_DEFAULT_PLACE = 'Boston';
21
+ /** Days of forecast shown when none are asked for. */
22
+ const DAYS_DEFAULT = 3;
23
+ /** The system used when none is asked for: the lab reports in metric. */
24
+ export const WEATHER_UNITS_DEFAULT = 'metric';
25
+ /** The most forecast days Open-Meteo answers. */
26
+ const DAYS_MAX = 16;
27
+ /** How long one call may take before the report gives up. */
28
+ const FETCH_TIMEOUT_MS = 8_000;
29
+ /** The geocoder: a name to coordinates. */
30
+ const GEOCODE_URL = 'https://geocoding-api.open-meteo.com/v1/search';
31
+ /** The forecast itself. */
32
+ const FORECAST_URL = 'https://api.open-meteo.com/v1/forecast';
33
+ /** The compass, eight points, clockwise from north. */
34
+ const COMPASS = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
35
+ /** WMO weather interpretation codes, as Open-Meteo reports them, in words. */
36
+ const CONDITIONS = {
37
+ 0: 'clear sky',
38
+ 1: 'mainly clear',
39
+ 2: 'partly cloudy',
40
+ 3: 'overcast',
41
+ 45: 'fog',
42
+ 48: 'rime fog',
43
+ 51: 'light drizzle',
44
+ 53: 'drizzle',
45
+ 55: 'dense drizzle',
46
+ 56: 'light freezing drizzle',
47
+ 57: 'freezing drizzle',
48
+ 61: 'light rain',
49
+ 63: 'rain',
50
+ 65: 'heavy rain',
51
+ 66: 'light freezing rain',
52
+ 67: 'freezing rain',
53
+ 71: 'light snow',
54
+ 73: 'snow',
55
+ 75: 'heavy snow',
56
+ 77: 'snow grains',
57
+ 80: 'light rain showers',
58
+ 81: 'rain showers',
59
+ 82: 'violent rain showers',
60
+ 85: 'light snow showers',
61
+ 86: 'heavy snow showers',
62
+ 95: 'thunderstorm',
63
+ 96: 'thunderstorm with light hail',
64
+ 99: 'thunderstorm with heavy hail',
65
+ };
66
+ /**
67
+ * Words a WMO weather code.
68
+ *
69
+ * @param code - The code Open-Meteo reported.
70
+ * @returns The condition in words; an unknown code says so with its number.
71
+ */
72
+ export function condition_ofCode(code) {
73
+ return CONDITIONS[code] ?? `conditions code ${code}`;
74
+ }
75
+ /**
76
+ * Names a wind direction on the eight-point compass.
77
+ *
78
+ * @param degrees - Where the wind comes from, degrees clockwise from north.
79
+ * @returns The compass point.
80
+ */
81
+ export function compass_ofDegrees(degrees) {
82
+ const index = Math.round((((degrees % 360) + 360) % 360) / 45) % 8;
83
+ return COMPASS[index];
84
+ }
85
+ /**
86
+ * Reads a number out of an untyped JSON field.
87
+ *
88
+ * @param value - The field.
89
+ * @returns The number, or null when the field is not one.
90
+ */
91
+ function number_read(value) {
92
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
93
+ }
94
+ /**
95
+ * Reads a string out of an untyped JSON field.
96
+ *
97
+ * @param value - The field.
98
+ * @returns The string, or the empty string when the field is not one.
99
+ */
100
+ function string_read(value) {
101
+ return typeof value === 'string' ? value : '';
102
+ }
103
+ /**
104
+ * Parses the call line: a place in as many words as it takes, `--units`
105
+ * for the system, `-d N` for the days shown. Any other flag is refused by
106
+ * name.
107
+ *
108
+ * @param args - The words after `weather`.
109
+ * @returns The parsed call, or the refusal to print.
110
+ */
111
+ export function weatherArgs_parse(args) {
112
+ const words = [];
113
+ let units = WEATHER_UNITS_DEFAULT;
114
+ let days = DAYS_DEFAULT;
115
+ for (let i = 0; i < args.length; i++) {
116
+ const arg = args[i];
117
+ if (arg === '-u' || arg === '--units' || arg.startsWith('--units=')) {
118
+ const value = arg.startsWith('--units=') ? arg.slice('--units='.length) : (args[++i] ?? '');
119
+ if (value !== 'metric' && value !== 'imperial') {
120
+ return `weather: --units takes metric or imperial, not '${value}'`;
121
+ }
122
+ units = value;
123
+ }
124
+ else if (arg === '-d' || arg === '--days') {
125
+ const value = parseInt(args[i + 1] ?? '', 10);
126
+ if (!Number.isFinite(value) || value < 1 || value > DAYS_MAX) {
127
+ return `weather: ${arg} takes a number of days, 1 to ${DAYS_MAX}`;
128
+ }
129
+ days = value;
130
+ i++;
131
+ }
132
+ else if (arg.startsWith('-')) {
133
+ return `weather: unknown option '${arg}' (usage: weather [place] [-u|--units metric|imperial] [-d|--days N])`;
134
+ }
135
+ else {
136
+ words.push(arg);
137
+ }
138
+ }
139
+ return { place: words.length > 0 ? words.join(' ') : WEATHER_DEFAULT_PLACE, units, days };
140
+ }
141
+ /**
142
+ * Fetches one JSON answer, bounded in time.
143
+ *
144
+ * @param fetchFn - The fetch to go through.
145
+ * @param url - What to ask.
146
+ * @returns The parsed body.
147
+ * @throws {Error} When the service is unreachable, answers an error, or
148
+ * answers something that is not JSON.
149
+ */
150
+ async function json_fetch(fetchFn, url) {
151
+ const response = await fetchFn(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
152
+ if (!response.ok)
153
+ throw new Error(`open-meteo answered ${response.status}`);
154
+ return response.json();
155
+ }
156
+ /**
157
+ * Finds a place by name.
158
+ *
159
+ * @param fetchFn - The fetch to go through.
160
+ * @param name - The place as the operator typed it.
161
+ * @returns The first place the geocoder offers, or null when it offers none.
162
+ */
163
+ export async function place_find(fetchFn, name) {
164
+ const url = `${GEOCODE_URL}?name=${encodeURIComponent(name)}&count=1&language=en&format=json`;
165
+ const answer = (await json_fetch(fetchFn, url));
166
+ const first = answer.results?.[0];
167
+ const latitude = number_read(first?.latitude);
168
+ const longitude = number_read(first?.longitude);
169
+ if (first === undefined || latitude === null || longitude === null)
170
+ return null;
171
+ return {
172
+ name: string_read(first.name),
173
+ region: string_read(first.admin1),
174
+ country: string_read(first.country),
175
+ latitude,
176
+ longitude,
177
+ timezone: string_read(first.timezone) || 'auto',
178
+ };
179
+ }
180
+ /**
181
+ * Fetches the conditions at a place.
182
+ *
183
+ * @param fetchFn - The fetch to go through.
184
+ * @param place - Where.
185
+ * @param units - Metric (°C, km/h) or imperial (°F, mph).
186
+ * @param days - How many days of forecast.
187
+ * @returns The report.
188
+ * @throws {Error} When the service cannot be reached or answers without the
189
+ * fields a report is made of.
190
+ */
191
+ export async function report_fetch(fetchFn, place, units, days) {
192
+ const celsius = units === 'metric';
193
+ const query = [
194
+ `latitude=${place.latitude}`,
195
+ `longitude=${place.longitude}`,
196
+ 'current=temperature_2m,apparent_temperature,relative_humidity_2m,weather_code,wind_speed_10m,wind_direction_10m',
197
+ 'daily=weather_code,temperature_2m_max,temperature_2m_min',
198
+ `temperature_unit=${celsius ? 'celsius' : 'fahrenheit'}`,
199
+ `wind_speed_unit=${celsius ? 'kmh' : 'mph'}`,
200
+ `timezone=${encodeURIComponent(place.timezone)}`,
201
+ `forecast_days=${days}`,
202
+ ].join('&');
203
+ const answer = (await json_fetch(fetchFn, `${FORECAST_URL}?${query}`));
204
+ const current = answer.current;
205
+ const temperature = number_read(current?.temperature_2m);
206
+ const code = number_read(current?.weather_code);
207
+ if (current === undefined || temperature === null || code === null) {
208
+ throw new Error('open-meteo answered without current conditions');
209
+ }
210
+ const now = {
211
+ time: string_read(current.time),
212
+ temperature,
213
+ apparent: number_read(current.apparent_temperature) ?? temperature,
214
+ humidity: number_read(current.relative_humidity_2m) ?? 0,
215
+ code,
216
+ condition: condition_ofCode(code),
217
+ windSpeed: number_read(current.wind_speed_10m) ?? 0,
218
+ windDirection: compass_ofDegrees(number_read(current.wind_direction_10m) ?? 0),
219
+ };
220
+ const dates = Array.isArray(answer.daily?.time) ? answer.daily.time : [];
221
+ const highs = Array.isArray(answer.daily?.temperature_2m_max) ? answer.daily.temperature_2m_max : [];
222
+ const lows = Array.isArray(answer.daily?.temperature_2m_min) ? answer.daily.temperature_2m_min : [];
223
+ const codes = Array.isArray(answer.daily?.weather_code) ? answer.daily.weather_code : [];
224
+ const forecast = [];
225
+ for (let i = 0; i < dates.length; i++) {
226
+ const high = number_read(highs[i]);
227
+ const low = number_read(lows[i]);
228
+ const dayCode = number_read(codes[i]);
229
+ if (high === null || low === null || dayCode === null)
230
+ continue;
231
+ forecast.push({ date: string_read(dates[i]), low, high, code: dayCode, condition: condition_ofCode(dayCode) });
232
+ }
233
+ return { place, units: celsius ? 'C' : 'F', now, days: forecast };
234
+ }
235
+ /**
236
+ * Names a forecast day: `today`, then the weekday and date.
237
+ *
238
+ * @param date - The day as `YYYY-MM-DD`.
239
+ * @param index - Its position in the forecast, 0 being today.
240
+ * @returns The label.
241
+ */
242
+ function day_label(date, index) {
243
+ if (index === 0)
244
+ return 'today';
245
+ const parsed = new Date(`${date}T12:00:00Z`);
246
+ const weekday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][parsed.getUTCDay()] ?? '';
247
+ return `${weekday} ${date.slice(5)}`;
248
+ }
249
+ /**
250
+ * Renders a report as the lines a terminal shows.
251
+ *
252
+ * @param report - The report.
253
+ * @returns The text, with a trailing newline.
254
+ */
255
+ export function report_render(report) {
256
+ const unit = `°${report.units}`;
257
+ const speed = report.units === 'C' ? 'km/h' : 'mph';
258
+ const where = [report.place.name, report.place.region, report.place.country]
259
+ .filter((part) => part.length > 0)
260
+ .join(', ');
261
+ const when = report.now.time.replace('T', ' ');
262
+ const lines = [
263
+ `${where} · ${when}`,
264
+ ` now ${Math.round(report.now.temperature)}${unit} (feels ${Math.round(report.now.apparent)}${unit}) · ${report.now.condition} · wind ${Math.round(report.now.windSpeed)} ${speed} ${report.now.windDirection} · humidity ${Math.round(report.now.humidity)}%`,
265
+ ];
266
+ report.days.forEach((day, index) => {
267
+ lines.push(` ${day_label(day.date, index).padEnd(9)} ${Math.round(day.low)}–${Math.round(day.high)}${unit} · ${day.condition}`);
268
+ });
269
+ return `${lines.join('\n')}\n`;
270
+ }
271
+ /**
272
+ * Reports the weather at a place.
273
+ *
274
+ * @param args - `[place…] [-u|--units metric|imperial] [-d|--days N]`.
275
+ * @param fetchFn - The fetch to go through; the global one unless a test
276
+ * hands in its own.
277
+ * @returns An envelope carrying the rendered report and the typed report
278
+ * beneath it; an error envelope when a flag is unknown, the place is not
279
+ * found, or the service cannot be reached.
280
+ */
281
+ export async function builtin_weather(args, fetchFn = (url, init) => fetch(url, init)) {
282
+ const parsed = weatherArgs_parse(args);
283
+ if (typeof parsed === 'string')
284
+ return envelope_error(`${parsed}\n`);
285
+ try {
286
+ const place = await place_find(fetchFn, parsed.place);
287
+ if (place === null)
288
+ return envelope_error(`weather: no such place '${parsed.place}'\n`);
289
+ const report = await report_fetch(fetchFn, place, parsed.units, parsed.days);
290
+ return envelope_ok(report_render(report), { kind: 'sys.weather', data: report });
291
+ }
292
+ catch (error) {
293
+ const reason = error instanceof Error ? error.message : String(error);
294
+ return envelope_error(`weather: ${reason}\n`);
295
+ }
296
+ }
297
+ //# sourceMappingURL=weather.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weather.js","sourceRoot":"","sources":["../../../src/builtins/sys/weather.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,10 @@ export const COMMAND_HANDLERS_KEYS = [
21
21
  'files', 'links', 'dirs',
22
22
  'store',
23
23
  'tag', 'tags',
24
+ 'expect',
25
+ 'play',
26
+ 'record',
27
+ 'gather',
24
28
  'group', 'groups',
25
29
  'user', 'users',
26
30
  'pluginmeta', 'pluginmetas', 'meta', 'metas',
@@ -1 +1 @@
1
- {"version":3,"file":"command-keys.js","sourceRoot":"","sources":["../src/command-keys.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAa;IAC7C,aAAa;IACb,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI;IAC1E,QAAQ,EAAE,UAAU,EAAE,MAAM;IAC5B,MAAM;IACN,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IAChD,MAAM;IACN,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;IAClG,kCAAkC;IAClC,QAAQ,EAAE,SAAS;IACnB,MAAM,EAAE,OAAO;IACf,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,UAAU;IACrB,OAAO,EAAE,OAAO,EAAE,MAAM;IACxB,OAAO;IACP,KAAK,EAAE,MAAM;IACb,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,OAAO;IACf,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;IAC5C,gBAAgB,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM;IAC3E,UAAU,EAAE,WAAW;IACvB,oBAAoB;IACpB,kBAAkB;IAClB,aAAa,EAAE,aAAa,EAAE,cAAc;CAC7C,CAAC"}
1
+ {"version":3,"file":"command-keys.js","sourceRoot":"","sources":["../src/command-keys.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAa;IAC7C,aAAa;IACb,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI;IAC1E,QAAQ,EAAE,UAAU,EAAE,MAAM;IAC5B,MAAM;IACN,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;IAChD,MAAM;IACN,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM;IAClG,kCAAkC;IAClC,QAAQ,EAAE,SAAS;IACnB,MAAM,EAAE,OAAO;IACf,UAAU,EAAE,WAAW;IACvB,SAAS,EAAE,UAAU;IACrB,OAAO,EAAE,OAAO,EAAE,MAAM;IACxB,OAAO;IACP,KAAK,EAAE,MAAM;IACb,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,OAAO;IACf,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;IAC5C,gBAAgB,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM;IAC3E,UAAU,EAAE,WAAW;IACvB,oBAAoB;IACpB,kBAAkB;IAClB,aAAa,EAAE,aAAa,EAAE,cAAc;CAC7C,CAAC"}
@@ -5,7 +5,8 @@
5
5
  * context and validate its token against the server. It carries no terminal
6
6
  * output of its own: it returns a status a frontend narrates (the CLI prints
7
7
  * progress; a daemon logs or exits). Interactive login (prompting for a URL and
8
- * password) is a frontend concern and lives with the CLI, not here.
8
+ * password) is a frontend concern and lives with the CLI, not here. A token a
9
+ * door already minted is the other headless way in, and it is here too.
9
10
  *
10
11
  * @module
11
12
  */
@@ -36,3 +37,32 @@ export interface SavedSessionResult {
36
37
  * @returns The restore outcome and the resolved context.
37
38
  */
38
39
  export declare function sessionConnect_fromSaved(): Promise<SavedSessionResult>;
40
+ /**
41
+ * The outcome of a token hand-over.
42
+ *
43
+ * - `connected` — the server accepted the token; the session is this identity's.
44
+ * - `refused` — the server did not; the session is offline and `error` says why.
45
+ */
46
+ export interface TokenSessionResult {
47
+ status: 'connected' | 'refused';
48
+ context: SingleContext;
49
+ /** What the server said, when the status is `refused`. */
50
+ error?: string;
51
+ }
52
+ /**
53
+ * Connects a session from a CUBE token a door already minted, without prompting.
54
+ *
55
+ * A login front on a shared host exchanges the operator's password for a
56
+ * token itself and hands the token to the session it spawns; the session
57
+ * never sees a password. The token is proved against the server before the
58
+ * context is written, so a refusal changes nothing on disk. On acceptance
59
+ * the context is set the way a credentialed boot sets it: the identity, and
60
+ * no feed or plugin — the working directory is the identity's own and is
61
+ * not touched, so a session that was evicted comes back where it was.
62
+ *
63
+ * @param user - The CUBE username the token was minted for.
64
+ * @param url - The CUBE API base the token was minted against.
65
+ * @param token - The token.
66
+ * @returns Connected, or refused with the server's reason.
67
+ */
68
+ export declare function sessionConnect_withToken(user: string, url: string, token: string): Promise<TokenSessionResult>;
@@ -5,12 +5,14 @@
5
5
  * context and validate its token against the server. It carries no terminal
6
6
  * output of its own: it returns a status a frontend narrates (the CLI prints
7
7
  * progress; a daemon logs or exits). Interactive login (prompting for a URL and
8
- * password) is a frontend concern and lives with the CLI, not here.
8
+ * password) is a frontend concern and lives with the CLI, not here. A token a
9
+ * door already minted is the other headless way in, and it is here too.
9
10
  *
10
11
  * @module
11
12
  */
12
13
  import { session } from '../session/index.js';
13
14
  import { context_getSingle } from '@fnndsc/salsa';
15
+ import { chrisContext, Context } from '@fnndsc/cumin';
14
16
  /**
15
17
  * Restores a session from saved credentials, without any prompting.
16
18
  *
@@ -43,4 +45,33 @@ export async function sessionConnect_fromSaved() {
43
45
  return { status: 'invalid-token', context, error: error instanceof Error ? error.message : String(error) };
44
46
  }
45
47
  }
48
+ /**
49
+ * Connects a session from a CUBE token a door already minted, without prompting.
50
+ *
51
+ * A login front on a shared host exchanges the operator's password for a
52
+ * token itself and hands the token to the session it spawns; the session
53
+ * never sees a password. The token is proved against the server before the
54
+ * context is written, so a refusal changes nothing on disk. On acceptance
55
+ * the context is set the way a credentialed boot sets it: the identity, and
56
+ * no feed or plugin — the working directory is the identity's own and is
57
+ * not touched, so a session that was evicted comes back where it was.
58
+ *
59
+ * @param user - The CUBE username the token was minted for.
60
+ * @param url - The CUBE API base the token was minted against.
61
+ * @param token - The token.
62
+ * @returns Connected, or refused with the server's reason.
63
+ */
64
+ export async function sessionConnect_withToken(user, url, token) {
65
+ const outcome = await session.connection.connection_connectWithToken({ user, url, token });
66
+ if (!outcome.connected) {
67
+ session.offline = true;
68
+ return { status: 'refused', context: await context_getSingle(), error: outcome.reason };
69
+ }
70
+ session.offline = false;
71
+ await chrisContext.current_set(Context.ChRISuser, user);
72
+ await chrisContext.current_set(Context.ChRISURL, url);
73
+ await chrisContext.current_set(Context.ChRISfeed, '');
74
+ await chrisContext.current_set(Context.ChRISplugin, '');
75
+ return { status: 'connected', context: await context_getSingle() };
76
+ }
46
77
  //# sourceMappingURL=connect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/core/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAsBlD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,MAAM,OAAO,GAAkB,MAAM,iBAAiB,EAAE,CAAC;IAEzD,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAkB,MAAM,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAkB,MAAM,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACpE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;QAC1C,CAAC;QACD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7G,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/core/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAyB,YAAY,EAAE,OAAO,EAA4B,MAAM,eAAe,CAAC;AAqBvG;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,MAAM,OAAO,GAAkB,MAAM,iBAAiB,EAAE,CAAC;IAEzD,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,KAAK,GAAkB,MAAM,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC1E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAkB,MAAM,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;QACpE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YACvB,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;QAC1C,CAAC;QACD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7G,CAAC;AACH,CAAC;AAeD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAAY,EAAE,GAAW,EAAE,KAAa;IACrF,MAAM,OAAO,GAAwB,MAAM,OAAO,CAAC,UAAU,CAAC,2BAA2B,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAChH,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IAC1F,CAAC;IACD,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;IACxB,MAAM,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACtD,MAAM,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,EAAE,EAAE,CAAC;AACrE,CAAC"}
@@ -25,20 +25,6 @@ export { COMMAND_HANDLERS_KEYS } from '../command-keys.js';
25
25
  * @param enabled - Whether timing display is active.
26
26
  */
27
27
  export declare function command_timingMaybePrint(startTime: number, enabled: boolean): void;
28
- /**
29
- * Expands `$NAME` and `${NAME}` environment references in one token.
30
- *
31
- * Applied per token after parsing, so an expanded value can never inject
32
- * command separators, pipes or redirects (the structural characters were
33
- * already consumed). References to unset variables are left verbatim —
34
- * kinder than the shell's silent empty string when a script forgets an
35
- * export. Shell-escape lines (`!...`) never reach this point; bash does
36
- * its own expansion there.
37
- *
38
- * @param token - A single parsed command token.
39
- * @returns The token with set environment references substituted.
40
- */
41
- export declare function envRefs_expand(token: string): string;
42
28
  /**
43
29
  * Dispatches a parsed command to its handler and returns its envelope, with
44
30
  * any errors the command left on the stack drained into the envelope.
@@ -14,16 +14,18 @@
14
14
  */
15
15
  import { writeFileSync, appendFileSync } from 'fs';
16
16
  import chalk from 'chalk';
17
- import { builtin_cd, builtin_ls, builtin_pwd, builtin_connect, builtin_logout, builtin_cat, builtin_cp, builtin_mv, builtin_upload, builtin_pacs, builtin_pipeline, builtin_pull, builtin_query, builtin_cubepath, builtin_dcm, builtin_image, builtin_rm, builtin_setfacl, builtin_getfacl, builtin_touch, builtin_mkdir, builtin_plugin, builtin_feed, builtin_compute, builtin_tag, builtin_group, builtin_user, builtin_pluginmeta, builtin_plugininstance, builtin_workflow, builtin_download, builtin_edit, builtin_config, builtin_files, builtin_links, builtin_dirs, builtin_context, builtin_parametersofplugin, builtin_physicalmode, builtin_timing, builtin_id, builtin_whoami, builtin_whereami, builtin_version, builtin_fortune, builtin_date, builtin_cal, builtin_debug, builtin_help, builtin_tree, builtin_du, builtin_store, error_stripDebugPrefix } from '../builtins/index.js';
17
+ import { builtin_cd, builtin_ls, builtin_pwd, builtin_connect, builtin_logout, builtin_cat, builtin_cp, builtin_mv, builtin_upload, builtin_pacs, builtin_pipeline, builtin_pull, builtin_query, builtin_cubepath, builtin_dcm, builtin_image, builtin_rm, builtin_setfacl, builtin_getfacl, builtin_touch, builtin_mkdir, builtin_plugin, builtin_feed, builtin_compute, builtin_tag, builtin_expect, builtin_play, builtin_record, builtin_gather, builtin_group, builtin_user, builtin_pluginmeta, builtin_plugininstance, builtin_workflow, builtin_download, builtin_edit, builtin_config, builtin_files, builtin_links, builtin_dirs, builtin_context, builtin_parametersofplugin, builtin_physicalmode, builtin_timing, builtin_id, builtin_whoami, builtin_whereami, builtin_version, builtin_fortune, builtin_weather, builtin_date, builtin_cal, builtin_debug, builtin_help, builtin_tree, builtin_du, builtin_store, error_stripDebugPrefix } from '../builtins/index.js';
18
18
  import { builtin_executePlugin } from '../builtins/pluginExecute.js';
19
19
  import { builtin_proc } from '../builtins/proc.js';
20
20
  import { shellWords_expand } from '../builtins/wildcard.js';
21
21
  import { help_render, commandHelp_get, pipelineExecutableHelp_render, args_checkHasHelpFlag, } from '../builtins/help.js';
22
22
  import { pluginExecutable_handle } from '../builtins/executable.js';
23
- import { errorStack, Ok, envelope_error } from '@fnndsc/cumin';
23
+ import { errorStack, Ok, Err, envelope_error } from '@fnndsc/cumin';
24
24
  import { envelopeHandler_wrap, envelope_deliver, sink_get, PipeCaptureSink, sinkScope_run } from './sink.js';
25
+ import { reference_refusal, reference_resolve, unresolvedStands_get, verbInHand_set } from './expansion.js';
26
+ import { answer_note, answerConsulted_take } from '../session/answer.js';
25
27
  import { vfs } from '../lib/vfs/vfs.js';
26
- import { shellArguments_envRefsExpand, shellWords_tokenize, shellWords_values, } from '../lib/parser.js';
28
+ import { shellWords_referencesExpand, shellWords_tokenize, shellWords_values, } from '../lib/parser.js';
27
29
  import { surface_get, capability_require } from './surface.js';
28
30
  import { sudoCommand_run } from './elevation.js';
29
31
  import { redirectTarget_resolve, pathnameExpansion_isEligible, } from './preprocess.js';
@@ -76,6 +78,7 @@ export const ENVELOPE_HANDLERS = {
76
78
  debug: builtin_debug,
77
79
  version: builtin_version,
78
80
  fortune: builtin_fortune,
81
+ weather: builtin_weather,
79
82
  date: builtin_date,
80
83
  cal: builtin_cal,
81
84
  ls: builtin_ls,
@@ -94,6 +97,10 @@ export const ENVELOPE_HANDLERS = {
94
97
  computes: builtin_compute,
95
98
  tag: builtin_tag,
96
99
  tags: builtin_tag,
100
+ expect: builtin_expect,
101
+ play: builtin_play,
102
+ record: builtin_record,
103
+ gather: builtin_gather,
97
104
  group: builtin_group,
98
105
  groups: builtin_group,
99
106
  user: builtin_user,
@@ -166,6 +173,7 @@ export const COMMAND_HANDLERS = {
166
173
  whereami: envelopeHandler_wrap(builtin_whereami),
167
174
  debug: envelopeHandler_wrap(builtin_debug),
168
175
  fortune: envelopeHandler_wrap(builtin_fortune),
176
+ weather: envelopeHandler_wrap(builtin_weather),
169
177
  date: envelopeHandler_wrap(builtin_date),
170
178
  cal: envelopeHandler_wrap(builtin_cal),
171
179
  help: envelopeHandler_wrap(builtin_help),
@@ -181,6 +189,10 @@ export const COMMAND_HANDLERS = {
181
189
  computes: envelopeHandler_wrap(builtin_compute),
182
190
  tag: envelopeHandler_wrap(builtin_tag),
183
191
  tags: envelopeHandler_wrap(builtin_tag),
192
+ expect: envelopeHandler_wrap(builtin_expect),
193
+ play: envelopeHandler_wrap(builtin_play),
194
+ record: envelopeHandler_wrap(builtin_record),
195
+ gather: envelopeHandler_wrap(builtin_gather),
184
196
  group: envelopeHandler_wrap(builtin_group),
185
197
  groups: envelopeHandler_wrap(builtin_group),
186
198
  pluginmeta: envelopeHandler_wrap(builtin_pluginmeta),
@@ -277,25 +289,6 @@ async function binExecutableMatches_get(command) {
277
289
  pipeline: binResult.value.find((item) => item.name === command && item.type === 'pipeline'),
278
290
  };
279
291
  }
280
- /**
281
- * Expands `$NAME` and `${NAME}` environment references in one token.
282
- *
283
- * Applied per token after parsing, so an expanded value can never inject
284
- * command separators, pipes or redirects (the structural characters were
285
- * already consumed). References to unset variables are left verbatim —
286
- * kinder than the shell's silent empty string when a script forgets an
287
- * export. Shell-escape lines (`!...`) never reach this point; bash does
288
- * its own expansion there.
289
- *
290
- * @param token - A single parsed command token.
291
- * @returns The token with set environment references substituted.
292
- */
293
- export function envRefs_expand(token) {
294
- return token.replace(/\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/g, (match, braced, bare) => {
295
- const value = process.env[braced ?? bare ?? ''];
296
- return value !== undefined ? value : match;
297
- });
298
- }
299
292
  /**
300
293
  * Reads the current process exit code as a number.
301
294
  *
@@ -386,7 +379,6 @@ async function commandDispatchEnvelope_run(command, args) {
386
379
  if (command === 'exit') {
387
380
  process.exit(0);
388
381
  }
389
- args = shellArguments_envRefsExpand(args, envRefs_expand);
390
382
  if (command === 'sudo') {
391
383
  return await sudoCommand_run(args, commandDispatchEnvelope_run);
392
384
  }
@@ -490,6 +482,34 @@ async function helpEnvelope_maybe(command, args) {
490
482
  * @param args - Parsed argument words.
491
483
  * @returns Expanded compatibility arguments with expansion provenance.
492
484
  */
485
+ /**
486
+ * Resolves the references in a whole command line, the command word included.
487
+ *
488
+ * A shell runs `$CMD arg`, and a manifest names its plugin as a parameter
489
+ * (`@param ANON = pl-pfdicom_tagSub-v3.3.4`) for exactly that reason: the
490
+ * same workflow on another CUBE supplies its own build. Expanding only the
491
+ * arguments left the command word as written, and the line refused with
492
+ * "command not found: ${ANON}".
493
+ *
494
+ * @param words - The line's tokenized words.
495
+ * @returns The words with references resolved, or a refusal naming the one
496
+ * nothing could answer.
497
+ */
498
+ async function commandLine_referencesExpand(words) {
499
+ // The verb is known before its operands expand, so an index can be
500
+ // refused by KIND: IMAGE given a study says so, instead of resolving to
501
+ // a path and failing later for a reason that names nothing typed. A
502
+ // `sudo` line's verb is the one after it.
503
+ const verb = words[0]?.value === 'sudo' ? words[1]?.value : words[0]?.value;
504
+ verbInHand_set(verb ?? null);
505
+ const expanded = await shellWords_referencesExpand(words, reference_resolve, unresolvedStands_get());
506
+ verbInHand_set(null);
507
+ if (!expanded.ok) {
508
+ errorStack.stack_push('error', reference_refusal(expanded.missing));
509
+ return Err();
510
+ }
511
+ return Ok(expanded.words);
512
+ }
493
513
  async function commandWords_expand(command, args) {
494
514
  const sudoNestedCommand = command === 'sudo' ? args[0] : undefined;
495
515
  const targetCommand = sudoNestedCommand?.value ?? command;
@@ -515,10 +535,19 @@ async function chellCommand_executeAndCapture(commandLine) {
515
535
  const trimmedLine = commandLine.trim();
516
536
  if (!trimmedLine)
517
537
  return { text: '', buffer: Buffer.alloc(0) };
518
- const words = shellWords_tokenize(trimmedLine);
519
- if (words.length === 0) {
538
+ const tokenized = shellWords_tokenize(trimmedLine);
539
+ if (tokenized.length === 0) {
520
540
  return { text: '', buffer: Buffer.alloc(0) };
521
541
  }
542
+ const referenced = await commandLine_referencesExpand(tokenized);
543
+ if (!referenced.ok) {
544
+ const lastError = errorStack.stack_pop();
545
+ const message = lastError === undefined
546
+ ? 'unresolved reference'
547
+ : error_stripDebugPrefix(lastError.message);
548
+ return { text: chalk.red(`${message}\n`), buffer: Buffer.from('') };
549
+ }
550
+ const words = referenced.value;
522
551
  const [commandWord, ...argumentWords] = words;
523
552
  const command = commandWord.value;
524
553
  const expandResult = await commandWords_expand(command, argumentWords);
@@ -615,9 +644,26 @@ export async function pipe_execute(segments) {
615
644
  * @returns The command's envelope, or null when the line held no tokens.
616
645
  */
617
646
  export async function command_executeToEnvelope(trimmedLine, startTime, timingEnabled) {
618
- const words = shellWords_tokenize(trimmedLine);
619
- if (words.length === 0)
647
+ const tokenized = shellWords_tokenize(trimmedLine);
648
+ if (tokenized.length === 0)
620
649
  return null;
650
+ const referenced = await commandLine_referencesExpand(tokenized);
651
+ if (!referenced.ok) {
652
+ const lastError = errorStack.stack_pop();
653
+ // The refusal is the operator's to read, not the stack's: the debug
654
+ // prefix names the function that pushed it, which tells them nothing.
655
+ if (lastError)
656
+ sink_get().err_write(`${chalk.red(error_stripDebugPrefix(lastError.message))}\n`);
657
+ process.exitCode = 1;
658
+ return { status: 'error', rendered: '' };
659
+ }
660
+ const words = referenced.value;
661
+ // A line that acted on a number says which listing it counted: the rule
662
+ // that a readout which acts says so, applied to the rows it acted on.
663
+ const counted = answerConsulted_take();
664
+ if (counted !== null) {
665
+ sink_get().data_write(chalk.gray(` from: ${counted.source} · ${counted.rows.length} rows\n`));
666
+ }
621
667
  const [commandWord, ...argumentWords] = words;
622
668
  const command = commandWord.value;
623
669
  const expandResult = await commandWords_expand(command, argumentWords);
@@ -647,10 +693,20 @@ export async function command_executeToEnvelope(trimmedLine, startTime, timingEn
647
693
  const result = failed
648
694
  ? { ...pluginEnvelope, status: 'error' }
649
695
  : pluginEnvelope;
696
+ if (result.model !== undefined) {
697
+ answer_note(result.model.kind, result.model.data, trimmedLine);
698
+ }
650
699
  return envelope_drainErrorsInto(checkpoint, result);
651
700
  }
652
701
  const envelope = await command_dispatchEnvelope(command, args);
653
702
  command_timingMaybePrint(startTime, timingEnabled);
703
+ // An answer with rows becomes the thing `@3` counts. Recorded from the
704
+ // MODEL, not from the rendering, so a console table and a graphical pane
705
+ // number the same rows — an index that meant different things on
706
+ // different surfaces would be worse than no index at all.
707
+ if (envelope.model !== undefined) {
708
+ answer_note(envelope.model.kind, envelope.model.data, trimmedLine);
709
+ }
654
710
  return envelope;
655
711
  }
656
712
  //# sourceMappingURL=dispatch.js.map