leduo-patrol 1.0.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.
@@ -0,0 +1,84 @@
1
+ import { spawn } from "node-pty";
2
+ import { EventEmitter } from "node:events";
3
+ import { existsSync } from "node:fs";
4
+ // Resolve bash path; prefer the user's login shell if it is bash, then common locations
5
+ function resolveBashPath() {
6
+ const loginShell = process.env.SHELL ?? "";
7
+ if (loginShell && /bash$/i.test(loginShell) && existsSync(loginShell)) {
8
+ return loginShell;
9
+ }
10
+ for (const candidate of ["/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"]) {
11
+ if (existsSync(candidate)) {
12
+ return candidate;
13
+ }
14
+ }
15
+ // Fall back to plain "bash" and let the OS resolve it via PATH
16
+ return "bash";
17
+ }
18
+ /**
19
+ * A restricted interactive shell session backed by a PTY.
20
+ *
21
+ * Security model:
22
+ * - Spawns `bash --restricted` (rbash): prevents `cd`, modifying PATH,
23
+ * output redirections, and running commands with slashes in the name.
24
+ * - Starts in `workspacePath` – with rbash's cd restriction users remain there.
25
+ * - Strips sensitive environment variables (API keys, tokens, etc.).
26
+ * - Inherits PATH so common tools (git, npm, etc.) stay available.
27
+ */
28
+ export class ShellSession extends EventEmitter {
29
+ pty;
30
+ _alive = true;
31
+ constructor(workspacePath, cols = 80, rows = 24) {
32
+ super();
33
+ const safeEnv = {
34
+ TERM: "xterm-256color",
35
+ COLORTERM: "truecolor",
36
+ HOME: workspacePath,
37
+ PWD: workspacePath,
38
+ // Inherit PATH so tools like git, npm, etc. are available
39
+ PATH: process.env.PATH ?? "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
40
+ LANG: process.env.LANG ?? "en_US.UTF-8",
41
+ // Pass through locale/user info if available
42
+ ...(process.env.USER ? { USER: process.env.USER } : {}),
43
+ ...(process.env.LOGNAME ? { LOGNAME: process.env.LOGNAME } : {}),
44
+ // Git user info so commits work inside the terminal
45
+ ...(process.env.GIT_AUTHOR_NAME ? { GIT_AUTHOR_NAME: process.env.GIT_AUTHOR_NAME } : {}),
46
+ ...(process.env.GIT_AUTHOR_EMAIL ? { GIT_AUTHOR_EMAIL: process.env.GIT_AUTHOR_EMAIL } : {}),
47
+ ...(process.env.GIT_COMMITTER_NAME ? { GIT_COMMITTER_NAME: process.env.GIT_COMMITTER_NAME } : {}),
48
+ ...(process.env.GIT_COMMITTER_EMAIL ? { GIT_COMMITTER_EMAIL: process.env.GIT_COMMITTER_EMAIL } : {}),
49
+ };
50
+ this.pty = spawn(resolveBashPath(), ["--restricted", "--norc", "--noprofile"], {
51
+ name: "xterm-256color",
52
+ cols,
53
+ rows,
54
+ cwd: workspacePath,
55
+ env: safeEnv,
56
+ });
57
+ this.pty.onData((data) => {
58
+ this.emit("output", data);
59
+ });
60
+ this.pty.onExit(({ exitCode, signal }) => {
61
+ this._alive = false;
62
+ this.emit("exit", exitCode, signal);
63
+ });
64
+ }
65
+ get alive() {
66
+ return this._alive;
67
+ }
68
+ write(data) {
69
+ if (this._alive) {
70
+ this.pty.write(data);
71
+ }
72
+ }
73
+ resize(cols, rows) {
74
+ if (this._alive) {
75
+ this.pty.resize(Math.max(cols, 2), Math.max(rows, 2));
76
+ }
77
+ }
78
+ kill() {
79
+ if (this._alive) {
80
+ this._alive = false;
81
+ this.pty.kill();
82
+ }
83
+ }
84
+ }
@@ -0,0 +1 @@
1
+ var p=2,c=1,d=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let i=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(i._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let i=this._terminal.options.scrollback===0?0:this._terminal.options.overviewRuler?.width||14,s=window.getComputedStyle(this._terminal.element.parentElement),l=parseInt(s.getPropertyValue("height")),o=Math.max(0,parseInt(s.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r={top:parseInt(t.getPropertyValue("padding-top")),bottom:parseInt(t.getPropertyValue("padding-bottom")),right:parseInt(t.getPropertyValue("padding-right")),left:parseInt(t.getPropertyValue("padding-left"))},a=r.top+r.bottom,n=r.right+r.left,h=l-a,m=o-n-i;return{cols:Math.max(p,Math.floor(m/e.css.cell.width)),rows:Math.max(c,Math.floor(h/e.css.cell.height))}}};export{d as FitAddon};
Binary file