openviber 0.4.3 → 0.5.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.js CHANGED
@@ -7347,103 +7347,33 @@ async function runTask(goal, options, messages) {
7347
7347
  }
7348
7348
 
7349
7349
  // src/daemon/terminal.ts
7350
- import { spawn, execSync } from "child_process";
7350
+ import { spawn, spawnSync, execSync } from "child_process";
7351
7351
  import { EventEmitter } from "events";
7352
- function listSessions() {
7353
- try {
7354
- const out = execSync(
7355
- "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
7356
- { encoding: "utf8", stdio: "pipe" }
7357
- ).trim();
7358
- if (!out) return [];
7359
- return out.split("\n").map((line) => {
7360
- const [name, windows, attached] = line.split("|");
7361
- return {
7362
- name,
7363
- windows: parseInt(windows, 10) || 0,
7364
- attached: attached === "1"
7365
- };
7366
- });
7367
- } catch {
7368
- return [];
7369
- }
7370
- }
7371
- function listPanes() {
7372
- try {
7373
- const out = execSync(
7374
- "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
7375
- { encoding: "utf8", stdio: "pipe" }
7376
- ).trim();
7377
- if (!out) return [];
7378
- return out.split("\n").map((line) => {
7379
- const [session, window2, windowName, pane, command] = line.split("|");
7380
- return {
7381
- session,
7382
- window: window2,
7383
- windowName,
7384
- pane,
7385
- command,
7386
- target: `${session}:${window2}.${pane}`
7387
- };
7388
- });
7389
- } catch {
7390
- return [];
7391
- }
7392
- }
7393
- function sendKeys(target, keys, pressEnter = false) {
7394
- try {
7395
- const args = ["send-keys", "-t", target, keys];
7396
- if (pressEnter) args.push("Enter");
7397
- execSync(`tmux ${args.map((a) => `'${a}'`).join(" ")}`, {
7398
- encoding: "utf8",
7399
- stdio: "pipe"
7400
- });
7401
- return true;
7402
- } catch {
7403
- return false;
7404
- }
7352
+ import { randomUUID } from "crypto";
7353
+ var SAFE_NAME_RE = /[^a-zA-Z0-9_.:-]/g;
7354
+ function sanitizeName(input) {
7355
+ return input.replace(SAFE_NAME_RE, "-");
7405
7356
  }
7406
- function capturePane(target, lines = 500) {
7407
- const cmds = [
7408
- `tmux capture-pane -t '${target}' -pae -S -${lines}`,
7409
- `tmux capture-pane -t '${target}' -pe -S -${lines}`
7410
- ];
7411
- for (const cmd of cmds) {
7412
- try {
7413
- return execSync(cmd, { encoding: "utf8", stdio: "pipe" });
7414
- } catch {
7415
- }
7416
- }
7417
- return "";
7418
- }
7419
- function resizePane(target, cols, rows) {
7420
- try {
7421
- execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
7422
- encoding: "utf8",
7423
- stdio: "pipe"
7424
- });
7425
- return true;
7426
- } catch {
7427
- return false;
7357
+ function resolveAppTarget(target, appHint) {
7358
+ if (target.includes("::")) {
7359
+ const [appId, ...rest] = target.split("::");
7360
+ return { appId, rawTarget: rest.join("::") };
7428
7361
  }
7362
+ return { appId: appHint || "tmux", rawTarget: target };
7429
7363
  }
7430
- var TerminalStream = class extends EventEmitter {
7431
- target;
7432
- catProcess = null;
7433
- pipePath;
7434
- isAttached = false;
7364
+ var TmuxTerminalStream = class extends EventEmitter {
7435
7365
  constructor(target) {
7436
7366
  super();
7437
7367
  this.target = target;
7438
7368
  this.pipePath = `/tmp/viber-term-${target.replace(/[^a-zA-Z0-9]/g, "-")}-${Date.now()}`;
7439
7369
  }
7440
- /**
7441
- * Start streaming the pane output
7442
- */
7370
+ catProcess = null;
7371
+ pipePath;
7372
+ isAttached = false;
7443
7373
  async attach() {
7444
7374
  if (this.isAttached) return true;
7445
7375
  try {
7446
- const history = capturePane(this.target, 200);
7376
+ const history = captureTmuxPane(this.target, 200);
7447
7377
  if (history) {
7448
7378
  this.emit("data", history);
7449
7379
  }
@@ -7473,9 +7403,6 @@ var TerminalStream = class extends EventEmitter {
7473
7403
  return false;
7474
7404
  }
7475
7405
  }
7476
- /**
7477
- * Stop streaming
7478
- */
7479
7406
  detach() {
7480
7407
  if (!this.isAttached) return;
7481
7408
  try {
@@ -7484,12 +7411,6 @@ var TerminalStream = class extends EventEmitter {
7484
7411
  }
7485
7412
  this.cleanup();
7486
7413
  }
7487
- /**
7488
- * Send input to the pane
7489
- */
7490
- sendInput(keys) {
7491
- return sendKeys(this.target, keys, false);
7492
- }
7493
7414
  cleanup() {
7494
7415
  this.isAttached = false;
7495
7416
  if (this.catProcess) {
@@ -7506,20 +7427,61 @@ var TerminalStream = class extends EventEmitter {
7506
7427
  return this.isAttached;
7507
7428
  }
7508
7429
  };
7509
- var TerminalManager = class {
7430
+ var TmuxTerminalApp = class {
7431
+ id = "tmux";
7432
+ label = "tmux";
7510
7433
  streams = /* @__PURE__ */ new Map();
7511
- /**
7512
- * List all available terminals
7513
- */
7514
- list() {
7515
- return {
7516
- sessions: listSessions(),
7517
- panes: listPanes()
7518
- };
7434
+ isAvailable() {
7435
+ try {
7436
+ execSync("tmux -V", { stdio: "pipe" });
7437
+ return true;
7438
+ } catch {
7439
+ return false;
7440
+ }
7441
+ }
7442
+ listSessions() {
7443
+ try {
7444
+ const out = execSync(
7445
+ "tmux list-sessions -F '#{session_name}|#{session_windows}|#{session_attached}' 2>/dev/null",
7446
+ { encoding: "utf8", stdio: "pipe" }
7447
+ ).trim();
7448
+ if (!out) return [];
7449
+ return out.split("\n").map((line) => {
7450
+ const [name, windows, attached] = line.split("|");
7451
+ return {
7452
+ appId: this.id,
7453
+ name,
7454
+ windows: parseInt(windows, 10) || 0,
7455
+ attached: attached === "1"
7456
+ };
7457
+ });
7458
+ } catch {
7459
+ return [];
7460
+ }
7461
+ }
7462
+ listPanes() {
7463
+ try {
7464
+ const out = execSync(
7465
+ "tmux list-panes -a -F '#{session_name}|#{window_index}|#{window_name}|#{pane_index}|#{pane_current_command}' 2>/dev/null",
7466
+ { encoding: "utf8", stdio: "pipe" }
7467
+ ).trim();
7468
+ if (!out) return [];
7469
+ return out.split("\n").map((line) => {
7470
+ const [session, window2, windowName, pane, command] = line.split("|");
7471
+ return {
7472
+ appId: this.id,
7473
+ session,
7474
+ window: window2,
7475
+ windowName,
7476
+ pane,
7477
+ command,
7478
+ target: `${session}:${window2}.${pane}`
7479
+ };
7480
+ });
7481
+ } catch {
7482
+ return [];
7483
+ }
7519
7484
  }
7520
- /**
7521
- * Attach to a pane and return the stream
7522
- */
7523
7485
  async attach(target, onData, onClose) {
7524
7486
  let stream = this.streams.get(target);
7525
7487
  if (stream && stream.attached) {
@@ -7527,7 +7489,7 @@ var TerminalManager = class {
7527
7489
  stream.on("close", onClose);
7528
7490
  return true;
7529
7491
  }
7530
- stream = new TerminalStream(target);
7492
+ stream = new TmuxTerminalStream(target);
7531
7493
  stream.on("data", onData);
7532
7494
  stream.on("close", () => {
7533
7495
  this.streams.delete(target);
@@ -7539,31 +7501,62 @@ var TerminalManager = class {
7539
7501
  }
7540
7502
  return ok;
7541
7503
  }
7542
- /**
7543
- * Detach from a pane
7544
- */
7545
7504
  detach(target) {
7546
7505
  const stream = this.streams.get(target);
7547
- if (stream) {
7548
- stream.detach();
7549
- this.streams.delete(target);
7550
- }
7506
+ if (!stream) return;
7507
+ stream.detach();
7508
+ this.streams.delete(target);
7551
7509
  }
7552
- /**
7553
- * Send input to a pane
7554
- */
7555
7510
  sendInput(target, keys) {
7556
- return sendKeys(target, keys, false);
7511
+ return sendTmuxKeys(target, keys);
7557
7512
  }
7558
- /**
7559
- * Resize pane to match web terminal
7560
- */
7561
7513
  resize(target, cols, rows) {
7562
- return resizePane(target, cols, rows);
7514
+ try {
7515
+ execSync(`tmux resize-pane -t '${target}' -x ${cols} -y ${rows}`, {
7516
+ encoding: "utf8",
7517
+ stdio: "pipe"
7518
+ });
7519
+ return true;
7520
+ } catch {
7521
+ return false;
7522
+ }
7523
+ }
7524
+ createSession(sessionName, windowName = "main", cwd) {
7525
+ const safeSession = sanitizeName(sessionName || "coding");
7526
+ const safeWindow = sanitizeName(windowName || "main");
7527
+ try {
7528
+ execSync(`tmux has-session -t '${safeSession}' 2>/dev/null`, { stdio: "pipe" });
7529
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
7530
+ } catch {
7531
+ }
7532
+ const args = ["new-session", "-d", "-s", safeSession, "-n", safeWindow];
7533
+ if (cwd) {
7534
+ args.push("-c", cwd);
7535
+ }
7536
+ const result = spawnSync("tmux", args, {
7537
+ encoding: "utf8",
7538
+ stdio: "pipe"
7539
+ });
7540
+ if (result.error) {
7541
+ return {
7542
+ ok: false,
7543
+ appId: this.id,
7544
+ sessionName: safeSession,
7545
+ created: false,
7546
+ error: `Failed to start tmux: ${result.error.message}`
7547
+ };
7548
+ }
7549
+ if (result.status !== 0) {
7550
+ return {
7551
+ ok: false,
7552
+ appId: this.id,
7553
+ sessionName: safeSession,
7554
+ created: false,
7555
+ error: (result.stderr || result.stdout || "Failed to create tmux session").trim()
7556
+ };
7557
+ }
7558
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
7563
7559
  }
7564
- /**
7565
- * Detach all streams
7566
- */
7567
7560
  detachAll() {
7568
7561
  for (const stream of this.streams.values()) {
7569
7562
  stream.detach();
@@ -7571,6 +7564,203 @@ var TerminalManager = class {
7571
7564
  this.streams.clear();
7572
7565
  }
7573
7566
  };
7567
+ var ShellTerminalApp = class {
7568
+ id = "shell";
7569
+ label = "shell";
7570
+ sessions = /* @__PURE__ */ new Map();
7571
+ isAvailable() {
7572
+ return true;
7573
+ }
7574
+ listSessions() {
7575
+ return Array.from(this.sessions.values()).map((state) => ({
7576
+ appId: this.id,
7577
+ name: state.sessionName,
7578
+ windows: 1,
7579
+ attached: state.listeners.size > 0
7580
+ }));
7581
+ }
7582
+ listPanes() {
7583
+ return Array.from(this.sessions.entries()).map(([id, state]) => ({
7584
+ appId: this.id,
7585
+ session: state.sessionName,
7586
+ window: "0",
7587
+ windowName: state.windowName,
7588
+ pane: state.pane,
7589
+ command: process.env.SHELL || "sh",
7590
+ target: id
7591
+ }));
7592
+ }
7593
+ async attach(target, onData, onClose) {
7594
+ const state = this.sessions.get(target);
7595
+ if (!state) return false;
7596
+ state.listeners.add(onData);
7597
+ state.closeListeners.add(onClose);
7598
+ if (state.history.length > 0) {
7599
+ onData(state.history.join(""));
7600
+ }
7601
+ return true;
7602
+ }
7603
+ detach(target) {
7604
+ const state = this.sessions.get(target);
7605
+ if (!state) return;
7606
+ state.listeners.clear();
7607
+ state.closeListeners.clear();
7608
+ }
7609
+ sendInput(target, keys) {
7610
+ const state = this.sessions.get(target);
7611
+ if (!state || !state.proc.stdin?.writable) return false;
7612
+ state.proc.stdin.write(keys);
7613
+ return true;
7614
+ }
7615
+ resize(_target, _cols, _rows) {
7616
+ return true;
7617
+ }
7618
+ createSession(sessionName, windowName = "main", cwd) {
7619
+ const safeSession = sanitizeName(sessionName || `shell-${Date.now()}`);
7620
+ const shell = process.env.SHELL || "sh";
7621
+ const target = `${safeSession}:${randomUUID().slice(0, 8)}`;
7622
+ if (this.sessions.has(target)) {
7623
+ return { ok: true, appId: this.id, sessionName: safeSession, created: false };
7624
+ }
7625
+ const proc = spawn(shell, [], {
7626
+ cwd,
7627
+ stdio: ["pipe", "pipe", "pipe"],
7628
+ env: process.env
7629
+ });
7630
+ if (!proc.pid) {
7631
+ return {
7632
+ ok: false,
7633
+ appId: this.id,
7634
+ sessionName: safeSession,
7635
+ created: false,
7636
+ error: "Failed to start shell process"
7637
+ };
7638
+ }
7639
+ const state = {
7640
+ proc,
7641
+ sessionName: safeSession,
7642
+ windowName,
7643
+ pane: "0",
7644
+ history: [],
7645
+ listeners: /* @__PURE__ */ new Set(),
7646
+ closeListeners: /* @__PURE__ */ new Set()
7647
+ };
7648
+ const pushChunk = (chunk) => {
7649
+ const text = chunk.toString();
7650
+ state.history.push(text);
7651
+ if (state.history.length > 200) {
7652
+ state.history.shift();
7653
+ }
7654
+ for (const listener of state.listeners) {
7655
+ listener(text);
7656
+ }
7657
+ };
7658
+ proc.stdout?.on("data", pushChunk);
7659
+ proc.stderr?.on("data", pushChunk);
7660
+ proc.on("close", () => {
7661
+ for (const onClose of state.closeListeners) {
7662
+ onClose();
7663
+ }
7664
+ this.sessions.delete(target);
7665
+ });
7666
+ this.sessions.set(target, state);
7667
+ return { ok: true, appId: this.id, sessionName: safeSession, created: true };
7668
+ }
7669
+ detachAll() {
7670
+ for (const [id, state] of this.sessions.entries()) {
7671
+ state.proc.kill();
7672
+ this.sessions.delete(id);
7673
+ }
7674
+ }
7675
+ };
7676
+ function sendTmuxKeys(target, keys, pressEnter = false) {
7677
+ try {
7678
+ const args = ["send-keys", "-t", target, keys];
7679
+ if (pressEnter) args.push("Enter");
7680
+ execSync(`tmux ${args.map((a) => `'${a}'`).join(" ")}`, {
7681
+ encoding: "utf8",
7682
+ stdio: "pipe"
7683
+ });
7684
+ return true;
7685
+ } catch {
7686
+ return false;
7687
+ }
7688
+ }
7689
+ function captureTmuxPane(target, lines = 500) {
7690
+ const cmds = [
7691
+ `tmux capture-pane -t '${target}' -pae -S -${lines}`,
7692
+ `tmux capture-pane -t '${target}' -pe -S -${lines}`
7693
+ ];
7694
+ for (const cmd of cmds) {
7695
+ try {
7696
+ return execSync(cmd, { encoding: "utf8", stdio: "pipe" });
7697
+ } catch {
7698
+ }
7699
+ }
7700
+ return "";
7701
+ }
7702
+ var TerminalManager = class {
7703
+ apps = /* @__PURE__ */ new Map();
7704
+ constructor(adapters) {
7705
+ const defaultAdapters = adapters ?? [new TmuxTerminalApp(), new ShellTerminalApp()];
7706
+ for (const adapter of defaultAdapters) {
7707
+ this.apps.set(adapter.id, adapter);
7708
+ }
7709
+ }
7710
+ list() {
7711
+ const metadata = Array.from(this.apps.values()).map((app) => ({
7712
+ id: app.id,
7713
+ label: app.label,
7714
+ available: app.isAvailable()
7715
+ }));
7716
+ const sessions = [];
7717
+ const panes = [];
7718
+ for (const app of this.apps.values()) {
7719
+ if (!app.isAvailable()) continue;
7720
+ sessions.push(...app.listSessions());
7721
+ panes.push(...app.listPanes());
7722
+ }
7723
+ return { apps: metadata, sessions, panes };
7724
+ }
7725
+ async attach(target, onData, onClose, appHint) {
7726
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7727
+ const app = this.apps.get(appId);
7728
+ if (!app || !app.isAvailable()) return false;
7729
+ return app.attach(rawTarget, onData, onClose);
7730
+ }
7731
+ detach(target, appHint) {
7732
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7733
+ this.apps.get(appId)?.detach(rawTarget);
7734
+ }
7735
+ sendInput(target, keys, appHint) {
7736
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7737
+ const app = this.apps.get(appId);
7738
+ return !!app && app.isAvailable() ? app.sendInput(rawTarget, keys) : false;
7739
+ }
7740
+ resize(target, cols, rows, appHint) {
7741
+ const { appId, rawTarget } = resolveAppTarget(target, appHint);
7742
+ const app = this.apps.get(appId);
7743
+ return !!app && app.isAvailable() ? app.resize(rawTarget, cols, rows) : false;
7744
+ }
7745
+ createSession(sessionName, windowName = "main", cwd, appId = "tmux") {
7746
+ const app = this.apps.get(appId);
7747
+ if (!app || !app.isAvailable()) {
7748
+ return {
7749
+ ok: false,
7750
+ appId,
7751
+ sessionName,
7752
+ created: false,
7753
+ error: `Terminal app '${appId}' is not available`
7754
+ };
7755
+ }
7756
+ return app.createSession(sessionName, windowName, cwd);
7757
+ }
7758
+ detachAll() {
7759
+ for (const app of this.apps.values()) {
7760
+ app.detachAll();
7761
+ }
7762
+ }
7763
+ };
7574
7764
 
7575
7765
  // src/daemon/controller.ts
7576
7766
  var ViberController = class extends EventEmitter2 {
@@ -7721,16 +7911,16 @@ var ViberController = class extends EventEmitter2 {
7721
7911
  this.handleTerminalList();
7722
7912
  break;
7723
7913
  case "terminal:attach":
7724
- await this.handleTerminalAttach(message.target);
7914
+ await this.handleTerminalAttach(message.target, message.appId);
7725
7915
  break;
7726
7916
  case "terminal:detach":
7727
- this.handleTerminalDetach(message.target);
7917
+ this.handleTerminalDetach(message.target, message.appId);
7728
7918
  break;
7729
7919
  case "terminal:input":
7730
- this.handleTerminalInput(message.target, message.keys);
7920
+ this.handleTerminalInput(message.target, message.keys, message.appId);
7731
7921
  break;
7732
7922
  case "terminal:resize":
7733
- this.handleTerminalResize(message.target, message.cols, message.rows);
7923
+ this.handleTerminalResize(message.target, message.cols, message.rows, message.appId);
7734
7924
  break;
7735
7925
  }
7736
7926
  } catch (error) {
@@ -7760,7 +7950,87 @@ var ViberController = class extends EventEmitter2 {
7760
7950
  },
7761
7951
  messages
7762
7952
  );
7763
- const finalText = await streamResult.text;
7953
+ let finalText = "";
7954
+ let pendingDelta = "";
7955
+ let lastProgressAt = 0;
7956
+ const flushProgress = (force = false) => {
7957
+ if (!pendingDelta) return;
7958
+ const now = Date.now();
7959
+ if (!force && now - lastProgressAt < 250) return;
7960
+ this.send({
7961
+ type: "task:progress",
7962
+ taskId,
7963
+ event: {
7964
+ kind: "text-delta",
7965
+ delta: pendingDelta,
7966
+ totalLength: finalText.length,
7967
+ at: new Date(now).toISOString()
7968
+ }
7969
+ });
7970
+ pendingDelta = "";
7971
+ lastProgressAt = now;
7972
+ };
7973
+ this.send({
7974
+ type: "task:progress",
7975
+ taskId,
7976
+ event: {
7977
+ kind: "status",
7978
+ phase: "executing",
7979
+ message: "Agent execution started",
7980
+ at: (/* @__PURE__ */ new Date()).toISOString()
7981
+ }
7982
+ });
7983
+ for await (const part of streamResult.fullStream) {
7984
+ switch (part.type) {
7985
+ case "text-delta":
7986
+ if (part.text) {
7987
+ finalText += part.text;
7988
+ pendingDelta += part.text;
7989
+ flushProgress(false);
7990
+ }
7991
+ break;
7992
+ case "tool-call":
7993
+ this.send({
7994
+ type: "task:progress",
7995
+ taskId,
7996
+ event: {
7997
+ kind: "tool-call",
7998
+ toolName: part.toolName,
7999
+ toolCallId: part.toolCallId,
8000
+ at: (/* @__PURE__ */ new Date()).toISOString()
8001
+ }
8002
+ });
8003
+ break;
8004
+ case "tool-result":
8005
+ this.send({
8006
+ type: "task:progress",
8007
+ taskId,
8008
+ event: {
8009
+ kind: "tool-result",
8010
+ toolCallId: part.toolCallId,
8011
+ at: (/* @__PURE__ */ new Date()).toISOString()
8012
+ }
8013
+ });
8014
+ break;
8015
+ case "error":
8016
+ throw new Error(part.error?.message || "Task stream error");
8017
+ case "finish":
8018
+ flushProgress(true);
8019
+ this.send({
8020
+ type: "task:progress",
8021
+ taskId,
8022
+ event: {
8023
+ kind: "status",
8024
+ phase: "verifying",
8025
+ message: "Agent execution finished, preparing final output",
8026
+ at: (/* @__PURE__ */ new Date()).toISOString()
8027
+ }
8028
+ });
8029
+ break;
8030
+ default:
8031
+ break;
8032
+ }
8033
+ }
7764
8034
  this.send({
7765
8035
  type: "task:completed",
7766
8036
  taskId,
@@ -7797,33 +8067,34 @@ var ViberController = class extends EventEmitter2 {
7797
8067
  }
7798
8068
  // ==================== Terminal Streaming ====================
7799
8069
  handleTerminalList() {
7800
- const { sessions, panes } = this.terminalManager.list();
7801
- this.send({ type: "terminal:list", sessions, panes });
8070
+ const { apps, sessions, panes } = this.terminalManager.list();
8071
+ this.send({ type: "terminal:list", apps, sessions, panes });
7802
8072
  }
7803
- async handleTerminalAttach(target) {
8073
+ async handleTerminalAttach(target, appId) {
7804
8074
  console.log(`[Viber] Attaching to terminal: ${target}`);
7805
8075
  const ok = await this.terminalManager.attach(
7806
8076
  target,
7807
8077
  (data) => {
7808
- this.send({ type: "terminal:output", target, data });
8078
+ this.send({ type: "terminal:output", target, appId, data });
7809
8079
  },
7810
8080
  () => {
7811
- this.send({ type: "terminal:detached", target });
7812
- }
8081
+ this.send({ type: "terminal:detached", target, appId });
8082
+ },
8083
+ appId
7813
8084
  );
7814
- this.send({ type: "terminal:attached", target, ok });
8085
+ this.send({ type: "terminal:attached", target, appId, ok });
7815
8086
  }
7816
- handleTerminalDetach(target) {
8087
+ handleTerminalDetach(target, appId) {
7817
8088
  console.log(`[Viber] Detaching from terminal: ${target}`);
7818
- this.terminalManager.detach(target);
7819
- this.send({ type: "terminal:detached", target });
8089
+ this.terminalManager.detach(target, appId);
8090
+ this.send({ type: "terminal:detached", target, appId });
7820
8091
  }
7821
- handleTerminalInput(target, keys) {
7822
- this.terminalManager.sendInput(target, keys);
8092
+ handleTerminalInput(target, keys, appId) {
8093
+ this.terminalManager.sendInput(target, keys, appId);
7823
8094
  }
7824
- handleTerminalResize(target, cols, rows) {
7825
- const ok = this.terminalManager.resize(target, cols, rows);
7826
- this.send({ type: "terminal:resized", target, ok });
8095
+ handleTerminalResize(target, cols, rows, appId) {
8096
+ const ok = this.terminalManager.resize(target, cols, rows, appId);
8097
+ this.send({ type: "terminal:resized", target, appId, ok });
7827
8098
  }
7828
8099
  // ==================== Communication ====================
7829
8100
  send(message) {