@buildautomaton/cli 0.1.77 → 0.1.79

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.js CHANGED
@@ -3691,7 +3691,7 @@ var require_tree_kill = __commonJS({
3691
3691
  "../../node_modules/.pnpm/tree-kill@1.2.2/node_modules/tree-kill/index.js"(exports, module) {
3692
3692
  "use strict";
3693
3693
  var childProcess2 = __require("child_process");
3694
- var spawn11 = childProcess2.spawn;
3694
+ var spawn12 = childProcess2.spawn;
3695
3695
  var exec = childProcess2.exec;
3696
3696
  module.exports = function(pid, signal, callback) {
3697
3697
  if (typeof signal === "function" && callback === void 0) {
@@ -3716,7 +3716,7 @@ var require_tree_kill = __commonJS({
3716
3716
  break;
3717
3717
  case "darwin":
3718
3718
  buildProcessTree(pid, tree, pidsToProcess, function(parentPid) {
3719
- return spawn11("pgrep", ["-P", parentPid]);
3719
+ return spawn12("pgrep", ["-P", parentPid]);
3720
3720
  }, function() {
3721
3721
  killAll(tree, signal, callback);
3722
3722
  });
@@ -3728,7 +3728,7 @@ var require_tree_kill = __commonJS({
3728
3728
  // break;
3729
3729
  default:
3730
3730
  buildProcessTree(pid, tree, pidsToProcess, function(parentPid) {
3731
- return spawn11("ps", ["-o", "pid", "--no-headers", "--ppid", parentPid]);
3731
+ return spawn12("ps", ["-o", "pid", "--no-headers", "--ppid", parentPid]);
3732
3732
  }, function() {
3733
3733
  killAll(tree, signal, callback);
3734
3734
  });
@@ -21909,31 +21909,31 @@ function handleWorkerOutMessage(message) {
21909
21909
  return;
21910
21910
  }
21911
21911
  if (message.type === "error") {
21912
- const pending2 = message.requestId != null ? pendingRequests.get(message.requestId) : void 0;
21912
+ const pending3 = message.requestId != null ? pendingRequests.get(message.requestId) : void 0;
21913
21913
  maybeLogCodeNavCacheWorkerError(message.message);
21914
- if (pending2 != null) {
21914
+ if (pending3 != null) {
21915
21915
  pendingRequests.delete(message.requestId);
21916
21916
  if (isCliImmediateShutdownRequested()) {
21917
- pending2.reject(new CliSqliteInterrupted());
21917
+ pending3.reject(new CliSqliteInterrupted());
21918
21918
  } else {
21919
- pending2.reject(new Error(message.message));
21919
+ pending3.reject(new Error(message.message));
21920
21920
  }
21921
21921
  }
21922
21922
  return;
21923
21923
  }
21924
21924
  if (!("requestId" in message)) return;
21925
- const pending = pendingRequests.get(message.requestId);
21926
- if (pending == null) return;
21925
+ const pending2 = pendingRequests.get(message.requestId);
21926
+ if (pending2 == null) return;
21927
21927
  pendingRequests.delete(message.requestId);
21928
- pending.resolve(message);
21928
+ pending2.resolve(message);
21929
21929
  }
21930
21930
  function resetWorkerReadyState() {
21931
21931
  workerReady = false;
21932
21932
  workerReadyWaiters = [];
21933
21933
  }
21934
21934
  function rejectAllPendingRequests(error40) {
21935
- for (const [, pending] of pendingRequests) {
21936
- pending.reject(error40);
21935
+ for (const [, pending2] of pendingRequests) {
21936
+ pending2.reject(error40);
21937
21937
  }
21938
21938
  pendingRequests.clear();
21939
21939
  }
@@ -22966,14 +22966,14 @@ function legacyCliDiskMigrationIsPending() {
22966
22966
  return false;
22967
22967
  }
22968
22968
  function importCliSqliteLegacyDiskData(db, log2) {
22969
- const pending = legacyCliDiskMigrationIsPending();
22970
- if (pending && log2) {
22969
+ const pending2 = legacyCliDiskMigrationIsPending();
22970
+ if (pending2 && log2) {
22971
22971
  log2("Migrating legacy on-disk CLI data to SQLite\u2026");
22972
22972
  }
22973
22973
  archiveLegacyFileIndexJsonFiles();
22974
22974
  importLegacyAgentSessionsFromDisk(db);
22975
22975
  importLegacyPromptQueuesFromDisk(db);
22976
- if (pending && log2) {
22976
+ if (pending2 && log2) {
22977
22977
  log2("Legacy on-disk CLI data migration finished.");
22978
22978
  }
22979
22979
  }
@@ -23060,7 +23060,11 @@ var init_migrate_cli_sqlite = __esm({
23060
23060
  });
23061
23061
 
23062
23062
  // src/sqlite/cli-database.ts
23063
- var cliSqlite, safeCloseCliSqliteDatabase, closeAllCliSqliteConnections, openCliSqliteConnection, withCliSqliteSync, withCliSqlite, ensureCliSqliteInitialized;
23063
+ function closeAllCliSqliteConnections() {
23064
+ legacyImportDoneForPath = null;
23065
+ cliSqlite.closeAllConnections();
23066
+ }
23067
+ var legacyImportDoneForPath, cliSqlite, safeCloseCliSqliteDatabase, openCliSqliteConnection, withCliSqliteSync, withCliSqlite, ensureCliSqliteInitialized;
23064
23068
  var init_cli_database = __esm({
23065
23069
  "src/sqlite/cli-database.ts"() {
23066
23070
  "use strict";
@@ -23070,16 +23074,19 @@ var init_cli_database = __esm({
23070
23074
  init_import_cli_legacy_disk_data();
23071
23075
  init_migrate_cli_sqlite();
23072
23076
  init_sqlite_errors();
23077
+ legacyImportDoneForPath = null;
23073
23078
  cliSqlite = createSqliteDatabaseAccess({
23074
23079
  getPath: getCliSqlitePath,
23075
23080
  ensureParentDir: ensureCliSqliteParentDir,
23076
23081
  migrate: migrateCliSqlite,
23077
23082
  afterOpen: (db, options) => {
23083
+ const sqlitePath = getCliSqlitePath();
23084
+ if (legacyImportDoneForPath === sqlitePath) return;
23078
23085
  importCliSqliteLegacyDiskData(db, options?.logLegacyMigration);
23086
+ legacyImportDoneForPath = sqlitePath;
23079
23087
  }
23080
23088
  });
23081
23089
  safeCloseCliSqliteDatabase = cliSqlite.safeClose;
23082
- closeAllCliSqliteConnections = cliSqlite.closeAllConnections;
23083
23090
  openCliSqliteConnection = cliSqlite.openConnection;
23084
23091
  withCliSqliteSync = cliSqlite.withSync;
23085
23092
  withCliSqlite = cliSqlite.withAsync;
@@ -28212,9 +28219,9 @@ function createWsBridge(options) {
28212
28219
  return ws;
28213
28220
  }
28214
28221
  function sendWsMessage(ws, payload) {
28215
- if (ws.readyState === wrapper_default.OPEN) {
28216
- ws.send(JSON.stringify(payload));
28217
- }
28222
+ if (ws.readyState !== wrapper_default.OPEN) return false;
28223
+ ws.send(JSON.stringify(payload));
28224
+ return true;
28218
28225
  }
28219
28226
 
28220
28227
  // src/files/cwd/index.ts
@@ -29345,6 +29352,31 @@ function getAgentModelFromAgentConfig(config2) {
29345
29352
  var HOUR_MS = 36e5;
29346
29353
  var DAY_MS = 24 * HOUR_MS;
29347
29354
 
29355
+ // ../types/src/hosts/skus.ts
29356
+ function defineHostSku(params) {
29357
+ return structuredClone(params);
29358
+ }
29359
+ var FLY_LEGACY_HOST_SKU = defineHostSku({
29360
+ id: "fly-shared-1x512-1gb",
29361
+ provider: "fly",
29362
+ compute: { cpus: 1, cpuKind: "shared" },
29363
+ memory: { memoryMb: 512 },
29364
+ storage: { storageGb: 1 },
29365
+ monthlyPriceCents: 750
29366
+ });
29367
+ var FLY_DEFAULT_HOST_SKU = defineHostSku({
29368
+ id: "fly-shared-1x1024-1gb",
29369
+ provider: "fly",
29370
+ compute: { cpus: 1, cpuKind: "shared" },
29371
+ memory: { memoryMb: 1024 },
29372
+ storage: { storageGb: 1 },
29373
+ monthlyPriceCents: 1e3
29374
+ });
29375
+ var HOST_SKUS_BY_ID = {
29376
+ [FLY_LEGACY_HOST_SKU.id]: FLY_LEGACY_HOST_SKU,
29377
+ [FLY_DEFAULT_HOST_SKU.id]: FLY_DEFAULT_HOST_SKU
29378
+ };
29379
+
29348
29380
  // ../types/src/bridges/agents/types.ts
29349
29381
  var INSTALLABLE_BRIDGE_AGENTS = [
29350
29382
  { value: "claude-code", label: "Anthropic Claude Code", tokenLabel: "Anthropic API key", tokenEnvVar: "ANTHROPIC_API_KEY" },
@@ -29458,11 +29490,11 @@ function createSdkStdioExtNotificationHandler(options) {
29458
29490
 
29459
29491
  // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
29460
29492
  function awaitSdkStdioPermissionRequestHandshake(params) {
29461
- const { requestId, paramsRecord, pending, onRequest } = params;
29493
+ const { requestId, paramsRecord, pending: pending2, onRequest } = params;
29462
29494
  return new Promise((resolve35) => {
29463
- pending.set(requestId, { resolve: resolve35, params: paramsRecord });
29495
+ pending2.set(requestId, { resolve: resolve35, params: paramsRecord });
29464
29496
  if (onRequest == null) {
29465
- pending.delete(requestId);
29497
+ pending2.delete(requestId);
29466
29498
  resolve35({ outcome: { outcome: "denied" } });
29467
29499
  return;
29468
29500
  }
@@ -29478,9 +29510,9 @@ function awaitSdkStdioPermissionRequestHandshake(params) {
29478
29510
  }
29479
29511
 
29480
29512
  // src/agents/acp/clients/sdk/sdk-stdio-permission-pending.ts
29481
- function resolvePendingSdkStdioPermissionCancellations(pending) {
29482
- for (const [id, entry] of [...pending.entries()]) {
29483
- pending.delete(id);
29513
+ function resolvePendingSdkStdioPermissionCancellations(pending2) {
29514
+ for (const [id, entry] of [...pending2.entries()]) {
29515
+ pending2.delete(id);
29484
29516
  entry.resolve({ outcome: { outcome: "cancelled" } });
29485
29517
  }
29486
29518
  }
@@ -30606,7 +30638,7 @@ function installBridgeProcessResilience() {
30606
30638
  }
30607
30639
 
30608
30640
  // src/cli-version.ts
30609
- var CLI_VERSION = "0.1.77".length > 0 ? "0.1.77" : "0.0.0-dev";
30641
+ var CLI_VERSION = "0.1.79".length > 0 ? "0.1.79" : "0.0.0-dev";
30610
30642
 
30611
30643
  // src/connection/heartbeat/constants.ts
30612
30644
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -33213,8 +33245,8 @@ function createCursorAcpSessionContext(options) {
33213
33245
 
33214
33246
  // src/agents/acp/clients/cursor/cancel-pending-cursor-permission-requests.ts
33215
33247
  function cancelPendingCursorPermissionRequests(pendingRequests2, respond) {
33216
- for (const [reqId, pending] of [...pendingRequests2.entries()]) {
33217
- if (pending.method === "session/request_permission") {
33248
+ for (const [reqId, pending2] of [...pendingRequests2.entries()]) {
33249
+ if (pending2.method === "session/request_permission") {
33218
33250
  respond(reqId, { outcome: { outcome: "cancelled" } });
33219
33251
  pendingRequests2.delete(reqId);
33220
33252
  }
@@ -33406,8 +33438,8 @@ function handleCursorIncomingPermissionRequest(id, method, msg, deps) {
33406
33438
 
33407
33439
  // src/agents/acp/clients/cursor/cursor-incoming-resolve-request.ts
33408
33440
  function resolveCursorIncomingRequest(pendingRequests2, respond, requestId, result) {
33409
- const pending = pendingRequests2.get(requestId);
33410
- const payload = pending?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending.params) : result;
33441
+ const pending2 = pendingRequests2.get(requestId);
33442
+ const payload = pending2?.method === "session/request_permission" ? enrichAcpPermissionRpcResultFromRequestParams(result, pending2.params) : result;
33411
33443
  respond(requestId, payload);
33412
33444
  pendingRequests2.delete(requestId);
33413
33445
  }
@@ -33545,25 +33577,25 @@ function createCursorJsonRpcInboundRespond(stdin) {
33545
33577
 
33546
33578
  // src/agents/acp/clients/cursor/cursor-json-rpc-outbound.ts
33547
33579
  function createCursorJsonRpcOutboundPending() {
33548
- const pending = /* @__PURE__ */ new Map();
33580
+ const pending2 = /* @__PURE__ */ new Map();
33549
33581
  let nextId = 1;
33550
33582
  function allocateId() {
33551
33583
  return nextId++;
33552
33584
  }
33553
33585
  function register(id, waiter) {
33554
- pending.set(id, waiter);
33586
+ pending2.set(id, waiter);
33555
33587
  }
33556
33588
  function settleResponse(id, msg) {
33557
- const waiter = pending.get(id);
33589
+ const waiter = pending2.get(id);
33558
33590
  if (!waiter) return false;
33559
- pending.delete(id);
33591
+ pending2.delete(id);
33560
33592
  if (msg.error) waiter.reject(msg.error);
33561
33593
  else waiter.resolve(msg.result);
33562
33594
  return true;
33563
33595
  }
33564
33596
  function rejectOnWriteError(id, err) {
33565
- const waiter = pending.get(id);
33566
- pending.delete(id);
33597
+ const waiter = pending2.get(id);
33598
+ pending2.delete(id);
33567
33599
  waiter?.reject(err);
33568
33600
  }
33569
33601
  return { allocateId, register, settleResponse, rejectOnWriteError };
@@ -33820,13 +33852,15 @@ import { existsSync as existsSync4, statSync } from "node:fs";
33820
33852
  // src/git/get-git-repo-root-sync.ts
33821
33853
  import { execFileSync as execFileSync3 } from "node:child_process";
33822
33854
  import * as path27 from "node:path";
33855
+ var GIT_LOOKUP_TIMEOUT_MS = 2e3;
33823
33856
  function getGitRepoRootSync(startDir) {
33824
33857
  try {
33825
33858
  const out = execFileSync3("git", ["rev-parse", "--show-toplevel"], {
33826
33859
  cwd: path27.resolve(startDir),
33827
33860
  encoding: "utf8",
33828
33861
  stdio: ["ignore", "pipe", "ignore"],
33829
- maxBuffer: 1024 * 1024
33862
+ maxBuffer: 1024 * 1024,
33863
+ timeout: GIT_LOOKUP_TIMEOUT_MS
33830
33864
  }).trim();
33831
33865
  return out ? path27.resolve(out) : null;
33832
33866
  } catch {
@@ -33838,6 +33872,7 @@ function getGitRepoRootSync(startDir) {
33838
33872
  import { execFileSync as execFileSync4 } from "node:child_process";
33839
33873
  import { readFileSync as readFileSync4 } from "node:fs";
33840
33874
  import * as path28 from "node:path";
33875
+ var GIT_FILE_READ_TIMEOUT_MS = 2e3;
33841
33876
  function resolveWorkspaceFilePath(sessionParentPath, rawPath) {
33842
33877
  const trimmed2 = rawPath.trim();
33843
33878
  if (!trimmed2) return null;
@@ -33899,7 +33934,8 @@ function readGitHeadBlob(sessionParentPath, displayPath) {
33899
33934
  return execFileSync4("git", ["show", `HEAD:${displayPath}`], {
33900
33935
  cwd: execCwd,
33901
33936
  encoding: "utf8",
33902
- maxBuffer: 50 * 1024 * 1024
33937
+ maxBuffer: 50 * 1024 * 1024,
33938
+ timeout: GIT_FILE_READ_TIMEOUT_MS
33903
33939
  });
33904
33940
  } catch {
33905
33941
  return "";
@@ -33907,6 +33943,7 @@ function readGitHeadBlob(sessionParentPath, displayPath) {
33907
33943
  }
33908
33944
 
33909
33945
  // src/agents/acp/session-file-change-path-kind.ts
33946
+ var GIT_PATH_KIND_TIMEOUT_MS = 2e3;
33910
33947
  function gitHeadPathObjectType(sessionParentPath, displayPath) {
33911
33948
  if (!displayPath || displayPath.includes("..")) return null;
33912
33949
  const gitRoot = getGitRepoRootSync(sessionParentPath);
@@ -33914,7 +33951,8 @@ function gitHeadPathObjectType(sessionParentPath, displayPath) {
33914
33951
  try {
33915
33952
  return execFileSync5("git", ["cat-file", "-t", `HEAD:${displayPath}`], {
33916
33953
  cwd: gitRoot,
33917
- encoding: "utf8"
33954
+ encoding: "utf8",
33955
+ timeout: GIT_PATH_KIND_TIMEOUT_MS
33918
33956
  }).trim();
33919
33957
  } catch {
33920
33958
  return null;
@@ -35022,23 +35060,30 @@ function augmentPromptResultAuthFields(agentType, errorText) {
35022
35060
  }
35023
35061
 
35024
35062
  // src/agents/planning/submit-planning-todos-for-turn.ts
35063
+ var PLANNING_TODO_SUBMIT_TIMEOUT_MS = 3e3;
35025
35064
  async function submitPlanningTodosForTurn(params) {
35026
35065
  const token = params.getCloudAccessToken();
35027
35066
  if (!token.trim()) return { ok: false, error: "Missing cloud access token" };
35028
35067
  const base = params.cloudApiBaseUrl.replace(/\/$/, "");
35029
35068
  const url2 = `${base}/internal/sessions/todos/submit`;
35030
- const res = await fetch(url2, {
35031
- method: "POST",
35032
- headers: {
35033
- Authorization: `Bearer ${token}`,
35034
- "Content-Type": "application/json"
35035
- },
35036
- body: JSON.stringify({
35037
- sessionId: params.sessionId,
35038
- turnId: params.turnId,
35039
- items: params.items
35040
- })
35041
- });
35069
+ let res;
35070
+ try {
35071
+ res = await fetch(url2, {
35072
+ method: "POST",
35073
+ signal: AbortSignal.timeout(PLANNING_TODO_SUBMIT_TIMEOUT_MS),
35074
+ headers: {
35075
+ Authorization: `Bearer ${token}`,
35076
+ "Content-Type": "application/json"
35077
+ },
35078
+ body: JSON.stringify({
35079
+ sessionId: params.sessionId,
35080
+ turnId: params.turnId,
35081
+ items: params.items
35082
+ })
35083
+ });
35084
+ } catch (error40) {
35085
+ return { ok: false, error: error40 instanceof Error ? error40.message : String(error40) };
35086
+ }
35042
35087
  if (!res.ok) {
35043
35088
  const body = await res.json().catch(() => null);
35044
35089
  return { ok: false, error: body?.error ?? `Planning todo submit failed (${res.status})` };
@@ -36017,6 +36062,7 @@ async function resolveSendPromptImages(params) {
36017
36062
  }
36018
36063
 
36019
36064
  // src/agents/acp/prompts/send-prompt-to-agent.ts
36065
+ var SLOW_ACP_PROMPT_MS = 5e3;
36020
36066
  async function sendPromptToAgent(options) {
36021
36067
  const {
36022
36068
  handle,
@@ -36063,7 +36109,12 @@ async function sendPromptToAgent(options) {
36063
36109
  sendResult(imagesResolved.errorResult);
36064
36110
  return;
36065
36111
  }
36112
+ const promptStartedAt = Date.now();
36066
36113
  const result = await handle.sendPrompt(prepared.agentPromptText, imagesResolved.sendOpts);
36114
+ const promptDurationMs = Date.now() - promptStartedAt;
36115
+ if (promptDurationMs >= SLOW_ACP_PROMPT_MS) {
36116
+ log2(`[Agent] ACP session/prompt completed after ${promptDurationMs}ms.`);
36117
+ }
36067
36118
  await finalizeAndSendPromptResult({
36068
36119
  result,
36069
36120
  sessionId,
@@ -38432,11 +38483,11 @@ function tryConsumeBinaryProxyBody(raw, deps) {
38432
38483
  if (!PROXY_ID_RE.test(id)) return false;
38433
38484
  const body = raw.slice(PROXY_ID_BYTES);
38434
38485
  const bodyBytes = body.length > 0 ? new Uint8Array(body) : new Uint8Array(0);
38435
- const pending = deps.pendingProxyBody.get(id);
38436
- if (pending) {
38486
+ const pending2 = deps.pendingProxyBody.get(id);
38487
+ if (pending2) {
38437
38488
  deps.pendingProxyBody.delete(id);
38438
38489
  deps.startStreamingProxy({
38439
- ...pending.pr,
38490
+ ...pending2.pr,
38440
38491
  body: bodyBytes.length > 0 ? bodyBytes : void 0
38441
38492
  });
38442
38493
  return true;
@@ -38829,9 +38880,9 @@ function processHeartbeatAck(params) {
38829
38880
  if (awaitingSeq === null) return { awaitingSeq, missed: 0 };
38830
38881
  if (!Number.isFinite(seq)) return { awaitingSeq, missed: 0 };
38831
38882
  const ack = Math.trunc(seq);
38832
- const pending = awaitingSeq;
38833
- if (ack < pending) return { awaitingSeq, missed: 0 };
38834
- if (ack === pending) {
38883
+ const pending2 = awaitingSeq;
38884
+ if (ack < pending2) return { awaitingSeq, missed: 0 };
38885
+ if (ack === pending2) {
38835
38886
  recordRtt(Date.now() - sentAtMs);
38836
38887
  }
38837
38888
  return { awaitingSeq: null, missed: 0 };
@@ -46479,9 +46530,13 @@ import * as fs49 from "node:fs";
46479
46530
  import * as path70 from "node:path";
46480
46531
 
46481
46532
  // src/git/snapshot/git.ts
46533
+ var SNAPSHOT_GIT_TIMEOUT_MS = 5e3;
46482
46534
  async function gitStashCreate(repoRoot, log2) {
46483
46535
  try {
46484
- const { stdout } = await execGitFile(["stash", "create"], { cwd: repoRoot });
46536
+ const { stdout } = await execGitFile(["stash", "create"], {
46537
+ cwd: repoRoot,
46538
+ timeout: SNAPSHOT_GIT_TIMEOUT_MS
46539
+ });
46485
46540
  return stdout.trim();
46486
46541
  } catch (e) {
46487
46542
  log2(
@@ -46492,7 +46547,10 @@ async function gitStashCreate(repoRoot, log2) {
46492
46547
  }
46493
46548
  async function gitUntrackedPaths(repoRoot, log2) {
46494
46549
  try {
46495
- const { stdout } = await execGitFile(["ls-files", "--others", "--exclude-standard"], { cwd: repoRoot });
46550
+ const { stdout } = await execGitFile(["ls-files", "--others", "--exclude-standard"], {
46551
+ cwd: repoRoot,
46552
+ timeout: SNAPSHOT_GIT_TIMEOUT_MS
46553
+ });
46496
46554
  return stdout.split("\n").map((l) => l.trim()).filter(Boolean);
46497
46555
  } catch (e) {
46498
46556
  log2(
@@ -49177,28 +49235,78 @@ async function startPromptQueueRuns(entries, deps) {
49177
49235
  }
49178
49236
 
49179
49237
  // src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
49238
+ var SLOW_QUEUE_SYNC_MS = 2e3;
49180
49239
  async function applyPromptQueueStateFromServer(msg, deps) {
49181
49240
  const raw = msg.queues;
49182
49241
  if (!raw || typeof raw !== "object") return;
49242
+ const startedAt = Date.now();
49183
49243
  const entries = promptQueueSnapshotEntries(raw);
49184
49244
  await persistPromptQueueSnapshot(entries);
49185
49245
  await handlePromptQueueCancellations(entries, deps);
49186
49246
  const started = await startPromptQueueRuns(entries, deps);
49187
49247
  await dispatchStartedPromptQueueRuns(entries, started, deps);
49248
+ const durationMs = Date.now() - startedAt;
49249
+ if (durationMs >= SLOW_QUEUE_SYNC_MS) {
49250
+ deps.log(`[Queue] Queue state sync and dispatch took ${durationMs}ms.`);
49251
+ }
49252
+ }
49253
+
49254
+ // src/agents/acp/from-bridge/pending-prompt-results.ts
49255
+ var pending = [];
49256
+ function enqueuePendingPromptResult(message) {
49257
+ const runId = typeof message.runId === "string" ? message.runId : "";
49258
+ if (runId) {
49259
+ const idx = pending.findIndex((m) => m.runId === runId);
49260
+ if (idx >= 0) pending.splice(idx, 1);
49261
+ }
49262
+ pending.push(message);
49263
+ }
49264
+ function flushPendingPromptResults(getWs, encrypt) {
49265
+ if (pending.length === 0) return;
49266
+ const ws = getWs();
49267
+ if (!ws || ws.readyState !== wrapper_default.OPEN) return;
49268
+ const still = [];
49269
+ for (const message of pending) {
49270
+ const wire = encrypt ? encrypt(message) : message;
49271
+ if (!sendWsMessage(ws, wire)) still.push(message);
49272
+ }
49273
+ pending.length = 0;
49274
+ pending.push(...still);
49275
+ }
49276
+
49277
+ // src/agents/acp/from-bridge/send-session-update-wire.ts
49278
+ function sendSessionUpdateWire(getWs, payload, opts) {
49279
+ const s = getWs();
49280
+ if (!s || s.readyState !== wrapper_default.OPEN) {
49281
+ opts.log("[Bridge service] Session update not sent: not connected to the bridge.");
49282
+ return;
49283
+ }
49284
+ const p = payload;
49285
+ const wire = p.type === "session_update" && opts.e2ee ? opts.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && opts.e2ee ? opts.e2ee.encryptFields(payload, [
49286
+ "path",
49287
+ "oldText",
49288
+ "newText",
49289
+ "patchContent",
49290
+ "isDirectory",
49291
+ "directoryRemoved"
49292
+ ]) : payload;
49293
+ sendWsMessage(s, wire);
49188
49294
  }
49189
49295
 
49190
49296
  // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
49191
49297
  function createBridgePromptSenders(deps, getWs) {
49192
49298
  const sendBridgeMessage = (message, encryptedFields = []) => {
49193
49299
  const s = getWs();
49194
- if (!s) return false;
49300
+ if (!s || s.readyState !== wrapper_default.OPEN) return false;
49195
49301
  const wire = deps.e2ee && encryptedFields.length > 0 ? deps.e2ee.encryptFields(message, encryptedFields) : message;
49196
- sendWsMessage(s, wire);
49197
- return true;
49302
+ return sendWsMessage(s, wire);
49198
49303
  };
49199
49304
  const sendResult = (result) => {
49200
- const encryptedFields = result.type === "prompt_result" ? ["output", "error"] : [];
49201
- sendBridgeMessage(result, encryptedFields);
49305
+ const sent = sendBridgeMessage(result, result.type === "prompt_result" ? ["output", "error"] : []);
49306
+ if (!sent && result.type === "prompt_result") {
49307
+ enqueuePendingPromptResult(result);
49308
+ deps.log("[Bridge service] prompt_result deferred until bridge reconnect.");
49309
+ }
49202
49310
  if (result.type === "prompt_result") {
49203
49311
  const pr = result;
49204
49312
  const cancelled = pr.stopReason === "cancelled";
@@ -49211,24 +49319,11 @@ function createBridgePromptSenders(deps, getWs) {
49211
49319
  });
49212
49320
  }
49213
49321
  };
49214
- const sendSessionUpdate = (payload) => {
49215
- const s = getWs();
49216
- if (!s) {
49217
- deps.log("[Bridge service] Session update not sent: not connected to the bridge.");
49218
- return;
49219
- }
49220
- const p = payload;
49221
- const wire = p.type === "session_update" && deps.e2ee ? deps.e2ee.encryptFields(payload, ["payload"]) : p.type === "session_file_change" && deps.e2ee ? deps.e2ee.encryptFields(payload, [
49222
- "path",
49223
- "oldText",
49224
- "newText",
49225
- "patchContent",
49226
- "isDirectory",
49227
- "directoryRemoved"
49228
- ]) : payload;
49229
- sendWsMessage(s, wire);
49322
+ return {
49323
+ sendBridgeMessage,
49324
+ sendResult,
49325
+ sendSessionUpdate: (payload) => sendSessionUpdateWire(getWs, payload, { e2ee: deps.e2ee, log: deps.log })
49230
49326
  };
49231
- return { sendBridgeMessage, sendResult, sendSessionUpdate };
49232
49327
  }
49233
49328
 
49234
49329
  // src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
@@ -49261,59 +49356,93 @@ function parseWorktreeBaseBranches(msg) {
49261
49356
  return Object.keys(out).length > 0 ? out : void 0;
49262
49357
  }
49263
49358
 
49264
- // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
49359
+ // src/agents/acp/from-bridge/parse-follow-up-prompt-fields.ts
49360
+ function parseFollowUpFieldsFromPromptMessage(msg) {
49361
+ const value = msg.followUpCatalogPromptId;
49362
+ return {
49363
+ followUpCatalogPromptId: typeof value === "string" && value.trim() !== "" ? value.trim() : null
49364
+ };
49365
+ }
49366
+
49367
+ // src/agents/acp/from-bridge/prepare-bridge-prompt-git-state.ts
49265
49368
  import { execFile as execFile8 } from "node:child_process";
49266
49369
  import { promisify as promisify9 } from "node:util";
49370
+
49371
+ // src/agents/acp/from-bridge/git-state-preparation-timeout.ts
49372
+ async function awaitGitStatePreparation(options) {
49373
+ const { label, timeoutMs, work, fallback, log: log2 } = options;
49374
+ let timer;
49375
+ try {
49376
+ return await Promise.race([
49377
+ work,
49378
+ new Promise((resolve35) => {
49379
+ timer = setTimeout(() => {
49380
+ log2(`[Agent] ${label} exceeded ${timeoutMs}ms; continuing without it.`);
49381
+ resolve35(fallback);
49382
+ }, timeoutMs);
49383
+ timer.unref?.();
49384
+ })
49385
+ ]);
49386
+ } finally {
49387
+ if (timer) clearTimeout(timer);
49388
+ }
49389
+ }
49390
+
49391
+ // src/agents/acp/from-bridge/prepare-bridge-prompt-git-state.ts
49267
49392
  var execFileAsync7 = promisify9(execFile8);
49393
+ var GIT_STATE_DISCOVERY_TIMEOUT_MS = 2e3;
49268
49394
  async function readGitBranch(cwd) {
49269
49395
  try {
49270
- const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], { cwd, maxBuffer: 64 * 1024 });
49271
- const b = stdout.trim();
49272
- return b || null;
49396
+ const { stdout } = await execFileAsync7("git", ["branch", "--show-current"], {
49397
+ cwd,
49398
+ maxBuffer: 64 * 1024,
49399
+ timeout: GIT_STATE_DISCOVERY_TIMEOUT_MS
49400
+ });
49401
+ return stdout.trim() || null;
49273
49402
  } catch {
49274
49403
  return null;
49275
49404
  }
49276
49405
  }
49277
- async function runBridgePromptPreamble(params) {
49406
+ async function prepareBridgePromptGitState(params) {
49278
49407
  const { getWs, log: log2, sessionWorktreeManager, sessionId, runId, effectiveCwd } = params;
49279
- const s = getWs();
49280
- const repoCheckoutPaths = await sessionWorktreeManager.ensureRepoCheckoutPathsForSessionAsync(sessionId);
49408
+ const repoCheckoutPaths = await awaitGitStatePreparation({
49409
+ label: "Session worktree discovery",
49410
+ timeoutMs: GIT_STATE_DISCOVERY_TIMEOUT_MS,
49411
+ work: sessionWorktreeManager.ensureRepoCheckoutPathsForSessionAsync(sessionId),
49412
+ fallback: void 0,
49413
+ log: log2
49414
+ });
49281
49415
  const repoRoots = await resolveSnapshotRepoRoots({
49282
49416
  worktreePaths: repoCheckoutPaths,
49283
49417
  fallbackCwd: effectiveCwd,
49284
49418
  sessionId: sessionId?.trim() || void 0,
49285
49419
  log: log2
49286
49420
  });
49287
- if (s && sessionId) {
49288
- const usesWt = sessionWorktreeManager.usesWorktreeSession(sessionId);
49289
- const cliGitBranch = repoRoots.length > 0 ? await readGitBranch(effectiveCwd) : null;
49290
- const isolatedSessionParentPath = sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId);
49291
- sendWsMessage(s, {
49421
+ const socket = getWs();
49422
+ if (socket && sessionId) {
49423
+ const usesWorktree = sessionWorktreeManager.usesWorktreeSession(sessionId);
49424
+ sendWsMessage(socket, {
49292
49425
  type: "session_git_context_report",
49293
49426
  sessionId,
49294
- cliGitBranch,
49295
- agentUsesWorktree: usesWt,
49296
- sessionParent: usesWt ? "worktrees_root" : "bridge_root",
49297
- sessionParentPath: usesWt ? isolatedSessionParentPath ?? effectiveCwd : getBridgeRoot()
49427
+ cliGitBranch: repoRoots.length > 0 ? await readGitBranch(effectiveCwd) : null,
49428
+ agentUsesWorktree: usesWorktree,
49429
+ sessionParent: usesWorktree ? "worktrees_root" : "bridge_root",
49430
+ sessionParentPath: usesWorktree ? sessionWorktreeManager.getIsolatedSessionParentPathForSession(sessionId) ?? effectiveCwd : getBridgeRoot()
49298
49431
  });
49299
49432
  }
49300
- if (s && sessionId && runId) {
49301
- const cap = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false, error: "No git repos" };
49302
- sendWsMessage(s, {
49433
+ if (socket && sessionId && runId) {
49434
+ const snapshot = repoRoots.length > 0 ? await capturePreTurnSnapshot({ runId, repoRoots, agentCwd: effectiveCwd, log: log2 }) : { ok: false };
49435
+ sendWsMessage(socket, {
49303
49436
  type: "pre_turn_snapshot_report",
49304
49437
  sessionId,
49305
49438
  turnId: runId,
49306
- captured: cap.ok
49439
+ captured: snapshot.ok
49307
49440
  });
49308
49441
  }
49309
49442
  }
49310
- function parseFollowUpFieldsFromPromptMessage(msg) {
49311
- const followUpCatalogPromptId = typeof msg.followUpCatalogPromptId === "string" && msg.followUpCatalogPromptId.trim() !== "" ? msg.followUpCatalogPromptId.trim() : null;
49312
- return { followUpCatalogPromptId };
49313
- }
49314
49443
 
49315
- // src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
49316
- async function runPreambleAndPrompt(params) {
49444
+ // src/agents/acp/from-bridge/handle-bridge-prompt/prepare-git-state-and-run-prompt.ts
49445
+ async function prepareGitStateAndRunPrompt(params) {
49317
49446
  const {
49318
49447
  deps,
49319
49448
  msg,
@@ -49332,7 +49461,7 @@ async function runPreambleAndPrompt(params) {
49332
49461
  senders: { sendResult, sendSessionUpdate }
49333
49462
  } = params;
49334
49463
  const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
49335
- await runBridgePromptPreamble({
49464
+ await prepareBridgePromptGitState({
49336
49465
  getWs,
49337
49466
  log: log2,
49338
49467
  sessionWorktreeManager,
@@ -49403,7 +49532,7 @@ function handleBridgePrompt(msg, deps) {
49403
49532
  sessionParentPath,
49404
49533
  ...worktreeBaseBranches ? { worktreeBaseBranches } : {}
49405
49534
  }).then(
49406
- (cwd) => runPreambleAndPrompt({
49535
+ (cwd) => prepareGitStateAndRunPrompt({
49407
49536
  deps,
49408
49537
  msg,
49409
49538
  getWs,
@@ -49422,7 +49551,7 @@ function handleBridgePrompt(msg, deps) {
49422
49551
  })
49423
49552
  ).catch((err) => {
49424
49553
  log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
49425
- void runPreambleAndPrompt({
49554
+ void prepareGitStateAndRunPrompt({
49426
49555
  deps,
49427
49556
  msg,
49428
49557
  getWs,
@@ -49904,25 +50033,59 @@ var handleInstallSkillsMessage = (msg, deps) => {
49904
50033
  })();
49905
50034
  };
49906
50035
 
49907
- // src/agents/install/commands/run-npm-global-install.ts
49908
- import { execFile as execFile9 } from "node:child_process";
49909
- import { promisify as promisify10 } from "node:util";
49910
-
49911
- // src/agents/install/format-command-output.ts
49912
- function formatCommandOutput(stdout, stderr) {
49913
- const parts = [String(stdout ?? "").trimEnd(), String(stderr ?? "").trimEnd()].filter(Boolean);
49914
- return parts.join("\n");
50036
+ // src/agents/install/run-streaming-command.ts
50037
+ import { spawn as spawn11 } from "node:child_process";
50038
+ import * as readline3 from "node:readline";
50039
+ function runStreamingCommand(command, args, options) {
50040
+ return new Promise((resolve35, reject) => {
50041
+ const child = spawn11(command, args, {
50042
+ env: options.env,
50043
+ stdio: ["ignore", "pipe", "pipe"]
50044
+ });
50045
+ let settled = false;
50046
+ const timer = options.timeoutMs != null ? setTimeout(() => {
50047
+ child.kill("SIGKILL");
50048
+ if (!settled) {
50049
+ settled = true;
50050
+ reject(new Error(`Command timed out after ${options.timeoutMs}ms`));
50051
+ }
50052
+ }, options.timeoutMs) : null;
50053
+ const onLine = (line) => {
50054
+ if (line.length > 0) options.onLine?.(line);
50055
+ };
50056
+ if (child.stdout) {
50057
+ readline3.createInterface({ input: child.stdout, crlfDelay: Infinity }).on("line", onLine);
50058
+ }
50059
+ if (child.stderr) {
50060
+ readline3.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", onLine);
50061
+ }
50062
+ child.on("error", (err) => {
50063
+ if (timer) clearTimeout(timer);
50064
+ if (!settled) {
50065
+ settled = true;
50066
+ reject(err);
50067
+ }
50068
+ });
50069
+ child.on("close", (code, signal) => {
50070
+ if (timer) clearTimeout(timer);
50071
+ if (!settled) {
50072
+ settled = true;
50073
+ resolve35({ code, signal });
50074
+ }
50075
+ });
50076
+ });
49915
50077
  }
49916
50078
 
49917
50079
  // src/agents/install/commands/run-npm-global-install.ts
49918
- var execFileAsync8 = promisify10(execFile9);
49919
- async function runNpmGlobalInstall(packageName, env, timeoutMs = 3e5) {
49920
- const { stdout, stderr } = await execFileAsync8("npm", ["install", "-g", packageName], {
49921
- timeout: timeoutMs,
50080
+ async function runNpmGlobalInstall(packageName, env, options) {
50081
+ const result = await runStreamingCommand("npm", ["install", "-g", packageName], {
49922
50082
  env: bridgeAgentPathEnv(env),
49923
- maxBuffer: 10 * 1024 * 1024
50083
+ timeoutMs: options?.timeoutMs ?? 3e5,
50084
+ onLine: options?.onLine
49924
50085
  });
49925
- return formatCommandOutput(stdout, stderr);
50086
+ if (result.code !== 0) {
50087
+ throw new Error(`npm install -g ${packageName} failed (exit ${result.code ?? "signal"})`);
50088
+ }
49926
50089
  }
49927
50090
 
49928
50091
  // src/agents/install/commands/claude-code.ts
@@ -49930,11 +50093,12 @@ var claudeCodeInstallCommand = {
49930
50093
  agentType: "claude-code",
49931
50094
  detectCommand: "claude",
49932
50095
  async install(ctx) {
49933
- const logOutput = await runNpmGlobalInstall("@anthropic-ai/claude-code", {
49934
- ...ctx.env,
49935
- ANTHROPIC_API_KEY: ctx.authToken
49936
- });
49937
- ctx.onProgress?.("Installing Anthropic Claude Code", logOutput || void 0);
50096
+ ctx.onProgress?.("Installing Anthropic Claude Code");
50097
+ await runNpmGlobalInstall(
50098
+ "@anthropic-ai/claude-code",
50099
+ { ...ctx.env, ANTHROPIC_API_KEY: ctx.authToken },
50100
+ { onLine: (line) => ctx.onProgress?.("Installing Anthropic Claude Code", line) }
50101
+ );
49938
50102
  }
49939
50103
  };
49940
50104
 
@@ -49943,34 +50107,34 @@ var codexAcpInstallCommand = {
49943
50107
  agentType: "codex-acp",
49944
50108
  detectCommand: "codex",
49945
50109
  async install(ctx) {
49946
- const logOutput = await runNpmGlobalInstall("@openai/codex", {
49947
- ...ctx.env,
49948
- OPENAI_API_KEY: ctx.authToken
49949
- });
49950
- ctx.onProgress?.("Installing Codex", logOutput || void 0);
50110
+ ctx.onProgress?.("Installing Codex");
50111
+ await runNpmGlobalInstall(
50112
+ "@openai/codex",
50113
+ { ...ctx.env, OPENAI_API_KEY: ctx.authToken },
50114
+ { onLine: (line) => ctx.onProgress?.("Installing Codex", line) }
50115
+ );
49951
50116
  }
49952
50117
  };
49953
50118
 
49954
50119
  // src/agents/install/commands/cursor-cli.ts
49955
- import { execFile as execFile10 } from "node:child_process";
49956
- import { promisify as promisify11 } from "node:util";
49957
- var execFileAsync9 = promisify11(execFile10);
49958
50120
  var cursorCliInstallCommand = {
49959
50121
  agentType: "cursor-cli",
49960
50122
  detectCommand: "agent",
49961
50123
  alternateDetectCommands: ["cursor-agent"],
49962
50124
  async install(ctx) {
49963
- const { stdout, stderr } = await execFileAsync9(
50125
+ ctx.onProgress?.("Installing Cursor CLI");
50126
+ const result = await runStreamingCommand(
49964
50127
  "bash",
49965
50128
  ["-lc", "curl -fsSL https://cursor.com/install | bash"],
49966
50129
  {
49967
- timeout: 3e5,
50130
+ timeoutMs: 3e5,
49968
50131
  env: { ...bridgeAgentPathEnv(ctx.env), CURSOR_API_KEY: ctx.authToken },
49969
- maxBuffer: 10 * 1024 * 1024
50132
+ onLine: (line) => ctx.onProgress?.("Installing Cursor CLI", line)
49970
50133
  }
49971
50134
  );
49972
- const logOutput = formatCommandOutput(stdout, stderr);
49973
- ctx.onProgress?.("Installing Cursor CLI", logOutput || void 0);
50135
+ if (result.code !== 0) {
50136
+ throw new Error(`Cursor CLI install failed (exit ${result.code ?? "signal"})`);
50137
+ }
49974
50138
  }
49975
50139
  };
49976
50140
 
@@ -49979,11 +50143,12 @@ var opencodeInstallCommand = {
49979
50143
  agentType: "opencode",
49980
50144
  detectCommand: "opencode",
49981
50145
  async install(ctx) {
49982
- const logOutput = await runNpmGlobalInstall("opencode-ai", {
49983
- ...ctx.env,
49984
- OPENCODE_API_KEY: ctx.authToken
49985
- });
49986
- ctx.onProgress?.("Installing OpenCode", logOutput || void 0);
50146
+ ctx.onProgress?.("Installing OpenCode");
50147
+ await runNpmGlobalInstall(
50148
+ "opencode-ai",
50149
+ { ...ctx.env, OPENCODE_API_KEY: ctx.authToken },
50150
+ { onLine: (line) => ctx.onProgress?.("Installing OpenCode", line) }
50151
+ );
49987
50152
  }
49988
50153
  };
49989
50154
 
@@ -50044,25 +50209,47 @@ var handleInstallAgentMessage = (msg, deps) => {
50044
50209
  if (!processId || !agentId || !tokenId || !agentType || !authToken) return;
50045
50210
  void (async () => {
50046
50211
  const socket = deps.getWs();
50047
- const report = (step, message, logOutput) => {
50048
- if (socket) {
50049
- sendWsMessage(socket, {
50050
- type: "install_agent_progress",
50051
- processId,
50052
- agentId,
50053
- tokenId,
50054
- agentType,
50055
- step,
50056
- message,
50057
- ...logOutput ? { logOutput } : {}
50058
- });
50212
+ let pendingLines = [];
50213
+ let flushTimer = null;
50214
+ const sendProgress = (message, logOutput) => {
50215
+ if (!socket) return;
50216
+ sendWsMessage(socket, {
50217
+ type: "install_agent_progress",
50218
+ processId,
50219
+ agentId,
50220
+ tokenId,
50221
+ agentType,
50222
+ step: "agent_install_package",
50223
+ message,
50224
+ ...logOutput ? { logOutput } : {}
50225
+ });
50226
+ };
50227
+ const flushLines = (message) => {
50228
+ if (flushTimer) {
50229
+ clearTimeout(flushTimer);
50230
+ flushTimer = null;
50059
50231
  }
50232
+ if (pendingLines.length === 0) return;
50233
+ const logOutput = pendingLines.join("\n");
50234
+ pendingLines = [];
50235
+ sendProgress(message, logOutput);
50060
50236
  };
50061
50237
  const result = await installLocalAgentOnBridge({
50062
50238
  agentType,
50063
50239
  authToken,
50064
- onProgress: (message, logOutput) => report("agent_install_package", message, logOutput)
50240
+ onProgress: (message, logOutput) => {
50241
+ if (logOutput == null) {
50242
+ flushLines(message);
50243
+ sendProgress(message);
50244
+ return;
50245
+ }
50246
+ pendingLines.push(logOutput);
50247
+ if (!flushTimer) {
50248
+ flushTimer = setTimeout(() => flushLines(message), 100);
50249
+ }
50250
+ }
50065
50251
  });
50252
+ flushLines("Installing agent packages on the bridge machine.");
50066
50253
  if (socket) {
50067
50254
  sendWsMessage(socket, {
50068
50255
  type: "install_agent_result",
@@ -50074,9 +50261,7 @@ var handleInstallAgentMessage = (msg, deps) => {
50074
50261
  error: result.error
50075
50262
  });
50076
50263
  }
50077
- if (result.success) {
50078
- await deps.reportAutoDetectedAgents?.();
50079
- }
50264
+ if (result.success) await deps.reportAutoDetectedAgents?.();
50080
50265
  if (!result.success) {
50081
50266
  deps.log(`[Bridge service] Install agent failed: ${result.error ?? "unknown"}`);
50082
50267
  }
@@ -50661,6 +50846,10 @@ function createMainBridgeOpenHandler(params) {
50661
50846
  if (socket) {
50662
50847
  sendBridgeIdentify(socket, { identifyReportedPaths, e2ee });
50663
50848
  reportGitRepos(getWs, logFn);
50849
+ flushPendingPromptResults(
50850
+ getWs,
50851
+ (message) => e2ee ? e2ee.encryptFields(message, ["output", "error"]) : message
50852
+ );
50664
50853
  try {
50665
50854
  onBridgeSocketOpen?.();
50666
50855
  } catch {
@@ -50805,7 +50994,7 @@ async function createBridgeConnection(options) {
50805
50994
  }
50806
50995
 
50807
50996
  // src/e2e-certificates/key-command.ts
50808
- import * as readline3 from "node:readline";
50997
+ import * as readline4 from "node:readline";
50809
50998
  function installE2eCertificateKeyCommand({
50810
50999
  log: log2,
50811
51000
  onOpenCertificate,
@@ -50816,7 +51005,7 @@ function installE2eCertificateKeyCommand({
50816
51005
  return () => {
50817
51006
  };
50818
51007
  }
50819
- readline3.emitKeypressEvents(process.stdin);
51008
+ readline4.emitKeypressEvents(process.stdin);
50820
51009
  process.stdin.setRawMode(true);
50821
51010
  process.stdin.resume();
50822
51011
  const onKeypress = (str, key) => {