koishi-plugin-terminal 1.0.1 → 1.0.2

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/lib/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface ShellSession {
15
15
  terminal: pty.IPty;
16
16
  buffer: string;
17
17
  timer?: NodeJS.Timeout;
18
+ timeoutTimer?: NodeJS.Timeout;
18
19
  disposables: Array<{
19
20
  dispose(): void;
20
21
  }>;
package/lib/index.js CHANGED
@@ -75,10 +75,42 @@ function getKey(session) {
75
75
  return `${session.platform}:${session.userId}`;
76
76
  }
77
77
  __name(getKey, "getKey");
78
+ function isInteractiveCommand(command) {
79
+ const trimmed = command.trim();
80
+ if (!trimmed) return false;
81
+ const [name2, ...args] = trimmed.split(/\s+/);
82
+ if (/^(vi|vim|nvim|nano|emacs)$/.test(name2)) return true;
83
+ if (/^(less|more|man)$/.test(name2)) return true;
84
+ if (/^(top|htop|btop|watch)$/.test(name2)) return true;
85
+ if (/^(tmux|screen)$/.test(name2)) return true;
86
+ if (/^(ssh|sftp|ftp|telnet)$/.test(name2)) return true;
87
+ if (/^(mysql|psql|sqlite3|redis-cli|mongosh)$/.test(name2)) return true;
88
+ if (/^(node|python|python3|ipython|ruby|irb|php|lua|R)$/.test(name2) && !args.length) return true;
89
+ if (name2 === "tail" && args.includes("-f")) return true;
90
+ if (name2 === "docker" && args.includes("exec") && args.some((arg) => arg.includes("it"))) return true;
91
+ if (name2 === "kubectl" && args.includes("exec") && args.some((arg) => arg.includes("it"))) return true;
92
+ return false;
93
+ }
94
+ __name(isInteractiveCommand, "isInteractiveCommand");
78
95
  var map = /* @__PURE__ */ new Map();
79
96
  function apply(ctx, config) {
80
97
  const allowedUsers = config.admin;
81
- function sendCommand(shellSession, command) {
98
+ function refreshTimeout(shellSession, key, session) {
99
+ if (!config.timeout) return;
100
+ if (shellSession.timeoutTimer) (0, import_node_timers.clearTimeout)(shellSession.timeoutTimer);
101
+ shellSession.timeoutTimer = setTimeout(async () => {
102
+ if (map.get(key) !== shellSession) return;
103
+ cleanupSession(shellSession, key, true);
104
+ await session.send("Shell session timed out.");
105
+ }, config.timeout);
106
+ }
107
+ __name(refreshTimeout, "refreshTimeout");
108
+ function sendCommand(shellSession, key, session, command) {
109
+ refreshTimeout(shellSession, key, session);
110
+ if (isInteractiveCommand(command)) {
111
+ shellSession.terminal.write(`echo "Interactive command is not supported in chat terminal. Use a non-interactive form, or run shell -t to restart."\r`);
112
+ return;
113
+ }
82
114
  shellSession.terminal.write(command + "\r");
83
115
  }
84
116
  __name(sendCommand, "sendCommand");
@@ -115,12 +147,14 @@ function apply(ctx, config) {
115
147
  });
116
148
  shellSession.disposables.push(dataDisposable, exitDisposable);
117
149
  map.set(key, shellSession);
150
+ refreshTimeout(shellSession, key, session);
118
151
  return shellSession;
119
152
  }
120
153
  __name(initSession, "initSession");
121
154
  function cleanupSession(shellSession, key, kill = true) {
122
155
  map.delete(key);
123
156
  if (shellSession.timer) (0, import_node_timers.clearTimeout)(shellSession.timer);
157
+ if (shellSession.timeoutTimer) (0, import_node_timers.clearTimeout)(shellSession.timeoutTimer);
124
158
  shellSession.disposables.forEach((d) => d.dispose());
125
159
  shellSession.disposables.length = 0;
126
160
  if (kill) {
@@ -131,7 +165,7 @@ function apply(ctx, config) {
131
165
  }
132
166
  }
133
167
  __name(cleanupSession, "cleanupSession");
134
- ctx.command("shell [command:text]", "Start a persistent shell session", { authority: 0 }).option("terminate", "-t Terminate current shell session").usage("After start up, regular user messages will be sent to shell process.").example("shell echo Operating System: Three Easy Pieces > qljj.txt").action(async ({ session, options }, command) => {
168
+ ctx.command("shell [command:text]", "Start a persistent shell session", { authority: config.auth }).option("terminate", "-t Terminate current shell session").usage("After start up, regular user messages will be sent to shell process.").example("shell echo Operating System: Three Easy Pieces > qljj.txt").action(async ({ session, options }, command) => {
135
169
  if (!allowedUsers.includes(session.userId)) {
136
170
  return "Unauthorized user.";
137
171
  }
@@ -148,7 +182,7 @@ function apply(ctx, config) {
148
182
  if (!command) return "Shell session started. Send regular messages as commands. Send shell -t to terminate.";
149
183
  }
150
184
  if (!command) return "Shell session is running.";
151
- sendCommand(current, command);
185
+ sendCommand(current, key, session, command);
152
186
  });
153
187
  ctx.middleware(async (session, next) => {
154
188
  const key = getKey(session);
@@ -159,12 +193,13 @@ function apply(ctx, config) {
159
193
  return next();
160
194
  }
161
195
  if (!content) return;
162
- sendCommand(current, content);
196
+ sendCommand(current, key, session, content);
163
197
  }, true);
164
198
  ctx.on("dispose", () => {
165
199
  for (const current of map.values()) {
166
200
  current.disposables.forEach((d) => d.dispose());
167
201
  if (current.timer) (0, import_node_timers.clearTimeout)(current.timer);
202
+ if (current.timeoutTimer) (0, import_node_timers.clearTimeout)(current.timeoutTimer);
168
203
  current.terminal.kill();
169
204
  }
170
205
  map.clear();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-terminal",
3
3
  "description": "通过 QQ 运行持久的 Shell 终端",
4
- "version": "1.0.1",
4
+ "version": "1.0.2",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [