koishi-plugin-terminal 1.0.1 → 1.0.3
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 +1 -0
- package/lib/index.js +53 -4
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -37,6 +37,8 @@ __export(src_exports, {
|
|
|
37
37
|
module.exports = __toCommonJS(src_exports);
|
|
38
38
|
var import_koishi = require("koishi");
|
|
39
39
|
var pty = __toESM(require("node-pty"));
|
|
40
|
+
var import_node_fs = require("node:fs");
|
|
41
|
+
var import_node_path = require("node:path");
|
|
40
42
|
var import_node_timers = require("node:timers");
|
|
41
43
|
var name = "terminal";
|
|
42
44
|
var Config = import_koishi.Schema.object({
|
|
@@ -75,10 +77,54 @@ function getKey(session) {
|
|
|
75
77
|
return `${session.platform}:${session.userId}`;
|
|
76
78
|
}
|
|
77
79
|
__name(getKey, "getKey");
|
|
80
|
+
function fixNodePtyHelper() {
|
|
81
|
+
if (process.platform !== "darwin") return;
|
|
82
|
+
try {
|
|
83
|
+
const root = (0, import_node_path.dirname)(require.resolve("node-pty/package.json"));
|
|
84
|
+
const helper = (0, import_node_path.join)(root, "prebuilds", `darwin-${process.arch}`, "spawn-helper");
|
|
85
|
+
const mode = (0, import_node_fs.statSync)(helper).mode;
|
|
86
|
+
if (!(mode & 73)) (0, import_node_fs.chmodSync)(helper, mode | 493);
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
__name(fixNodePtyHelper, "fixNodePtyHelper");
|
|
91
|
+
function isInteractiveCommand(command) {
|
|
92
|
+
const trimmed = command.trim();
|
|
93
|
+
if (!trimmed) return false;
|
|
94
|
+
const [name2, ...args] = trimmed.split(/\s+/);
|
|
95
|
+
if (/^(vi|vim|nvim|nano|emacs)$/.test(name2)) return true;
|
|
96
|
+
if (/^(less|more|man)$/.test(name2)) return true;
|
|
97
|
+
if (/^(top|htop|btop|watch)$/.test(name2)) return true;
|
|
98
|
+
if (/^(tmux|screen)$/.test(name2)) return true;
|
|
99
|
+
if (/^(sftp|ftp|telnet)$/.test(name2)) return true;
|
|
100
|
+
if (/^(mysql|psql|sqlite3|redis-cli|mongosh)$/.test(name2)) return true;
|
|
101
|
+
if (/^(node|python|python3|ipython|ruby|irb|php|lua|R)$/.test(name2) && !args.length) return true;
|
|
102
|
+
if (name2 === "tail" && args.includes("-f")) return true;
|
|
103
|
+
if (name2 === "docker" && args.includes("exec") && args.some((arg) => arg.includes("it"))) return true;
|
|
104
|
+
if (name2 === "kubectl" && args.includes("exec") && args.some((arg) => arg.includes("it"))) return true;
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
__name(isInteractiveCommand, "isInteractiveCommand");
|
|
78
108
|
var map = /* @__PURE__ */ new Map();
|
|
79
109
|
function apply(ctx, config) {
|
|
110
|
+
fixNodePtyHelper();
|
|
80
111
|
const allowedUsers = config.admin;
|
|
81
|
-
function
|
|
112
|
+
function refreshTimeout(shellSession, key, session) {
|
|
113
|
+
if (!config.timeout) return;
|
|
114
|
+
if (shellSession.timeoutTimer) (0, import_node_timers.clearTimeout)(shellSession.timeoutTimer);
|
|
115
|
+
shellSession.timeoutTimer = setTimeout(async () => {
|
|
116
|
+
if (map.get(key) !== shellSession) return;
|
|
117
|
+
cleanupSession(shellSession, key, true);
|
|
118
|
+
await session.send("Shell session timed out.");
|
|
119
|
+
}, config.timeout);
|
|
120
|
+
}
|
|
121
|
+
__name(refreshTimeout, "refreshTimeout");
|
|
122
|
+
function sendCommand(shellSession, key, session, command) {
|
|
123
|
+
refreshTimeout(shellSession, key, session);
|
|
124
|
+
if (isInteractiveCommand(command)) {
|
|
125
|
+
shellSession.terminal.write(`echo "Interactive command is not supported in chat terminal. Use a non-interactive form, or run shell -t to restart."\r`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
82
128
|
shellSession.terminal.write(command + "\r");
|
|
83
129
|
}
|
|
84
130
|
__name(sendCommand, "sendCommand");
|
|
@@ -115,12 +161,14 @@ function apply(ctx, config) {
|
|
|
115
161
|
});
|
|
116
162
|
shellSession.disposables.push(dataDisposable, exitDisposable);
|
|
117
163
|
map.set(key, shellSession);
|
|
164
|
+
refreshTimeout(shellSession, key, session);
|
|
118
165
|
return shellSession;
|
|
119
166
|
}
|
|
120
167
|
__name(initSession, "initSession");
|
|
121
168
|
function cleanupSession(shellSession, key, kill = true) {
|
|
122
169
|
map.delete(key);
|
|
123
170
|
if (shellSession.timer) (0, import_node_timers.clearTimeout)(shellSession.timer);
|
|
171
|
+
if (shellSession.timeoutTimer) (0, import_node_timers.clearTimeout)(shellSession.timeoutTimer);
|
|
124
172
|
shellSession.disposables.forEach((d) => d.dispose());
|
|
125
173
|
shellSession.disposables.length = 0;
|
|
126
174
|
if (kill) {
|
|
@@ -131,7 +179,7 @@ function apply(ctx, config) {
|
|
|
131
179
|
}
|
|
132
180
|
}
|
|
133
181
|
__name(cleanupSession, "cleanupSession");
|
|
134
|
-
ctx.command("shell [command:text]", "Start a persistent shell session", { authority:
|
|
182
|
+
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
183
|
if (!allowedUsers.includes(session.userId)) {
|
|
136
184
|
return "Unauthorized user.";
|
|
137
185
|
}
|
|
@@ -148,7 +196,7 @@ function apply(ctx, config) {
|
|
|
148
196
|
if (!command) return "Shell session started. Send regular messages as commands. Send shell -t to terminate.";
|
|
149
197
|
}
|
|
150
198
|
if (!command) return "Shell session is running.";
|
|
151
|
-
sendCommand(current, command);
|
|
199
|
+
sendCommand(current, key, session, command);
|
|
152
200
|
});
|
|
153
201
|
ctx.middleware(async (session, next) => {
|
|
154
202
|
const key = getKey(session);
|
|
@@ -159,12 +207,13 @@ function apply(ctx, config) {
|
|
|
159
207
|
return next();
|
|
160
208
|
}
|
|
161
209
|
if (!content) return;
|
|
162
|
-
sendCommand(current, content);
|
|
210
|
+
sendCommand(current, key, session, content);
|
|
163
211
|
}, true);
|
|
164
212
|
ctx.on("dispose", () => {
|
|
165
213
|
for (const current of map.values()) {
|
|
166
214
|
current.disposables.forEach((d) => d.dispose());
|
|
167
215
|
if (current.timer) (0, import_node_timers.clearTimeout)(current.timer);
|
|
216
|
+
if (current.timeoutTimer) (0, import_node_timers.clearTimeout)(current.timeoutTimer);
|
|
168
217
|
current.terminal.kill();
|
|
169
218
|
}
|
|
170
219
|
map.clear();
|