@amritk/nish-x86_64-linux 0.18.0 → 0.21.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 (104) hide show
  1. package/INSTALL.md +58 -28
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +3 -2
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +99 -5
  22. package/runtime/nish.h +289 -19
  23. package/runtime/nish.mjs +39 -3
  24. package/runtime/node.mjs +383 -0
  25. package/runtime/runtime-aes.c +490 -0
  26. package/runtime/runtime-bytes.c +401 -0
  27. package/runtime/runtime-cross.c +366 -0
  28. package/runtime/runtime-host.c +8 -7
  29. package/runtime/runtime-net.c +93 -34
  30. package/runtime/runtime-os.c +214 -16
  31. package/runtime/runtime-parallel.c +590 -107
  32. package/runtime/runtime-simd.c +235 -11
  33. package/runtime/runtime-term.c +504 -0
  34. package/runtime/runtime-wasm.c +5 -14
  35. package/runtime/runtime.c +257 -17
  36. package/runtime/shim.mjs +6 -915
  37. package/runtime/sqlite/LICENSE.md +85 -0
  38. package/runtime/sqlite/README.md +45 -0
  39. package/runtime/sqlite/nish-sqlite.c +359 -0
  40. package/runtime/sqlite/sqlite3.c +271131 -0
  41. package/runtime/sqlite/sqlite3.h +14447 -0
  42. package/scripts/build.sh +437 -28
  43. package/std/LICENSE-fast-float +23 -0
  44. package/std/LICENSE-fdlibm +8 -0
  45. package/std/README.md +108 -58
  46. package/std/base64.ts +116 -0
  47. package/std/bytes.ts +419 -0
  48. package/std/cli.ts +328 -0
  49. package/std/collections.ts +6 -5
  50. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  51. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  52. package/std/crypto/aes.ts +238 -76
  53. package/std/crypto/base64url.ts +125 -0
  54. package/std/crypto/bcrypt.ts +760 -0
  55. package/std/crypto/chacha20poly1305.ts +43 -9
  56. package/std/crypto/ed25519.ts +840 -0
  57. package/std/crypto/hkdf.ts +304 -12
  58. package/std/crypto/hmac.ts +382 -54
  59. package/std/crypto/p256.ts +932 -61
  60. package/std/crypto/x25519.ts +55 -37
  61. package/std/crypto/x509-random.ts +71 -0
  62. package/std/crypto/x509.ts +20 -131
  63. package/std/json-tokenizer.ts +4237 -0
  64. package/std/json-typed.ts +1014 -0
  65. package/std/json-value.ts +1182 -0
  66. package/std/json.ts +1202 -34
  67. package/std/math.ts +608 -0
  68. package/std/net/buffer-pool.ts +151 -0
  69. package/std/net/hpack.ts +416 -112
  70. package/std/net/http-fields.ts +93 -0
  71. package/std/net/http1-server.ts +501 -62
  72. package/std/net/http1.ts +31 -6
  73. package/std/net/http2-tls.ts +112 -24
  74. package/std/net/http2.ts +333 -75
  75. package/std/net/http3-server.ts +261 -43
  76. package/std/net/http3.ts +187 -28
  77. package/std/net/postgres.ts +1875 -0
  78. package/std/net/prefork.ts +412 -0
  79. package/std/net/quic-listener.ts +108 -11
  80. package/std/net/quic-packet.ts +243 -82
  81. package/std/net/quic-recovery.ts +361 -30
  82. package/std/net/quic-stream.ts +424 -24
  83. package/std/net/quic.ts +519 -167
  84. package/std/net/router.ts +676 -0
  85. package/std/net/tls/codec.ts +426 -27
  86. package/std/net/tls/record-server.ts +478 -132
  87. package/std/net/tls/record.ts +133 -42
  88. package/std/net/tls/schedule.ts +464 -55
  89. package/std/net/tls/ticket.ts +610 -0
  90. package/std/net/tls-tcp.ts +196 -53
  91. package/std/net/tls.ts +1018 -260
  92. package/std/net/url.ts +571 -0
  93. package/std/random.ts +94 -0
  94. package/std/regex.ts +4274 -0
  95. package/std/sqlite.ts +391 -0
  96. package/std/term-io.ts +978 -0
  97. package/std/term-pixels.ts +874 -0
  98. package/std/term-probe.ts +459 -0
  99. package/std/term-rate.ts +993 -0
  100. package/std/term.ts +2453 -0
  101. package/std/text.ts +90 -7
  102. package/std/threads.ts +438 -14
  103. package/std/yaml-lines.ts +141 -0
  104. package/std/yaml.ts +4675 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * `std/term-probe` — what the terminal a program runs in can do (its colours,
3
+ * keyboard protocol, graphics and size), asked of it once at start and read
4
+ * from its answer.
5
+ *
6
+ * A program writes `probeQueries(env)` in one write, then feeds what the
7
+ * terminal sends back to a `TermProbe` until `done()` (the terminal answered
8
+ * the last query) or `timedOut(now)`, and reads the `TermCaps` from `result()`.
9
+ * The module is pure: the environment comes in as a `TermEnv`, times are the
10
+ * caller's milliseconds as `f64` from any origin, and nothing here reads,
11
+ * writes or waits.
12
+ *
13
+ * ```ts
14
+ * import { TermEnv, TermProbe, probeQueries } from "nish/term-probe";
15
+ *
16
+ * const env: TermEnv = new TermEnv();
17
+ * env.term = "xterm-256color";
18
+ * const probe: TermProbe = new TermProbe(env, 80, 24, 0.0);
19
+ * const queries: string = probeQueries(env); // write these to the terminal
20
+ * // …feed each read: probe.feed(bytes, count, nowMs), until probe.done() or probe.timedOut(nowMs)
21
+ * console.log(`${probe.result().colors}`);
22
+ * ```
23
+ *
24
+ * - `TermEnv` — the environment variables the probe reads, each "" when
25
+ * unset, one field each in camelCase: `term` (`TERM`), `colorterm`
26
+ * (`COLORTERM`), `termProgram` (`TERM_PROGRAM`), `tmux` (`TMUX`), `sty`
27
+ * (`STY`), `sshConnection`, `sshClient` and `sshTty` (`SSH_CONNECTION`,
28
+ * `SSH_CLIENT`, `SSH_TTY`), `noColor` (`NO_COLOR`), `nishTermGraphics`
29
+ * (`NISH_TERM_GRAPHICS`) and `nishTermFps` (`NISH_TERM_FPS`, which the probe
30
+ * carries for the renderer and does not read).
31
+ * - `probeQueries(env)` — every query, in this order, as one string:
32
+ * XTVERSION `CSI > q`, DECRQM for synchronized output `CSI ? 2026 $ p` and
33
+ * pixel mouse `CSI ? 1016 $ p`, kitty's keyboard flags `CSI ? u`, a kitty
34
+ * graphics query `APC G i=31,s=1,v=1,a=q,t=d,f=24;AAAA ST`, XTSMGRAPHICS
35
+ * for the sixel colour registers `CSI ? 1 ; 1 ; 0 S`, the cell size
36
+ * `CSI 16 t`, the text area size `CSI 14 t`, a true-colour background set
37
+ * and read back with DECRQSS (`CSI 48 ; 2 ; 1 ; 2 ; 3 m`, `DCS $ q m ST`,
38
+ * `CSI m`), and DA1 `CSI c` last. Terminals answer in order, so the answer
39
+ * to DA1, which every terminal gives, means the rest are in. Under tmux
40
+ * (`TMUX` set) the queries only the outer terminal can answer — kitty
41
+ * graphics and the two sizes — go inside tmux's passthrough,
42
+ * `DCS tmux; … ST` with each ESC doubled; under GNU screen (`STY` set) the
43
+ * sizes go inside screen's `DCS … ST`, and the kitty graphics query and
44
+ * DECRQSS, which hold an ST of their own and which screen would forward
45
+ * rather than answer, are left out. On the Linux console (`TERM=linux`)
46
+ * both are left out too: it has no string states and would print them. DA1 is never wrapped: the multiplexer
47
+ * answers it, so a multiplexer that forwards no reply still ends the probe.
48
+ * - `TermProbe` — the probe in progress, made when the queries are sent.
49
+ * `feed(bytes, count, nowMs)` decodes a read through its own `KeyDecoder`
50
+ * (`nish/term`), keeping the replies and holding back every other event (a
51
+ * key typed while the probe ran) for `heldEvents()`, in order. `done()` is
52
+ * true once DA1 is answered; `timedOut(nowMs)` once `timeoutMs` (250 by
53
+ * default) has passed since the send with no answer to DA1. Either way
54
+ * the program then calls `finish(nowMs)` once: it gives up whatever the
55
+ * decoder still holds (a key cut off at the end of the read with DA1, say)
56
+ * and returns `heldEvents()` complete, and later reads go to the program's
57
+ * own decoder. `rttMs()` is the time from the
58
+ * send to the answer to DA1, -1 before it. `result()` reads the replies and
59
+ * the environment into a `TermCaps`, and can be called at any point.
60
+ * - `TermCaps` — what the terminal can do: `colors` (16, 256 or 16777216),
61
+ * `trueColor`, `sync`, `kittyKeyboard`, `kittyGraphics`, `sixel`,
62
+ * `sixelRegisters`, `cellWidthPx` and `cellHeightPx`, `pixelMouse`,
63
+ * `multiplexer` ("none", "tmux" or "screen"), `linuxConsole`, `remoteHint`,
64
+ * `answered`, `probeRttMs`, `graphics` ("kitty", "sixel", "halfblock" or
65
+ * "ascii"), `name` and `overrides`; each field's comment says where it
66
+ * comes from.
67
+ *
68
+ * The environment decides where the terminal cannot answer, and where the
69
+ * user says so: `COLORTERM=truecolor` or `24bit` means true colour, as does
70
+ * a DECRQSS that reads the 24-bit background back; otherwise a `TERM` with
71
+ * `256color` in it means 256 colours, and anything else 16. `TERM=linux` is
72
+ * the Linux console: 16 colours, ASCII graphics, no kitty keyboard and no
73
+ * synchronized output, whatever it answered. `NO_COLOR` set to anything
74
+ * (no-color.org) means 16 colours and touches nothing but colour.
75
+ * `NISH_TERM_GRAPHICS` names a graphics backend to use instead of the one the
76
+ * answers pick; a pixel backend the terminal did not answer for is refused
77
+ * when it would come out as garbage or nothing — on the Linux console, under
78
+ * a multiplexer, or when the terminal answered nothing at all. `overrides`
79
+ * says what each variable did, refusals included. The match is exact:
80
+ * `kitty`, `sixel`, `halfblock` or `ascii`, lower case, nothing around it.
81
+ *
82
+ * Under a multiplexer the probe sees the multiplexer first. tmux and screen
83
+ * answer DA1 themselves, so `done()` comes on their answer, before any reply
84
+ * forwarded through the passthrough; `probeRttMs` is the round trip to the
85
+ * multiplexer, and graphics come from what the multiplexer itself answers
86
+ * (tmux's own sixel, say). The environment is taken at face value: an
87
+ * `SSH_*` variable or `COLORTERM` left in a tmux session by the client that
88
+ * started it still counts, though the client attached now may differ.
89
+ */
90
+ import { EVENT_REPLY, KeyDecoder, TermEvent } from "nish/term"
91
+ import { replaceAll } from "nish/text"
92
+
93
+ /** How long a probe waits for the answer to DA1 by default, in milliseconds. */
94
+ export const TERM_PROBE_TIMEOUT_MS: f64 = 250.0
95
+
96
+ /** How long the probe's decoder holds an unfinished sequence, in milliseconds. */
97
+ const TERM_PROBE_ESCAPE_MS: f64 = 50.0
98
+
99
+ /** The colour count of a terminal with 24-bit colour. */
100
+ const TERM_PROBE_TRUE_COLORS: i32 = 16777216
101
+
102
+ /** The environment the probe reads, each variable "" when unset. */
103
+ export class TermEnv {
104
+ /** `TERM`: the terminal type, such as `xterm-256color` or `linux`. */
105
+ term: string
106
+ /** `COLORTERM`: `truecolor` or `24bit` where the terminal has 24-bit colour. */
107
+ colorterm: string
108
+ /** `TERM_PROGRAM`: the terminal's name where it sets one, such as `iTerm.app` or `Apple_Terminal`. */
109
+ termProgram: string
110
+ /** `TMUX`: set inside tmux. */
111
+ tmux: string
112
+ /** `STY`: set inside GNU screen. */
113
+ sty: string
114
+ /** `SSH_CONNECTION`: set by sshd for a remote session. */
115
+ sshConnection: string
116
+ /** `SSH_CLIENT`: set by sshd for a remote session. */
117
+ sshClient: string
118
+ /** `SSH_TTY`: set by sshd for a remote session with a terminal. */
119
+ sshTty: string
120
+ /** `NO_COLOR`: set to anything to ask for no colour (no-color.org). */
121
+ noColor: string
122
+ /** `NISH_TERM_GRAPHICS`: a graphics backend to use instead of the probed one, `kitty`, `sixel`, `halfblock` or `ascii`. */
123
+ nishTermGraphics: string
124
+ /** `NISH_TERM_FPS`: a frame rate for the renderer; carried, not read, by the probe. */
125
+ nishTermFps: string
126
+
127
+ /** An environment with every variable unset. */
128
+ constructor() {
129
+ this.term = ""
130
+ this.colorterm = ""
131
+ this.termProgram = ""
132
+ this.tmux = ""
133
+ this.sty = ""
134
+ this.sshConnection = ""
135
+ this.sshClient = ""
136
+ this.sshTty = ""
137
+ this.noColor = ""
138
+ this.nishTermGraphics = ""
139
+ this.nishTermFps = ""
140
+ }
141
+ }
142
+
143
+ /** What a terminal can do, from its answers to `probeQueries` and the environment. */
144
+ export class TermCaps {
145
+ /** The multiplexer in between: "none", "tmux" (`TMUX` set) or "screen" (`STY` set). */
146
+ multiplexer: string
147
+ /** The time from the send to the answer to DA1, in milliseconds; -1 unanswered. */
148
+ probeRttMs: f64
149
+ /** The graphics backend to draw with, best first — "kitty", "sixel" (256 registers or more), "halfblock", "ascii" — after `NISH_TERM_GRAPHICS`. */
150
+ graphics: string
151
+ /** The terminal's name and version from XTVERSION, else `TERM_PROGRAM`, else "". */
152
+ name: string
153
+ /** What the environment forced, one `VARIABLE: effect` per entry joined by `; `; "" when nothing. `nish/term-io` adds `NISH_TERM_FPS`'s, which the controller applies. */
154
+ overrides: string
155
+ /** How many colours to draw with: 16, 256 or 16777216. */
156
+ colors: i32
157
+ /** How many sixel colour registers there are, from XTSMGRAPHICS; 0 unanswered. */
158
+ sixelRegisters: i32
159
+ /** A cell's width in pixels: `CSI 16 t`, else the text area's from `CSI 14 t` over the columns; 0 unknown. */
160
+ cellWidthPx: i32
161
+ /** A cell's height in pixels: `CSI 16 t`, else the text area's from `CSI 14 t` over the rows; 0 unknown. */
162
+ cellHeightPx: i32
163
+ /** Whether 24-bit colour works: `COLORTERM`, or the background DECRQSS read back. */
164
+ trueColor: boolean
165
+ /** Whether synchronized output (mode 2026) is known: DECRPM answered set or reset. */
166
+ sync: boolean
167
+ /** Whether the kitty keyboard protocol is there: `CSI ? flags u` answered. */
168
+ kittyKeyboard: boolean
169
+ /** Whether kitty graphics are there: the query answered `OK`. */
170
+ kittyGraphics: boolean
171
+ /** Whether sixel is there: DA1 lists 4. */
172
+ sixel: boolean
173
+ /** Whether SGR mouse reports in pixels (mode 1016) are known: DECRPM answered set or reset. */
174
+ pixelMouse: boolean
175
+ /** Whether this is the Linux console (`TERM=linux`). */
176
+ linuxConsole: boolean
177
+ /** Whether the session looks remote (`SSH_CONNECTION`, `SSH_CLIENT` or `SSH_TTY` set), so slow. */
178
+ remoteHint: boolean
179
+ /** Whether the terminal answered DA1; false when the probe timed out. */
180
+ answered: boolean
181
+
182
+ /** Caps of a terminal that answered nothing: 16 colours, ASCII. */
183
+ constructor() {
184
+ this.colors = 16
185
+ this.trueColor = false
186
+ this.sync = false
187
+ this.kittyKeyboard = false
188
+ this.kittyGraphics = false
189
+ this.sixel = false
190
+ this.sixelRegisters = 0
191
+ this.cellWidthPx = 0
192
+ this.cellHeightPx = 0
193
+ this.pixelMouse = false
194
+ this.multiplexer = "none"
195
+ this.linuxConsole = false
196
+ this.remoteHint = false
197
+ this.answered = false
198
+ this.probeRttMs = -1.0
199
+ this.graphics = "ascii"
200
+ this.name = ""
201
+ this.overrides = ""
202
+ }
203
+ }
204
+
205
+ /** The multiplexer `env` says the program runs under: "tmux", "screen" or "none". */
206
+ const termMultiplexer = (env: TermEnv): string => {
207
+ if (env.tmux !== "") {
208
+ return "tmux"
209
+ }
210
+ return env.sty !== "" ? "screen" : "none"
211
+ }
212
+
213
+ /** `query` inside the passthrough of `multiplexer`, so the outer terminal gets it: tmux's with each ESC doubled, screen's as it is. */
214
+ const termPassthrough = (multiplexer: string, query: string): string => {
215
+ if (multiplexer === "tmux") {
216
+ return `\x1bPtmux;${replaceAll(query, "\x1b", "\x1b\x1b")}\x1b\\`
217
+ }
218
+ return multiplexer === "screen" ? `\x1bP${query}\x1b\\` : query
219
+ }
220
+
221
+ /** Every query the probe asks, as one write; `TermProbe` reads the answers. */
222
+ export const probeQueries = (env: TermEnv): string => {
223
+ const mux: string = termMultiplexer(env)
224
+ const parts: string[] = ["\x1b[>q", "\x1b[?2026$p", "\x1b[?1016$p", "\x1b[?u"]
225
+ const strings: boolean = mux !== "screen" && env.term !== "linux"
226
+ if (strings) {
227
+ parts.push(termPassthrough(mux, "\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\"))
228
+ }
229
+ parts.push("\x1b[?1;1;0S")
230
+ parts.push(termPassthrough(mux, "\x1b[16t"))
231
+ parts.push(termPassthrough(mux, "\x1b[14t"))
232
+ if (strings) {
233
+ parts.push("\x1b[48;2;1;2;3m\x1bP$qm\x1b\\\x1b[m")
234
+ }
235
+ parts.push("\x1b[c")
236
+ return parts.join("")
237
+ }
238
+
239
+ /** Whether `e` is the reply opened by `introducer`, with private marker `prefix`, intermediate `intermediate` and final `final`. */
240
+ const termIsReply = (e: TermEvent, introducer: i32, prefix: i32, intermediate: i32, final: i32): boolean =>
241
+ e.type === EVENT_REPLY &&
242
+ e.introducer === introducer &&
243
+ e.prefix === prefix &&
244
+ e.intermediate === intermediate &&
245
+ e.reply === final
246
+
247
+ /** Parameter `k` of `e`, or -1 when it has fewer. */
248
+ const termParam = (e: TermEvent, k: i32): i32 => (k < toI32(e.params.length) ? e.params[k] : -1)
249
+
250
+ /** Whether a DECRPM value says the mode is known: set (1), reset (2) or permanently set (3). */
251
+ const termModeKnown = (value: i32): boolean => value >= 1 && value <= 3
252
+
253
+ /** Whether `e` has a parameter equal to `value`. */
254
+ const termHasParam = (e: TermEvent, value: i32): boolean => {
255
+ for (const p of e.params) {
256
+ if (p === value) {
257
+ return true
258
+ }
259
+ }
260
+ return false
261
+ }
262
+
263
+ /**
264
+ * A probe in progress: made when `probeQueries` is written, at `sentAtMs`,
265
+ * for a window of `cols` by `rows` cells, fed what the terminal sends.
266
+ */
267
+ export class TermProbe {
268
+ /** The environment the queries were made for. */
269
+ env: TermEnv
270
+ /** The window's width in cells, to turn `CSI 14 t` into a cell width. */
271
+ cols: i32
272
+ /** The window's height in cells, to turn `CSI 14 t` into a cell height. */
273
+ rows: i32
274
+ /** When the queries were written. */
275
+ sentAtMs: f64
276
+ /** How long to wait for the answer to DA1 (`TERM_PROBE_TIMEOUT_MS` by default). */
277
+ timeoutMs: f64
278
+ /** The decoder the terminal's bytes go through. */
279
+ decoder: KeyDecoder
280
+ /** The replies so far, in order. */
281
+ replies: TermEvent[]
282
+ /** Every other event so far, in order: what the user typed during the probe. */
283
+ held: TermEvent[]
284
+ /** When DA1 was answered; -1 before. */
285
+ answeredAtMs: f64
286
+
287
+ /** A probe whose queries were written at `sentAtMs` to a window of `cols` by `rows` cells. */
288
+ constructor(env: TermEnv, cols: i32, rows: i32, sentAtMs: f64) {
289
+ this.env = env
290
+ this.cols = cols
291
+ this.rows = rows
292
+ this.sentAtMs = sentAtMs
293
+ this.timeoutMs = TERM_PROBE_TIMEOUT_MS
294
+ this.decoder = new KeyDecoder(TERM_PROBE_ESCAPE_MS)
295
+ this.replies = []
296
+ this.held = []
297
+ this.answeredAtMs = -1.0
298
+ }
299
+
300
+ /** Take in `events`: replies kept, DA1 marking the probe done, the rest held. */
301
+ take(events: TermEvent[], nowMs: f64): void {
302
+ for (const e of events) {
303
+ if (e.type !== EVENT_REPLY) {
304
+ this.held.push(e)
305
+ continue
306
+ }
307
+ this.replies.push(e)
308
+ if (this.answeredAtMs < 0.0 && termIsReply(e, 91, 63, 0, 99)) {
309
+ this.answeredAtMs = nowMs
310
+ }
311
+ }
312
+ }
313
+
314
+ /** Decode `bytes[0, count)`, read at `nowMs`. */
315
+ feed(bytes: u8[], count: i32, nowMs: f64): void {
316
+ this.take(this.decoder.feedEvents(bytes, count, nowMs), nowMs)
317
+ }
318
+
319
+ /** Whether DA1 has been answered, so every reply is in. */
320
+ done(): boolean {
321
+ return this.answeredAtMs >= 0.0
322
+ }
323
+
324
+ /**
325
+ * Whether `timeoutMs` has passed since the send with DA1 unanswered. Once it
326
+ * has, it calls `finish(nowMs)`, so `heldEvents` is complete.
327
+ */
328
+ timedOut(nowMs: f64): boolean {
329
+ if (this.done() || nowMs - this.sentAtMs < this.timeoutMs) {
330
+ return false
331
+ }
332
+ this.finish(nowMs)
333
+ return true
334
+ }
335
+
336
+ /**
337
+ * End the probe at `nowMs`: what the decoder still holds — an ESC or the
338
+ * start of a key that came in the read with DA1, or an unfinished reply on a
339
+ * timeout — is given up (the ESC as a key, the reply as nothing) and
340
+ * `heldEvents` returned complete. Call it once, when `done()` or `timedOut`
341
+ * ends the probe, and hand every later read to the program's own decoder.
342
+ */
343
+ finish(nowMs: f64): TermEvent[] {
344
+ this.take(this.decoder.pollEvents(nowMs + this.decoder.escapeTimeoutMs), nowMs)
345
+ return this.held
346
+ }
347
+
348
+ /** The events that were not replies — keys typed, a paste — in the order they came, for the program to handle. */
349
+ heldEvents(): TermEvent[] {
350
+ return this.held
351
+ }
352
+
353
+ /** The time from the send to the answer to DA1, in milliseconds; -1 before it. */
354
+ rttMs(): f64 {
355
+ return this.done() ? this.answeredAtMs - this.sentAtMs : -1.0
356
+ }
357
+
358
+ /** What the terminal can do, from the replies so far and the environment. */
359
+ result(): TermCaps {
360
+ const env: TermEnv = this.env
361
+ const caps: TermCaps = new TermCaps()
362
+ const overrides: string[] = []
363
+ let sgrRead: boolean = false
364
+ let areaW: i32 = 0
365
+ let areaH: i32 = 0
366
+ for (const e of this.replies) {
367
+ if (termIsReply(e, 91, 63, 0, 99)) {
368
+ caps.answered = true
369
+ caps.sixel = caps.sixel || termHasParam(e, 4)
370
+ } else if (termIsReply(e, 80, 62, 0, 124)) {
371
+ caps.name = e.text
372
+ } else if (termIsReply(e, 91, 63, 36, 121) && termParam(e, 0) === 2026) {
373
+ caps.sync = termModeKnown(termParam(e, 1))
374
+ } else if (termIsReply(e, 91, 63, 36, 121) && termParam(e, 0) === 1016) {
375
+ caps.pixelMouse = termModeKnown(termParam(e, 1))
376
+ } else if (termIsReply(e, 91, 63, 0, 117)) {
377
+ caps.kittyKeyboard = true
378
+ } else if (termIsReply(e, 95, 0, 0, 71)) {
379
+ caps.kittyGraphics =
380
+ caps.kittyGraphics || (toI32(e.text.indexOf("i=31")) === 0 && toI32(e.text.indexOf(";OK")) >= 0)
381
+ } else if (termIsReply(e, 91, 63, 0, 83) && termParam(e, 0) === 1 && termParam(e, 1) === 0) {
382
+ caps.sixelRegisters = termParam(e, 2)
383
+ } else if (termIsReply(e, 91, 0, 0, 116) && termParam(e, 0) === 6) {
384
+ caps.cellHeightPx = termParam(e, 1)
385
+ caps.cellWidthPx = termParam(e, 2)
386
+ } else if (termIsReply(e, 91, 0, 0, 116) && termParam(e, 0) === 4) {
387
+ areaH = termParam(e, 1)
388
+ areaW = termParam(e, 2)
389
+ } else if (termIsReply(e, 80, 0, 36, 114) && termParam(e, 0) === 1) {
390
+ sgrRead = sgrRead || toI32(e.text.indexOf("48:2:")) >= 0 || toI32(e.text.indexOf("48;2;")) >= 0
391
+ }
392
+ }
393
+ if (caps.cellWidthPx <= 0 && areaW > 0 && this.cols > 0 && areaH > 0 && this.rows > 0) {
394
+ caps.cellWidthPx = (areaW / this.cols) | 0
395
+ caps.cellHeightPx = (areaH / this.rows) | 0
396
+ }
397
+ if (caps.cellWidthPx <= 0 || caps.cellHeightPx <= 0) {
398
+ caps.cellWidthPx = 0
399
+ caps.cellHeightPx = 0
400
+ }
401
+ if (caps.name === "") {
402
+ caps.name = env.termProgram
403
+ }
404
+ caps.multiplexer = termMultiplexer(env)
405
+ caps.linuxConsole = env.term === "linux"
406
+ caps.remoteHint = env.sshConnection !== "" || env.sshClient !== "" || env.sshTty !== ""
407
+ caps.probeRttMs = this.rttMs()
408
+
409
+ const colorterm: boolean = env.colorterm === "truecolor" || env.colorterm === "24bit"
410
+ caps.trueColor = colorterm || sgrRead
411
+ if (colorterm && !sgrRead) {
412
+ overrides.push(`COLORTERM=${env.colorterm}: true colour`)
413
+ }
414
+ if (caps.trueColor) {
415
+ caps.colors = TERM_PROBE_TRUE_COLORS
416
+ } else if (toI32(env.term.indexOf("256color")) >= 0) {
417
+ caps.colors = 256
418
+ }
419
+ if (caps.linuxConsole) {
420
+ caps.colors = 16
421
+ caps.trueColor = false
422
+ caps.kittyKeyboard = false
423
+ caps.sync = false
424
+ overrides.push("TERM=linux: 16 colours, ascii, no kitty keyboard, no sync")
425
+ }
426
+ if (env.noColor !== "") {
427
+ caps.colors = 16
428
+ caps.trueColor = false
429
+ overrides.push("NO_COLOR: 16 colours")
430
+ }
431
+
432
+ if (caps.linuxConsole) {
433
+ caps.graphics = "ascii"
434
+ } else if (caps.kittyGraphics) {
435
+ caps.graphics = "kitty"
436
+ } else if (caps.sixel && caps.sixelRegisters >= 256) {
437
+ caps.graphics = "sixel"
438
+ } else if (env.term !== "dumb") {
439
+ caps.graphics = "halfblock"
440
+ }
441
+ const forced: string = env.nishTermGraphics
442
+ if (forced === "halfblock" || forced === "ascii") {
443
+ caps.graphics = forced
444
+ overrides.push(`NISH_TERM_GRAPHICS=${forced}: ${forced}`)
445
+ } else if (forced === "kitty" || forced === "sixel") {
446
+ const answeredFor: boolean = forced === "kitty" ? caps.kittyGraphics : caps.sixel
447
+ if (answeredFor || (caps.answered && !caps.linuxConsole && caps.multiplexer === "none")) {
448
+ caps.graphics = forced
449
+ overrides.push(`NISH_TERM_GRAPHICS=${forced}: ${forced}`)
450
+ } else {
451
+ overrides.push(`NISH_TERM_GRAPHICS=${forced}: refused, the terminal did not answer for it`)
452
+ }
453
+ } else if (forced !== "") {
454
+ overrides.push(`NISH_TERM_GRAPHICS=${forced}: ignored, not kitty, sixel, halfblock or ascii`)
455
+ }
456
+ caps.overrides = overrides.join("; ")
457
+ return caps
458
+ }
459
+ }