@brimveyn/aimux 1.2.5 → 1.3.1

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.
Files changed (59) hide show
  1. package/README.md +29 -8
  2. package/package.json +5 -5
  3. package/src/app-runtime/backend-runtime-events.ts +16 -1
  4. package/src/app-runtime/pty-write.ts +7 -4
  5. package/src/app-runtime/side-effects.ts +62 -4
  6. package/src/app-runtime/snippet-actions.ts +3 -2
  7. package/src/app-runtime/use-backend-runtime.ts +7 -2
  8. package/src/app-runtime/use-renderer-bindings.ts +2 -2
  9. package/src/app-runtime/use-terminal-resize.ts +15 -6
  10. package/src/app.tsx +89 -30
  11. package/src/config/loader.ts +2 -2
  12. package/src/config.ts +14 -3
  13. package/src/daemon/daemon.ts +279 -118
  14. package/src/daemon/runtime-paths.ts +34 -2
  15. package/src/daemon/session-manager.ts +20 -5
  16. package/src/daemon/session-registry.ts +18 -11
  17. package/src/index.tsx +19 -4
  18. package/src/input/keymap/describe-bindings.ts +68 -0
  19. package/src/input/keymap/key-format.ts +67 -0
  20. package/src/input/modes/bridge.ts +1 -0
  21. package/src/input/modes/transitions.ts +2 -0
  22. package/src/input/modes/types.ts +2 -0
  23. package/src/ipc/manager-protocol.ts +396 -0
  24. package/src/ipc/protocol.ts +98 -5
  25. package/src/platform/daemon-control.ts +42 -11
  26. package/src/profile-paths.ts +27 -0
  27. package/src/pty/pty-manager.ts +53 -3
  28. package/src/restart-daemon.ts +10 -10
  29. package/src/session-backend/bootstrap.ts +197 -46
  30. package/src/session-backend/local-session-backend.ts +12 -5
  31. package/src/session-backend/remote-session-backend.ts +143 -63
  32. package/src/session-backend/types.ts +4 -2
  33. package/src/state/reducers/modal-state.ts +18 -1
  34. package/src/state/reducers/tab-state.ts +20 -2
  35. package/src/state/session-catalog.ts +5 -4
  36. package/src/state/session-persistence.ts +9 -2
  37. package/src/state/snippet-catalog.ts +4 -4
  38. package/src/state/types.ts +23 -0
  39. package/src/state/validation.ts +8 -0
  40. package/src/terminal-manager/manager-client.ts +384 -0
  41. package/src/terminal-manager/terminal-manager.ts +288 -0
  42. package/src/ui/components/create-session-modal.tsx +3 -5
  43. package/src/ui/components/git-commit-modal.tsx +3 -5
  44. package/src/ui/components/help-modal.tsx +49 -68
  45. package/src/ui/components/list-item.tsx +24 -5
  46. package/src/ui/components/new-tab-modal.tsx +8 -9
  47. package/src/ui/components/pending-chord-overlay.tsx +28 -0
  48. package/src/ui/components/session-name-modal.tsx +3 -5
  49. package/src/ui/components/session-picker-modal.tsx +3 -1
  50. package/src/ui/components/snippet-editor-modal.tsx +3 -1
  51. package/src/ui/components/snippet-picker-modal.tsx +3 -1
  52. package/src/ui/components/status-bar.tsx +6 -2
  53. package/src/ui/components/theme-picker-modal.tsx +3 -6
  54. package/src/ui/components/update-available-modal.tsx +42 -0
  55. package/src/ui/keymap-context.ts +39 -0
  56. package/src/ui/root.tsx +12 -0
  57. package/src/ui/status-bar-model.ts +67 -39
  58. package/src/update/version-check.ts +67 -0
  59. package/src/update.ts +3 -3
@@ -2,8 +2,9 @@ import { Terminal as XTerm } from '@xterm/headless'
2
2
  import { type IPty, spawn } from 'bun-pty'
3
3
  import { EventEmitter } from 'node:events'
4
4
 
5
- import type { TerminalModeState, TerminalSnapshot } from '../state/types'
5
+ import type { ScrollIntent, TerminalModeState, TerminalSnapshot } from '../state/types'
6
6
 
