pty-manager 1.7.2 → 1.8.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.
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +90 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +90 -13
- package/dist/index.mjs.map +1 -1
- package/dist/pty-worker.js +85 -7
- package/package.json +1 -1
package/dist/pty-worker.js
CHANGED
|
@@ -1223,24 +1223,71 @@ var PTYSession = class _PTYSession extends import_events.EventEmitter {
|
|
|
1223
1223
|
throw new Error("Session not started");
|
|
1224
1224
|
}
|
|
1225
1225
|
const keyList = Array.isArray(keys) ? keys : [keys];
|
|
1226
|
+
const normalized = _PTYSession.normalizeKeyList(keyList);
|
|
1226
1227
|
this._stallEmissionCount = 0;
|
|
1227
1228
|
this.resetStallTimer();
|
|
1228
|
-
for (const key of
|
|
1229
|
-
const
|
|
1230
|
-
const sequence = SPECIAL_KEYS[normalizedKey];
|
|
1229
|
+
for (const key of normalized) {
|
|
1230
|
+
const sequence = SPECIAL_KEYS[key];
|
|
1231
1231
|
if (sequence) {
|
|
1232
1232
|
this._lastActivityAt = /* @__PURE__ */ new Date();
|
|
1233
1233
|
this.ptyProcess.write(sequence);
|
|
1234
|
-
this.logger.debug({ sessionId: this.id, key
|
|
1234
|
+
this.logger.debug({ sessionId: this.id, key }, "Sent special key");
|
|
1235
1235
|
} else {
|
|
1236
1236
|
this.logger.warn(
|
|
1237
|
-
{ sessionId: this.id, key
|
|
1237
|
+
{ sessionId: this.id, key },
|
|
1238
1238
|
"Unknown special key, sending as literal"
|
|
1239
1239
|
);
|
|
1240
1240
|
this.ptyProcess.write(key);
|
|
1241
1241
|
}
|
|
1242
1242
|
}
|
|
1243
1243
|
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Normalize a list of key names for SPECIAL_KEYS lookup.
|
|
1246
|
+
*
|
|
1247
|
+
* Handles two problems:
|
|
1248
|
+
* 1. Modifier aliases: "control" → "ctrl", "command" → "meta", "option" → "alt"
|
|
1249
|
+
* 2. Comma-separated compound keys from stall classifier: ["control", "c"] → ["ctrl+c"]
|
|
1250
|
+
* A bare modifier followed by a single char/key is joined with "+".
|
|
1251
|
+
*/
|
|
1252
|
+
static normalizeKeyList(keys) {
|
|
1253
|
+
const MODIFIER_MAP = {
|
|
1254
|
+
control: "ctrl",
|
|
1255
|
+
command: "meta",
|
|
1256
|
+
cmd: "meta",
|
|
1257
|
+
option: "alt",
|
|
1258
|
+
opt: "alt"
|
|
1259
|
+
};
|
|
1260
|
+
const MODIFIER_NAMES = /* @__PURE__ */ new Set([
|
|
1261
|
+
"ctrl",
|
|
1262
|
+
"alt",
|
|
1263
|
+
"shift",
|
|
1264
|
+
"meta",
|
|
1265
|
+
// Also match the aliases so we can detect them before remapping
|
|
1266
|
+
...Object.keys(MODIFIER_MAP)
|
|
1267
|
+
]);
|
|
1268
|
+
const result = [];
|
|
1269
|
+
let i = 0;
|
|
1270
|
+
while (i < keys.length) {
|
|
1271
|
+
let key = keys[i].toLowerCase().trim();
|
|
1272
|
+
if (MODIFIER_MAP[key]) {
|
|
1273
|
+
key = MODIFIER_MAP[key];
|
|
1274
|
+
}
|
|
1275
|
+
if (MODIFIER_NAMES.has(key) && i + 1 < keys.length) {
|
|
1276
|
+
let nextKey = keys[i + 1].toLowerCase().trim();
|
|
1277
|
+
if (MODIFIER_MAP[nextKey]) {
|
|
1278
|
+
nextKey = MODIFIER_MAP[nextKey];
|
|
1279
|
+
}
|
|
1280
|
+
if (!MODIFIER_NAMES.has(nextKey)) {
|
|
1281
|
+
result.push(`${key}+${nextKey}`);
|
|
1282
|
+
i += 2;
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
result.push(key);
|
|
1287
|
+
i++;
|
|
1288
|
+
}
|
|
1289
|
+
return result;
|
|
1290
|
+
}
|
|
1244
1291
|
/**
|
|
1245
1292
|
* Select a TUI menu option by index (0-based).
|
|
1246
1293
|
* Sends Down arrow `optionIndex` times, then Enter, with 50ms delays.
|
|
@@ -1721,7 +1768,18 @@ var ShellAdapter = class {
|
|
|
1721
1768
|
return { detected: false };
|
|
1722
1769
|
}
|
|
1723
1770
|
detectReady(output) {
|
|
1724
|
-
|
|
1771
|
+
if (this.isContinuationPrompt(output)) {
|
|
1772
|
+
return false;
|
|
1773
|
+
}
|
|
1774
|
+
return this.getPromptPattern().test(this.stripAnsi(output));
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Detect shell continuation prompts that indicate the shell is NOT ready
|
|
1778
|
+
* for a new command (e.g., unclosed quote, heredoc, backtick).
|
|
1779
|
+
*/
|
|
1780
|
+
isContinuationPrompt(output) {
|
|
1781
|
+
const stripped = this.stripAnsi(output);
|
|
1782
|
+
return /(?:quote|dquote|heredoc|bquote|cmdsubst|pipe|then|else|do|loop)>\s*$/.test(stripped) || /(?:quote|dquote|heredoc|bquote)>\s*$/m.test(stripped);
|
|
1725
1783
|
}
|
|
1726
1784
|
detectExit(output) {
|
|
1727
1785
|
if (output.includes("exit")) {
|
|
@@ -1744,7 +1802,7 @@ var ShellAdapter = class {
|
|
|
1744
1802
|
}
|
|
1745
1803
|
getPromptPattern() {
|
|
1746
1804
|
const escaped = this.promptStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1747
|
-
return new RegExp(`(?:${escaped}
|
|
1805
|
+
return new RegExp(`(?:${escaped}|\\$|#)\\s*$`, "m");
|
|
1748
1806
|
}
|
|
1749
1807
|
async validateInstallation() {
|
|
1750
1808
|
return { installed: true };
|
|
@@ -1892,6 +1950,19 @@ function handleSendKeys(id, keys) {
|
|
|
1892
1950
|
ack("sendKeys", id, false, err instanceof Error ? err.message : String(err));
|
|
1893
1951
|
}
|
|
1894
1952
|
}
|
|
1953
|
+
function handleWriteRaw(id, data) {
|
|
1954
|
+
try {
|
|
1955
|
+
const session = manager.getSession(id);
|
|
1956
|
+
if (!session) {
|
|
1957
|
+
ack("writeRaw", id, false, `Session ${id} not found`);
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1960
|
+
session.writeRaw(data);
|
|
1961
|
+
ack("writeRaw", id, true);
|
|
1962
|
+
} catch (err) {
|
|
1963
|
+
ack("writeRaw", id, false, err instanceof Error ? err.message : String(err));
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1895
1966
|
function handlePaste(id, text, bracketed = true) {
|
|
1896
1967
|
try {
|
|
1897
1968
|
const session = manager.getSession(id);
|
|
@@ -2104,6 +2175,13 @@ function processCommand(line) {
|
|
|
2104
2175
|
}
|
|
2105
2176
|
handleSendKeys(command.id, command.keys);
|
|
2106
2177
|
break;
|
|
2178
|
+
case "writeRaw":
|
|
2179
|
+
if (!command.id || command.data === void 0) {
|
|
2180
|
+
ack("writeRaw", command.id, false, "Missing id or data");
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
handleWriteRaw(command.id, command.data);
|
|
2184
|
+
break;
|
|
2107
2185
|
case "paste":
|
|
2108
2186
|
if (!command.id || command.text === void 0) {
|
|
2109
2187
|
ack("paste", command.id, false, "Missing id or text");
|
package/package.json
CHANGED