typescript-virtual-container 1.5.8 → 1.5.9
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/README.md +39 -29
- package/dist/.tsbuildinfo +1 -1
- package/dist/SSHMimic/executor.js +9 -0
- package/dist/SSHMimic/prompt.js +2 -2
- package/dist/VirtualShell/shell.js +31 -1
- package/dist/VirtualShell/shellParser.js +35 -3
- package/dist/commands/coreutils.d.ts +55 -0
- package/dist/commands/coreutils.js +275 -0
- package/dist/commands/manuals-bundle.js +227 -0
- package/dist/commands/pacman.d.ts +8 -0
- package/dist/commands/pacman.js +15 -0
- package/dist/commands/registry.js +13 -0
- package/dist/commands/runtime.js +35 -0
- package/dist/commands/sh.js +5 -3
- package/dist/modules/linuxRootfs.js +4 -4
- package/dist/modules/nanoEditor.d.ts +1 -1
- package/dist/modules/nanoEditor.js +22 -4
- package/dist/modules/pacmanGame.d.ts +59 -0
- package/dist/modules/pacmanGame.js +655 -0
- package/dist/modules/webTermRenderer.d.ts +8 -0
- package/dist/modules/webTermRenderer.js +163 -29
- package/dist/types/commands.d.ts +2 -0
- package/dist/types/pipeline.d.ts +2 -0
- package/package.json +2 -2
|
@@ -15,9 +15,11 @@ export class WebTermRenderer {
|
|
|
15
15
|
rows;
|
|
16
16
|
cols;
|
|
17
17
|
screen;
|
|
18
|
+
scrollback = [];
|
|
18
19
|
curRow = 0;
|
|
19
20
|
curCol = 0;
|
|
20
21
|
cursorVisible = true;
|
|
22
|
+
_cleared = false;
|
|
21
23
|
// Current SGR state
|
|
22
24
|
bold = false;
|
|
23
25
|
reverse = false;
|
|
@@ -66,8 +68,34 @@ export class WebTermRenderer {
|
|
|
66
68
|
this.handleCsi(seq, cmd);
|
|
67
69
|
i = j + 1;
|
|
68
70
|
}
|
|
71
|
+
else if (next === "]") {
|
|
72
|
+
// OSC (Operating System Command) — terminator is BEL (\x07) or ST (ESC \)
|
|
73
|
+
// Must consume fully or the payload prints as raw text and corrupts SGR state.
|
|
74
|
+
let j = i + 2;
|
|
75
|
+
while (j < this.buf.length) {
|
|
76
|
+
if (this.buf[j] === "\x07") {
|
|
77
|
+
j++;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
if (this.buf[j] === "\x1b" && this.buf[j + 1] === "\\") {
|
|
81
|
+
j += 2;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
j++;
|
|
85
|
+
}
|
|
86
|
+
// If terminator not yet received, wait for more data
|
|
87
|
+
if (j >= this.buf.length && this.buf[j - 1] !== "\x07")
|
|
88
|
+
break;
|
|
89
|
+
i = j;
|
|
90
|
+
}
|
|
91
|
+
else if (next === "O") {
|
|
92
|
+
// SS3 — single extra byte (F1-F4, cursor keys in application mode)
|
|
93
|
+
if (i + 2 >= this.buf.length)
|
|
94
|
+
break; // wait for more data
|
|
95
|
+
i += 3; // ESC O <cmd>
|
|
96
|
+
}
|
|
69
97
|
else {
|
|
70
|
-
i += 2; // skip unknown ESC sequence
|
|
98
|
+
i += 2; // skip unknown 2-char ESC sequence
|
|
71
99
|
}
|
|
72
100
|
}
|
|
73
101
|
else if (ch === "\r") {
|
|
@@ -75,7 +103,12 @@ export class WebTermRenderer {
|
|
|
75
103
|
i++;
|
|
76
104
|
}
|
|
77
105
|
else if (ch === "\n") {
|
|
78
|
-
|
|
106
|
+
if (this.curRow < this.rows - 1) {
|
|
107
|
+
this.curRow++;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.scrollUp();
|
|
111
|
+
}
|
|
79
112
|
i++;
|
|
80
113
|
}
|
|
81
114
|
else if (ch.charCodeAt(0) >= 32) {
|
|
@@ -116,15 +149,6 @@ export class WebTermRenderer {
|
|
|
116
149
|
}
|
|
117
150
|
return;
|
|
118
151
|
}
|
|
119
|
-
if (cmd === "J") {
|
|
120
|
-
const mode = seq === "" ? 0 : Number.parseInt(seq, 10);
|
|
121
|
-
if (mode === 2) {
|
|
122
|
-
this.screen = this.makeScreen();
|
|
123
|
-
this.curRow = 0;
|
|
124
|
-
this.curCol = 0;
|
|
125
|
-
}
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
152
|
if (cmd === "m") {
|
|
129
153
|
this.handleSgr(seq);
|
|
130
154
|
return;
|
|
@@ -137,6 +161,57 @@ export class WebTermRenderer {
|
|
|
137
161
|
this.cursorVisible = true;
|
|
138
162
|
return;
|
|
139
163
|
}
|
|
164
|
+
// Cursor movement (relative)
|
|
165
|
+
if (cmd === "A") {
|
|
166
|
+
const n = Number.parseInt(seq || "1", 10) || 1;
|
|
167
|
+
this.curRow = Math.max(0, this.curRow - n);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (cmd === "B") {
|
|
171
|
+
const n = Number.parseInt(seq || "1", 10) || 1;
|
|
172
|
+
this.curRow = Math.min(this.rows - 1, this.curRow + n);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (cmd === "C") {
|
|
176
|
+
const n = Number.parseInt(seq || "1", 10) || 1;
|
|
177
|
+
this.curCol = Math.min(this.cols - 1, this.curCol + n);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (cmd === "D") {
|
|
181
|
+
const n = Number.parseInt(seq || "1", 10) || 1;
|
|
182
|
+
this.curCol = Math.max(0, this.curCol - n);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
// Cursor column absolute
|
|
186
|
+
if (cmd === "G") {
|
|
187
|
+
const n = Number.parseInt(seq || "1", 10) || 1;
|
|
188
|
+
this.curCol = Math.max(0, Math.min(n - 1, this.cols - 1));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Erase display modes 0/1
|
|
192
|
+
if (cmd === "J") {
|
|
193
|
+
const mode = seq === "" ? 0 : Number.parseInt(seq, 10);
|
|
194
|
+
if (mode === 0) {
|
|
195
|
+
for (let c = this.curCol; c < this.cols; c++)
|
|
196
|
+
this.screen[this.curRow][c] = makeCell();
|
|
197
|
+
for (let r = this.curRow + 1; r < this.rows; r++)
|
|
198
|
+
this.screen[r] = Array.from({ length: this.cols }, () => makeCell());
|
|
199
|
+
}
|
|
200
|
+
else if (mode === 1) {
|
|
201
|
+
for (let r = 0; r < this.curRow; r++)
|
|
202
|
+
this.screen[r] = Array.from({ length: this.cols }, () => makeCell());
|
|
203
|
+
for (let c = 0; c <= this.curCol; c++)
|
|
204
|
+
this.screen[this.curRow][c] = makeCell();
|
|
205
|
+
}
|
|
206
|
+
else if (mode === 2) {
|
|
207
|
+
this.screen = this.makeScreen();
|
|
208
|
+
this.scrollback = [];
|
|
209
|
+
this.curRow = 0;
|
|
210
|
+
this.curCol = 0;
|
|
211
|
+
this._cleared = true;
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
140
215
|
}
|
|
141
216
|
handleSgr(seq) {
|
|
142
217
|
const codes = seq === "" ? [0] : seq.split(";").map((n) => Number.parseInt(n || "0", 10));
|
|
@@ -202,9 +277,24 @@ export class WebTermRenderer {
|
|
|
202
277
|
i++;
|
|
203
278
|
}
|
|
204
279
|
}
|
|
280
|
+
scrollUp() {
|
|
281
|
+
const line = this.screen.shift();
|
|
282
|
+
this.scrollback.push(line);
|
|
283
|
+
if (this.scrollback.length > 1000)
|
|
284
|
+
this.scrollback.shift();
|
|
285
|
+
this.screen.push(Array.from({ length: this.cols }, () => makeCell()));
|
|
286
|
+
// curRow stays at rows-1 (bottom)
|
|
287
|
+
}
|
|
205
288
|
putChar(ch) {
|
|
206
|
-
if (this.
|
|
207
|
-
|
|
289
|
+
if (this.curCol >= this.cols) {
|
|
290
|
+
this.curCol = 0;
|
|
291
|
+
if (this.curRow < this.rows - 1) {
|
|
292
|
+
this.curRow++;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
this.scrollUp();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
208
298
|
this.screen[this.curRow][this.curCol] = makeCell({
|
|
209
299
|
ch,
|
|
210
300
|
bold: this.bold,
|
|
@@ -213,11 +303,6 @@ export class WebTermRenderer {
|
|
|
213
303
|
bg: this.bg,
|
|
214
304
|
});
|
|
215
305
|
this.curCol++;
|
|
216
|
-
if (this.curCol >= this.cols) {
|
|
217
|
-
this.curCol = 0;
|
|
218
|
-
if (this.curRow < this.rows - 1)
|
|
219
|
-
this.curRow++;
|
|
220
|
-
}
|
|
221
306
|
}
|
|
222
307
|
makeScreen(rows = this.rows, cols = this.cols) {
|
|
223
308
|
return Array.from({ length: rows }, () => Array.from({ length: cols }, () => makeCell()));
|
|
@@ -235,14 +320,60 @@ export class WebTermRenderer {
|
|
|
235
320
|
let fg = cell.fg ?? "#ccc";
|
|
236
321
|
let bg = cell.bg ?? "transparent";
|
|
237
322
|
if (cell.reverse) {
|
|
238
|
-
[fg, bg] = [bg === "transparent" ? "#000" : bg, fg];
|
|
323
|
+
[fg, bg] = [bg === "transparent" ? "#000" : bg, fg === "transparent" ? "#000" : fg];
|
|
239
324
|
}
|
|
240
|
-
if (cell.bold && !cell.fg)
|
|
241
|
-
fg = "#fff";
|
|
242
325
|
if (isCursor) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
326
|
+
// Isoler le curseur dans son propre span pour éviter que sa couleur
|
|
327
|
+
// inversée ne déborde sur les cellules vides adjacentes.
|
|
328
|
+
if (spanOpen) {
|
|
329
|
+
html += "</span>";
|
|
330
|
+
spanOpen = false;
|
|
331
|
+
lastStyle = "";
|
|
332
|
+
}
|
|
333
|
+
const curFg = bg === "transparent" ? "#000" : bg;
|
|
334
|
+
const boldPart = cell.bold ? "font-weight:bold;" : "";
|
|
335
|
+
html += `<span style="color:${curFg};background:#ccc;${boldPart}">${escHtml(cell.ch)}</span>`;
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
const style = `color:${fg};background:${bg};${cell.bold ? "font-weight:bold;" : ""}`;
|
|
339
|
+
if (style !== lastStyle) {
|
|
340
|
+
if (spanOpen)
|
|
341
|
+
html += "</span>";
|
|
342
|
+
html += `<span style="${style}">`;
|
|
343
|
+
spanOpen = true;
|
|
344
|
+
lastStyle = style;
|
|
345
|
+
}
|
|
346
|
+
html += escHtml(cell.ch);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (spanOpen)
|
|
350
|
+
html += "</span>";
|
|
351
|
+
if (r < this.rows - 1)
|
|
352
|
+
html += "\n";
|
|
353
|
+
}
|
|
354
|
+
return html;
|
|
355
|
+
}
|
|
356
|
+
get cursorRow() { return this.curRow; }
|
|
357
|
+
get cursorCol() { return this.curCol; }
|
|
358
|
+
get isCursorVisible() { return this.cursorVisible; }
|
|
359
|
+
/** Returns true (once) if CSI 2J was received since last call. */
|
|
360
|
+
consumeCleared() {
|
|
361
|
+
const v = this._cleared;
|
|
362
|
+
this._cleared = false;
|
|
363
|
+
return v;
|
|
364
|
+
}
|
|
365
|
+
get scrollbackLength() { return this.scrollback.length; }
|
|
366
|
+
clearScrollback() { this.scrollback = []; }
|
|
367
|
+
renderScrollbackHtml() {
|
|
368
|
+
let html = "";
|
|
369
|
+
for (const row of this.scrollback) {
|
|
370
|
+
let spanOpen = false;
|
|
371
|
+
let lastStyle = "";
|
|
372
|
+
for (const cell of row) {
|
|
373
|
+
let fg = cell.fg ?? "#ccc";
|
|
374
|
+
let bg = cell.bg ?? "transparent";
|
|
375
|
+
if (cell.reverse) {
|
|
376
|
+
[fg, bg] = [bg === "transparent" ? "#000" : bg, fg === "transparent" ? "#000" : fg];
|
|
246
377
|
}
|
|
247
378
|
const style = `color:${fg};background:${bg};${cell.bold ? "font-weight:bold;" : ""}`;
|
|
248
379
|
if (style !== lastStyle) {
|
|
@@ -256,15 +387,18 @@ export class WebTermRenderer {
|
|
|
256
387
|
}
|
|
257
388
|
if (spanOpen)
|
|
258
389
|
html += "</span>";
|
|
259
|
-
|
|
260
|
-
html += "\n";
|
|
390
|
+
html += "\n";
|
|
261
391
|
}
|
|
262
392
|
return html;
|
|
263
393
|
}
|
|
264
|
-
get cursorRow() { return this.curRow; }
|
|
265
|
-
get cursorCol() { return this.curCol; }
|
|
266
|
-
get isCursorVisible() { return this.cursorVisible; }
|
|
267
394
|
}
|
|
395
|
+
// const ANSI_NORMAL_TO_BRIGHT: Record<string, string> = {
|
|
396
|
+
// "#000": "#555", "#c00": "#f55", "#0c0": "#5f5", "#cc0": "#ff5",
|
|
397
|
+
// "#00c": "#55f", "#c0c": "#f5f", "#0cc": "#5ff", "#ccc": "#fff",
|
|
398
|
+
// };
|
|
399
|
+
// function boldBright(fg: string): string {
|
|
400
|
+
// return ANSI_NORMAL_TO_BRIGHT[fg] ?? fg;
|
|
401
|
+
// }
|
|
268
402
|
function escHtml(ch) {
|
|
269
403
|
if (ch === "&")
|
|
270
404
|
return "&";
|
package/dist/types/commands.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface CommandResult {
|
|
|
27
27
|
openEditor?: NanoEditorSession;
|
|
28
28
|
/** Request opening built-in htop-like screen. */
|
|
29
29
|
openHtop?: boolean;
|
|
30
|
+
/** Request opening built-in Pac-Man game. */
|
|
31
|
+
openPacman?: boolean;
|
|
30
32
|
/** Request sudo password challenge flow. */
|
|
31
33
|
sudoChallenge?: SudoChallenge;
|
|
32
34
|
/** Request a generic password challenge (adduser, passwd). */
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface Statement {
|
|
|
36
36
|
op?: LogicalOp;
|
|
37
37
|
/** Optional next statement in sequence. */
|
|
38
38
|
next?: Statement;
|
|
39
|
+
/** Run in background (trailing &). */
|
|
40
|
+
background?: boolean;
|
|
39
41
|
}
|
|
40
42
|
/** Top-level parse result for a script. */
|
|
41
43
|
export interface Script {
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "1.5.
|
|
7
|
+
"version": "1.5.9",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist/",
|
|
10
10
|
"README.md",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"deploy:npm": "bun publish --access public",
|
|
38
38
|
"bench": "rm -rf .benchmark-shells/ && bun benchmark-virtualshell.ts",
|
|
39
39
|
"benchmark": "bun benchmark-virtualshell.ts > benchmark-results.txt",
|
|
40
|
-
"web-build": "bunx esbuild src/web.ts --bundle --platform=browser --format=esm --target=es2020 --outfile=builds/web.min.js --tree-shaking=true --minify",
|
|
40
|
+
"web-build": "bunx esbuild src/web.ts --bundle --platform=browser --format=esm --target=es2020 --outfile=builds/web.min.js --tree-shaking=true --minify",
|
|
41
41
|
"web-build-iife": "bunx esbuild src/web.ts --bundle --platform=browser --format=iife --target=es2020 --outfile=builds/web-iife.min.js --tree-shaking=true --minify --global-name=WebShellLib",
|
|
42
42
|
"example-build": "bun run web-build && cp builds/web.min.js examples/web.min.js",
|
|
43
43
|
"example-serve": "cd examples && bun server.js",
|