7
+ import { logDebug } from '../debug/input-log'
7
8
  import { areTerminalSnapshotsEqual, snapshotTerminal } from './terminal-snapshot'
8
9
 
9
10
  type PtyManagerEvents = {
@@ -99,6 +100,12 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
99
100
 
100
101
  session.lastSnapshot = nextSnapshot
101
102
  session.lastTerminalModes = nextTerminalModes
103
+ logDebug('ptyManager.render', {
104
+ isAlternateBuffer: nextTerminalModes.isAlternateBuffer,
105
+ lines: nextSnapshot.lines.length,
106
+ tabId: session.tabId,
107
+ viewportY: nextSnapshot.viewportY,
108
+ })
102
109
  this.emit('render', session.tabId, nextSnapshot, nextTerminalModes)
103
110
  }
104
111
 
@@ -111,6 +118,7 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
111
118
  this.sessions.delete(session.tabId)
112
119
  this.emitRenderIfChanged(session)
113
120
  session.emulator.dispose()
121
+ logDebug('ptyManager.finalize', { exitCode, tabId: session.tabId })
114
122
  this.emit('exit', session.tabId, exitCode)
115
123
  }
116
124
 
@@ -123,6 +131,14 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
123
131
  cwd?: string
124
132
  }): void {
125
133
  this.disposeSession(options.tabId)
134
+ logDebug('ptyManager.create.start', {
135
+ args: options.args ?? [],
136
+ cols: options.cols,
137
+ command: options.command,
138
+ cwd: options.cwd ?? process.cwd(),
139
+ rows: options.rows,
140
+ tabId: options.tabId,
141
+ })
126
142
 
127
143
  try {
128
144
  const emulator = new XTerm({
@@ -157,6 +173,11 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
157
173
  }
158
174
 
159
175
  pty.onData((data) => {
176
+ logDebug('ptyManager.data', {
177
+ byteLength: Buffer.byteLength(data, 'utf8'),
178
+ pendingWrites: session.pendingWrites,
179
+ tabId: options.tabId,
180
+ })
160
181
  const trackedModes = trackPrivateModes(
161
182
  session.alternateScrollMode,
162
183
  session.cursorVisible,
@@ -178,6 +199,7 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
178
199
  })
179
200
 
180
201
  pty.onExit(({ exitCode }) => {
202
+ logDebug('ptyManager.exit', { exitCode, tabId: options.tabId })
181
203
  const current = this.sessions.get(options.tabId)
182
204
  if (!current || current.pty !== pty) {
183
205
  return
@@ -192,9 +214,11 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
192
214
  })
193
215
 
194
216
  this.sessions.set(options.tabId, session)
217
+ logDebug('ptyManager.create.success', { tabId: options.tabId })
195
218
  this.emitRenderIfChanged(session)
196
219
  } catch (error) {
197
220
  const message = error instanceof Error ? error.message : String(error)
221
+ logDebug('ptyManager.create.error', { error: message, tabId: options.tabId })
198
222
  this.emit('error', options.tabId, `Failed to start session: ${message}`)
199
223
  }
200
224
  }
@@ -223,18 +247,34 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
223
247
  this.emitRenderIfChanged(session)
224
248
  }
225
249
 
226
- resizeAll(cols: number, rows: number): void {
250
+ private applyScrollIntent(session: SessionHandle, intent: ScrollIntent | undefined): void {
251
+ if (!intent || intent.kind === 'bottom') {
252
+ session.emulator.scrollToBottom()
253
+ return
254
+ }
255
+
256
+ const baseY = session.emulator.buffer.active.baseY
257
+ if (intent.absoluteLine >= baseY) {
258
+ session.emulator.scrollToBottom()
259
+ return
260
+ }
261
+
262
+ session.emulator.scrollToLine(Math.max(0, intent.absoluteLine))
263
+ }
264
+
265
+ resizeAll(cols: number, rows: number, intents?: Map<string, ScrollIntent>): void {
227
266
  const safeCols = Math.max(20, cols)
228
267
  const safeRows = Math.max(8, rows)
229
268
 
230
269
  for (const session of this.sessions.values()) {
231
270
  session.pty.resize(safeCols, safeRows)
232
271
  session.emulator.resize(safeCols, safeRows)
272
+ this.applyScrollIntent(session, intents?.get(session.tabId))
233
273
  this.emitRenderIfChanged(session)
234
274
  }
235
275
  }
236
276
 
237
- resizeSession(tabId: string, cols: number, rows: number): void {
277
+ resizeSession(tabId: string, cols: number, rows: number, intent?: ScrollIntent): void {
238
278
  const session = this.sessions.get(tabId)
239
279
  if (!session) {
240
280
  return
@@ -243,6 +283,16 @@ export class PtyManager extends EventEmitter<PtyManagerEvents> {
243
283
  const safeRows = Math.max(8, rows)
244
284
  session.pty.resize(safeCols, safeRows)
245
285
  session.emulator.resize(safeCols, safeRows)
286
+ this.applyScrollIntent(session, intent)
287
+ this.emitRenderIfChanged(session)
288
+ }
289
+
290
+ reapplyScrollIntent(tabId: string, intent: ScrollIntent): void {
291
+ const session = this.sessions.get(tabId)
292
+ if (!session) {
293
+ return
294
+ }
295
+ this.applyScrollIntent(session, intent)
246
296
  this.emitRenderIfChanged(session)
247
297
  }
248
298
 
@@ -1,28 +1,28 @@
1
1
  import { getDaemonSocketPath, removeDaemonSocketIfExists } from './daemon/runtime-paths'
2
- import { findDaemonPid, killDaemon, spawnDetachedDaemon } from './platform/daemon-control'
2
+ import { findIpcDaemonPid, killProcess, spawnDetachedIpcDaemon } from './platform/daemon-control'
3
3
 
4
4
  export async function runRestartDaemon(): Promise<number> {
5
5
  const socketPath = getDaemonSocketPath()
6
- const pid = await findDaemonPid(socketPath)
6
+ const pid = await findIpcDaemonPid()
7
7
 
8
8
  if (pid !== null) {
9
- process.stdout.write(`Stopping daemon (pid ${pid})...\n`)
10
- await killDaemon(pid)
11
- process.stdout.write('Daemon stopped.\n')
9
+ process.stdout.write(`Stopping IPC daemon (pid ${pid})...\n`)
10
+ await killProcess(pid)
11
+ process.stdout.write('IPC daemon stopped.\n')
12
12
  } else {
13
- process.stdout.write('No running daemon found.\n')
13
+ process.stdout.write('No running IPC daemon found.\n')
14
14
  }
15
15
 
16
16
  removeDaemonSocketIfExists()
17
17
 
18
- process.stdout.write('Starting daemon...\n')
19
- const ok = await spawnDetachedDaemon()
18
+ process.stdout.write('Starting IPC daemon...\n')
19
+ const ok = await spawnDetachedIpcDaemon()
20
20
 
21
21
  if (ok) {
22
- process.stdout.write('Daemon started.\n')
22
+ process.stdout.write(`IPC daemon started on ${socketPath}.\n`)
23
23
  return 0
24
24
  }
25
25
 
26
- process.stderr.write('Failed to start daemon.\n')
26
+ process.stderr.write('Failed to start IPC daemon.\n')
27
27
  return 1
28
28
  }
@@ -3,32 +3,45 @@ import { connect } from 'node:net'
3
3
  import type { SessionBackend } from './types'
4
4
 
5
5
  import {
6
- getDaemonSocketPath,
7
- getDaemonSocketSecurityIssue,
6
+ getIpcDaemonSocketPath,
7
+ getSocketSecurityIssue,
8
8
  removeDaemonSocketIfExists,
9
9
  } from '../daemon/runtime-paths'
10
10
  import { logDebug } from '../debug/input-log'
11
- import { ProtocolMismatchError } from '../ipc/protocol'
12
- import { findDaemonPid, killDaemon, spawnDetachedDaemon } from '../platform/daemon-control'
11
+ import {
12
+ encodeMessage,
13
+ IPC_PROTOCOL_MIN_VERSION,
14
+ IPC_PROTOCOL_VERSION,
15
+ MessageDecoder,
16
+ parseServerMessage,
17
+ } from '../ipc/protocol'
18
+ import { findIpcDaemonPid, killProcess, spawnDetachedIpcDaemon } from '../platform/daemon-control'
13
19
  import { LocalSessionBackend } from './local-session-backend'
14
20
  import { RemoteSessionBackend } from './remote-session-backend'
15
21
 
22
+ interface DaemonHandshakeProbeResult {
23
+ compatible: boolean
24
+ error?: string
25
+ processVersion?: string
26
+ selectedVersion?: number
27
+ }
28
+
16
29
  async function spawnDaemon(): Promise<void> {
17
30
  logDebug('backend.spawnDaemon.start', {
18
31
  execPath: process.execPath,
19
- socketPath: getDaemonSocketPath(),
32
+ socketPath: getIpcDaemonSocketPath(),
20
33
  })
21
- const ok = await spawnDetachedDaemon()
34
+ const ok = await spawnDetachedIpcDaemon()
22
35
  if (ok) {
23
- logDebug('backend.spawnDaemon.ready', { socketPath: getDaemonSocketPath() })
36
+ logDebug('backend.spawnDaemon.ready', { socketPath: getIpcDaemonSocketPath() })
24
37
  return
25
38
  }
26
39
 
27
- logDebug('backend.spawnDaemon.timeout', { socketPath: getDaemonSocketPath() })
40
+ throw new Error(`IPC daemon unavailable at ${getIpcDaemonSocketPath()}`)
28
41
  }
29
42
 
30
43
  async function canConnectToDaemon(socketPath: string): Promise<boolean> {
31
- const securityIssue = getDaemonSocketSecurityIssue(socketPath)
44
+ const securityIssue = getSocketSecurityIssue(socketPath)
32
45
  if (securityIssue) {
33
46
  logDebug('backend.healthcheck.socketIssue', { issue: securityIssue, socketPath })
34
47
  return false
@@ -54,54 +67,192 @@ async function canConnectToDaemon(socketPath: string): Promise<boolean> {
54
67
  })
55
68
  }
56
69
 
70
+ export async function probeDaemonProtocolCompatibility(
71
+ socketPath: string
72
+ ): Promise<DaemonHandshakeProbeResult> {
73
+ const securityIssue = getSocketSecurityIssue(socketPath)
74
+ if (securityIssue) {
75
+ return { compatible: false, error: securityIssue }
76
+ }
77
+
78
+ return await new Promise<DaemonHandshakeProbeResult>((resolve) => {
79
+ const socket = connect(socketPath)
80
+ const decoder = new MessageDecoder(parseServerMessage)
81
+ const helloRequestId = crypto.randomUUID()
82
+ const attachRequestId = crypto.randomUUID()
83
+ const disposeRequestId = crypto.randomUUID()
84
+ const probeSessionId = `probe-${crypto.randomUUID()}`
85
+ let daemonProcessVersion: string | undefined
86
+ let settled = false
87
+ const timer = setTimeout(() => {
88
+ finish({ compatible: false, error: 'handshake timed out' })
89
+ }, 2_000)
90
+
91
+ const finish = (result: DaemonHandshakeProbeResult) => {
92
+ if (settled) {
93
+ return
94
+ }
95
+ settled = true
96
+ clearTimeout(timer)
97
+ socket.removeAllListeners()
98
+ socket.destroy()
99
+ resolve(result)
100
+ }
101
+
102
+ socket.once('connect', () => {
103
+ socket.write(
104
+ encodeMessage({
105
+ id: helloRequestId,
106
+ payload: {
107
+ maxVersion: IPC_PROTOCOL_VERSION,
108
+ minVersion: IPC_PROTOCOL_MIN_VERSION,
109
+ },
110
+ type: 'hello',
111
+ })
112
+ )
113
+ })
114
+ socket.once('error', (error: NodeJS.ErrnoException) => {
115
+ finish({ compatible: false, error: error.message })
116
+ })
117
+ socket.on('data', (chunk) => {
118
+ try {
119
+ for (const message of decoder.push(chunk)) {
120
+ if (!('id' in message)) {
121
+ continue
122
+ }
123
+
124
+ if (message.id === helloRequestId) {
125
+ if (message.type !== 'helloResult') {
126
+ finish({
127
+ compatible: false,
128
+ error:
129
+ message.type === 'error' ? message.payload.message : `unexpected ${message.type}`,
130
+ })
131
+ return
132
+ }
133
+
134
+ daemonProcessVersion = message.payload.processVersion
135
+
136
+ socket.write(
137
+ encodeMessage({
138
+ id: attachRequestId,
139
+ payload: {
140
+ cols: 80,
141
+ protocolVersion: message.payload.selectedVersion,
142
+ rows: 24,
143
+ sessionId: probeSessionId,
144
+ },
145
+ type: 'attach',
146
+ })
147
+ )
148
+ return
149
+ }
150
+
151
+ if (message.id === attachRequestId) {
152
+ if (message.type !== 'attachResult') {
153
+ finish({
154
+ compatible: false,
155
+ error:
156
+ message.type === 'error' ? message.payload.message : `unexpected ${message.type}`,
157
+ })
158
+ return
159
+ }
160
+
161
+ const compatible =
162
+ message.payload.protocolVersion >= IPC_PROTOCOL_MIN_VERSION &&
163
+ message.payload.protocolVersion <= IPC_PROTOCOL_VERSION
164
+
165
+ socket.write(
166
+ encodeMessage({
167
+ id: disposeRequestId,
168
+ payload: {},
169
+ type: 'disposeAll',
170
+ })
171
+ )
172
+ finish({
173
+ compatible,
174
+ error: compatible
175
+ ? undefined
176
+ : `attach returned protocol v${message.payload.protocolVersion}`,
177
+ processVersion: daemonProcessVersion,
178
+ selectedVersion: message.payload.protocolVersion,
179
+ })
180
+ return
181
+ }
182
+
183
+ if (message.id === disposeRequestId) {
184
+ continue
185
+ }
186
+ }
187
+ } catch (error) {
188
+ finish({
189
+ compatible: false,
190
+ error: error instanceof Error ? error.message : String(error),
191
+ })
192
+ }
193
+ })
194
+ })
195
+ }
196
+
57
197
  async function restartDaemon(socketPath: string): Promise<void> {
58
- const pid = await findDaemonPid(socketPath)
198
+ const pid = await findIpcDaemonPid()
59
199
  if (pid !== null) {
60
- logDebug('backend.restartDaemon.killing', { pid })
61
- await killDaemon(pid)
200
+ logDebug('backend.restartDaemon.killing', { pid, socketPath })
201
+ await killProcess(pid)
62
202
  }
63
203
  removeDaemonSocketIfExists()
64
204
  await spawnDaemon()
65
205
  }
66
206
 
67
207
  export async function createSessionBackend(): Promise<SessionBackend> {
68
- try {
69
- const socketPath = getDaemonSocketPath()
70
- const initialReachable = await canConnectToDaemon(socketPath)
71
- logDebug('backend.create.start', { initialReachable, socketPath })
72
-
73
- if (!initialReachable) {
74
- removeDaemonSocketIfExists()
75
- await spawnDaemon()
76
- }
208
+ if (process.env.AIMUX_LOCAL_BACKEND === '1') {
209
+ logDebug('backend.create.localExplicit')
210
+ return new LocalSessionBackend()
211
+ }
77
212
 
78
- const reachable = await canConnectToDaemon(socketPath)
79
- if (!reachable) {
80
- throw new Error(`Daemon unavailable at ${socketPath}`)
81
- }
213
+ const socketPath = getIpcDaemonSocketPath()
214
+ const initialReachable = await canConnectToDaemon(socketPath)
215
+ logDebug('backend.create.start', { initialReachable, socketPath })
82
216
 
83
- logDebug('backend.create.remote', { socketPath })
84
- return new RemoteSessionBackend()
85
- } catch (error) {
86
- if (error instanceof ProtocolMismatchError) {
87
- logDebug('backend.create.protocolMismatch', {
88
- clientVersion: error.clientVersion,
89
- daemonVersion: error.daemonVersion,
90
- })
91
- try {
92
- await restartDaemon(getDaemonSocketPath())
93
- logDebug('backend.create.remoteAfterRestart')
94
- return new RemoteSessionBackend()
95
- } catch (retryError) {
96
- logDebug('backend.create.retryFailed', {
97
- error: retryError instanceof Error ? retryError.message : String(retryError),
98
- })
99
- }
100
- }
217
+ if (!initialReachable) {
218
+ removeDaemonSocketIfExists()
219
+ await spawnDaemon()
220
+ }
221
+
222
+ const reachable = await canConnectToDaemon(socketPath)
223
+ if (!reachable) {
224
+ throw new Error(`IPC daemon unavailable at ${socketPath}`)
225
+ }
101
226
 
102
- logDebug('backend.create.localFallback', {
103
- error: error instanceof Error ? error.message : String(error),
227
+ const handshake = await probeDaemonProtocolCompatibility(socketPath)
228
+ logDebug('backend.create.handshake', {
229
+ compatible: handshake.compatible,
230
+ error: handshake.error ?? null,
231
+ processVersion: handshake.processVersion ?? null,
232
+ selectedVersion: handshake.selectedVersion ?? null,
233
+ socketPath,
234
+ })
235
+ if (!handshake.compatible) {
236
+ logDebug('backend.create.restartForHandshake', {
237
+ error: handshake.error ?? 'incompatible daemon handshake',
238
+ socketPath,
104
239
  })
105
- return new LocalSessionBackend()
240
+ await restartDaemon(socketPath)
241
+ const retriedHandshake = await probeDaemonProtocolCompatibility(socketPath)
242
+ logDebug('backend.create.handshakeAfterRestart', {
243
+ compatible: retriedHandshake.compatible,
244
+ error: retriedHandshake.error ?? null,
245
+ processVersion: retriedHandshake.processVersion ?? null,
246
+ selectedVersion: retriedHandshake.selectedVersion ?? null,
247
+ socketPath,
248
+ })
249
+ if (!retriedHandshake.compatible) {
250
+ throw new Error(
251
+ `IPC daemon handshake failed after restart: ${retriedHandshake.error ?? 'incompatible protocol'}`
252
+ )
253
+ }
106
254
  }
255
+
256
+ logDebug('backend.create.remote', { socketPath })
257
+ return new RemoteSessionBackend()
107
258
  }
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from 'node:events'
2
2
 
3
- import type { AssistantId, WorkspaceSnapshotV1 } from '../state/types'
3
+ import type { AssistantId, ScrollIntent, WorkspaceSnapshotV1 } from '../state/types'
4
4
  import type { SessionBackend, SessionBackendEvents } from './types'
5
5
 
6
6
  import { SessionManager } from '../daemon/session-manager'
@@ -114,6 +114,13 @@ export class LocalSessionBackend
114
114
  this.sessionManager.scrollToBottom(this.currentSessionId, tabId)
115
115
  }
116
116
 
117
+ reapplyScrollIntent(tabId: string, intent: ScrollIntent): void {
118
+ if (!this.currentSessionId) {
119
+ return
120
+ }
121
+ this.sessionManager.reapplyScrollIntent(this.currentSessionId, tabId, intent)
122
+ }
123
+
117
124
  setActiveTab(tabId: string | null): void {
118
125
  if (!this.currentSessionId) {
119
126
  return
@@ -122,18 +129,18 @@ export class LocalSessionBackend
122
129
  this.sessionManager.setActiveTab(this.currentSessionId, tabId)
123
130
  }
124
131
 
125
- resizeAll(cols: number, rows: number): void {
132
+ resizeAll(cols: number, rows: number, intents?: Map<string, ScrollIntent>): void {
126
133
  if (!this.currentSessionId) {
127
134
  return
128
135
  }
129
- this.sessionManager.resize(this.currentSessionId, cols, rows)
136
+ this.sessionManager.resize(this.currentSessionId, cols, rows, intents)
130
137
  }
131
138
 
132
- resizeTab(tabId: string, cols: number, rows: number): void {
139
+ resizeTab(tabId: string, cols: number, rows: number, intent?: ScrollIntent): void {
133
140
  if (!this.currentSessionId) {
134
141
  return
135
142
  }
136
- this.sessionManager.resizeTab(this.currentSessionId, tabId, cols, rows)
143
+ this.sessionManager.resizeTab(this.currentSessionId, tabId, cols, rows, intent)
137
144
  }
138
145
 
139
146
  disposeSession(tabId: string): void {