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.mjs CHANGED
@@ -1202,24 +1202,71 @@ var PTYSession = class _PTYSession extends EventEmitter {
1202
1202
  throw new Error("Session not started");
1203
1203
  }
1204
1204
  const keyList = Array.isArray(keys) ? keys : [keys];
1205
+ const normalized = _PTYSession.normalizeKeyList(keyList);
1205
1206
  this._stallEmissionCount = 0;
1206
1207
  this.resetStallTimer();
1207
- for (const key of keyList) {
1208
- const normalizedKey = key.toLowerCase().trim();
1209
- const sequence = SPECIAL_KEYS[normalizedKey];
1208
+ for (const key of normalized) {
1209
+ const sequence = SPECIAL_KEYS[key];
1210
1210
  if (sequence) {
1211
1211
  this._lastActivityAt = /* @__PURE__ */ new Date();
1212
1212
  this.ptyProcess.write(sequence);
1213
- this.logger.debug({ sessionId: this.id, key: normalizedKey }, "Sent special key");
1213
+ this.logger.debug({ sessionId: this.id, key }, "Sent special key");
1214
1214
  } else {
1215
1215
  this.logger.warn(
1216
- { sessionId: this.id, key: normalizedKey },
1216
+ { sessionId: this.id, key },
1217
1217
  "Unknown special key, sending as literal"
1218
1218
  );
1219
1219
  this.ptyProcess.write(key);
1220
1220
  }
1221
1221
  }
1222
1222
  }
1223
+ /**
1224
+ * Normalize a list of key names for SPECIAL_KEYS lookup.
1225
+ *
1226
+ * Handles two problems:
1227
+ * 1. Modifier aliases: "control" → "ctrl", "command" → "meta", "option" → "alt"
1228
+ * 2. Comma-separated compound keys from stall classifier: ["control", "c"] → ["ctrl+c"]
1229
+ * A bare modifier followed by a single char/key is joined with "+".
1230
+ */
1231
+ static normalizeKeyList(keys) {
1232
+ const MODIFIER_MAP = {
1233
+ control: "ctrl",
1234
+ command: "meta",
1235
+ cmd: "meta",
1236
+ option: "alt",
1237
+ opt: "alt"
1238
+ };
1239
+ const MODIFIER_NAMES = /* @__PURE__ */ new Set([
1240
+ "ctrl",
1241
+ "alt",
1242
+ "shift",
1243
+ "meta",
1244
+ // Also match the aliases so we can detect them before remapping
1245
+ ...Object.keys(MODIFIER_MAP)
1246
+ ]);
1247
+ const result = [];
1248
+ let i = 0;
1249
+ while (i < keys.length) {
1250
+ let key = keys[i].toLowerCase().trim();
1251
+ if (MODIFIER_MAP[key]) {
1252
+ key = MODIFIER_MAP[key];
1253
+ }
1254
+ if (MODIFIER_NAMES.has(key) && i + 1 < keys.length) {
1255
+ let nextKey = keys[i + 1].toLowerCase().trim();
1256
+ if (MODIFIER_MAP[nextKey]) {
1257
+ nextKey = MODIFIER_MAP[nextKey];
1258
+ }
1259
+ if (!MODIFIER_NAMES.has(nextKey)) {
1260
+ result.push(`${key}+${nextKey}`);
1261
+ i += 2;
1262
+ continue;
1263
+ }
1264
+ }
1265
+ result.push(key);
1266
+ i++;
1267
+ }
1268
+ return result;
1269
+ }
1223
1270
  /**
1224
1271
  * Select a TUI menu option by index (0-based).
1225
1272
  * Sends Down arrow `optionIndex` times, then Enter, with 50ms delays.
@@ -2221,7 +2268,18 @@ var ShellAdapter = class {
2221
2268
  return { detected: false };
2222
2269
  }
2223
2270
  detectReady(output) {
2224
- return output.includes(this.promptStr) || output.includes("$") || output.length > 10;
2271
+ if (this.isContinuationPrompt(output)) {
2272
+ return false;
2273
+ }
2274
+ return this.getPromptPattern().test(this.stripAnsi(output));
2275
+ }
2276
+ /**
2277
+ * Detect shell continuation prompts that indicate the shell is NOT ready
2278
+ * for a new command (e.g., unclosed quote, heredoc, backtick).
2279
+ */
2280
+ isContinuationPrompt(output) {
2281
+ const stripped = this.stripAnsi(output);
2282
+ return /(?:quote|dquote|heredoc|bquote|cmdsubst|pipe|then|else|do|loop)>\s*$/.test(stripped) || /(?:quote|dquote|heredoc|bquote)>\s*$/m.test(stripped);
2225
2283
  }
2226
2284
  detectExit(output) {
2227
2285
  if (output.includes("exit")) {
@@ -2244,7 +2302,7 @@ var ShellAdapter = class {
2244
2302
  }
2245
2303
  getPromptPattern() {
2246
2304
  const escaped = this.promptStr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2247
- return new RegExp(`(?:${escaped}|\\$|#|>)\\s*$`, "m");
2305
+ return new RegExp(`(?:${escaped}|\\$|#)\\s*$`, "m");
2248
2306
  }
2249
2307
  async validateInstallation() {
2250
2308
  return { installed: true };
@@ -2340,9 +2398,6 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
2340
2398
  const id = event.id;
2341
2399
  switch (eventType) {
2342
2400
  case "worker_ready":
2343
- if (this.adapterModules.length > 0) {
2344
- this.sendCommand({ cmd: "registerAdapters", modules: this.adapterModules });
2345
- }
2346
2401
  if (this._stallDetectionEnabled) {
2347
2402
  this.sendCommand({
2348
2403
  cmd: "configureStallDetection",
@@ -2350,9 +2405,23 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
2350
2405
  timeoutMs: this._stallTimeoutMs
2351
2406
  });
2352
2407
  }
2353
- this.ready = true;
2354
- this.readyResolve();
2355
- this.emit("ready");
2408
+ if (this.adapterModules.length > 0) {
2409
+ this.sendCommand({ cmd: "registerAdapters", modules: this.adapterModules });
2410
+ this.createPending("registerAdapters").then(() => {
2411
+ this.ready = true;
2412
+ this.readyResolve();
2413
+ this.emit("ready");
2414
+ }).catch((err) => {
2415
+ this.emit("worker_error", `Failed to register adapters: ${err}`);
2416
+ this.ready = true;
2417
+ this.readyResolve();
2418
+ this.emit("ready");
2419
+ });
2420
+ } else {
2421
+ this.ready = true;
2422
+ this.readyResolve();
2423
+ this.emit("ready");
2424
+ }
2356
2425
  break;
2357
2426
  case "spawned": {
2358
2427
  const session = {
@@ -2594,6 +2663,14 @@ var BunCompatiblePTYManager = class extends EventEmitter3 {
2594
2663
  this.sendCommand({ cmd: "sendKeys", id, keys });
2595
2664
  await this.createPending(`sendKeys:${id}`);
2596
2665
  }
2666
+ /**
2667
+ * Write raw data to a session (bypasses adapter formatting)
2668
+ */
2669
+ async writeRaw(id, data) {
2670
+ await this.waitForReady();
2671
+ this.sendCommand({ cmd: "writeRaw", id, data });
2672
+ await this.createPending(`writeRaw:${id}`);
2673
+ }
2597
2674
  /**
2598
2675
  * Paste text to a session
2599
2676
  */