@electerm/electerm-react 3.0.18 → 3.1.16
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/client/common/constants.js +4 -4
- package/client/common/db.js +20 -10
- package/client/components/file-transfer/conflict-resolve.jsx +38 -4
- package/client/components/file-transfer/transfer-queue.jsx +10 -4
- package/client/components/file-transfer/transfer.jsx +7 -4
- package/client/components/file-transfer/transports-action-store.jsx +14 -2
- package/client/components/footer/cmd-history.jsx +2 -4
- package/client/components/quick-commands/qm.styl +8 -0
- package/client/components/quick-commands/quick-command-item.jsx +4 -0
- package/client/components/quick-commands/quick-commands-box.jsx +10 -0
- package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -1
- package/client/components/quick-commands/quick-commands-list-form.jsx +59 -4
- package/client/components/quick-commands/quick-commands-list.jsx +33 -3
- package/client/components/setting-panel/list.styl +5 -1
- package/client/components/setting-sync/server-data-status.jsx +2 -1
- package/client/components/setting-sync/setting-sync-form.jsx +93 -15
- package/client/components/setting-sync/setting-sync.jsx +5 -1
- package/client/components/sidebar/transfer-history-modal.jsx +2 -2
- package/client/components/tabs/tabs.styl +1 -0
- package/client/components/tabs/window-control.jsx +2 -0
- package/client/components/terminal/terminal-command-dropdown.jsx +1 -1
- package/client/components/terminal/terminal.jsx +14 -1
- package/client/components/terminal/transfer-client-base.js +38 -17
- package/client/components/terminal-info/network.jsx +0 -1
- package/client/components/tree-list/tree-list-item.jsx +5 -0
- package/client/components/tree-list/tree-list.jsx +17 -4
- package/client/components/tree-list/tree-list.styl +1 -1
- package/client/store/common.js +15 -15
- package/client/store/init-state.js +6 -31
- package/client/store/mcp-handler.js +315 -129
- package/client/store/store.js +1 -1
- package/client/store/sync.js +129 -3
- package/client/store/transfer-list.js +3 -2
- package/client/store/watch.js +1 -25
- package/package.json +1 -1
|
@@ -7,6 +7,10 @@ import uid from '../common/uid'
|
|
|
7
7
|
import { settingMap } from '../common/constants'
|
|
8
8
|
import { refs } from '../components/common/ref'
|
|
9
9
|
import deepCopy from 'json-deep-copy'
|
|
10
|
+
import {
|
|
11
|
+
getLocalFileInfo,
|
|
12
|
+
getRemoteFileInfo
|
|
13
|
+
} from '../components/sftp/file-read'
|
|
10
14
|
|
|
11
15
|
export default Store => {
|
|
12
16
|
// Initialize MCP handler - called when MCP widget is started
|
|
@@ -56,8 +60,7 @@ export default Store => {
|
|
|
56
60
|
case 'add_bookmark_group':
|
|
57
61
|
result = await store.mcpAddBookmarkGroup(args)
|
|
58
62
|
break
|
|
59
|
-
|
|
60
|
-
// Quick command operations
|
|
63
|
+
/*
|
|
61
64
|
case 'list_quick_commands':
|
|
62
65
|
result = store.mcpListQuickCommands()
|
|
63
66
|
break
|
|
@@ -70,7 +73,7 @@ export default Store => {
|
|
|
70
73
|
case 'delete_quick_command':
|
|
71
74
|
result = store.mcpDeleteQuickCommand(args)
|
|
72
75
|
break
|
|
73
|
-
|
|
76
|
+
*/
|
|
74
77
|
// Tab operations
|
|
75
78
|
case 'list_tabs':
|
|
76
79
|
result = store.mcpListTabs()
|
|
@@ -105,20 +108,34 @@ export default Store => {
|
|
|
105
108
|
result = store.mcpGetTerminalOutput(args)
|
|
106
109
|
break
|
|
107
110
|
|
|
108
|
-
//
|
|
109
|
-
case '
|
|
110
|
-
result = store.
|
|
111
|
+
// SFTP operations
|
|
112
|
+
case 'sftp_list':
|
|
113
|
+
result = await store.mcpSftpList(args)
|
|
114
|
+
break
|
|
115
|
+
case 'sftp_del':
|
|
116
|
+
result = await store.mcpSftpDel(args)
|
|
111
117
|
break
|
|
112
|
-
case '
|
|
113
|
-
result = store.
|
|
118
|
+
case 'sftp_stat':
|
|
119
|
+
result = await store.mcpSftpStat(args)
|
|
120
|
+
break
|
|
121
|
+
case 'sftp_read_file':
|
|
122
|
+
result = await store.mcpSftpReadFile(args)
|
|
114
123
|
break
|
|
115
124
|
|
|
116
|
-
//
|
|
117
|
-
case '
|
|
118
|
-
result = store.
|
|
125
|
+
// File transfer operations
|
|
126
|
+
case 'sftp_upload':
|
|
127
|
+
result = await store.mcpSftpUpload(args)
|
|
119
128
|
break
|
|
120
|
-
case '
|
|
121
|
-
result = store.
|
|
129
|
+
case 'sftp_download':
|
|
130
|
+
result = await store.mcpSftpDownload(args)
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
// Zmodem (trzsz/rzsz) operations
|
|
134
|
+
case 'zmodem_upload':
|
|
135
|
+
result = store.mcpZmodemUpload(args)
|
|
136
|
+
break
|
|
137
|
+
case 'zmodem_download':
|
|
138
|
+
result = store.mcpZmodemDownload(args)
|
|
122
139
|
break
|
|
123
140
|
|
|
124
141
|
// Settings operations
|
|
@@ -249,58 +266,58 @@ export default Store => {
|
|
|
249
266
|
|
|
250
267
|
// ==================== Quick Command APIs ====================
|
|
251
268
|
|
|
252
|
-
Store.prototype.mcpListQuickCommands = function () {
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
Store.prototype.mcpAddQuickCommand = function (args) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
Store.prototype.mcpRunQuickCommand = function (args) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
Store.prototype.mcpDeleteQuickCommand = function (args) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
269
|
+
// Store.prototype.mcpListQuickCommands = function () {
|
|
270
|
+
// return deepCopy(window.store.quickCommands)
|
|
271
|
+
// }
|
|
272
|
+
|
|
273
|
+
// Store.prototype.mcpAddQuickCommand = function (args) {
|
|
274
|
+
// const { store } = window
|
|
275
|
+
// const qm = {
|
|
276
|
+
// id: uid(),
|
|
277
|
+
// name: args.name,
|
|
278
|
+
// commands: args.commands,
|
|
279
|
+
// inputOnly: args.inputOnly || false,
|
|
280
|
+
// labels: args.labels || []
|
|
281
|
+
// }
|
|
282
|
+
|
|
283
|
+
// store.addQuickCommand(qm)
|
|
284
|
+
|
|
285
|
+
// return {
|
|
286
|
+
// success: true,
|
|
287
|
+
// id: qm.id,
|
|
288
|
+
// message: `Quick command "${qm.name}" created`
|
|
289
|
+
// }
|
|
290
|
+
// }
|
|
291
|
+
|
|
292
|
+
// Store.prototype.mcpRunQuickCommand = function (args) {
|
|
293
|
+
// const { store } = window
|
|
294
|
+
// const qm = store.quickCommands.find(q => q.id === args.id)
|
|
295
|
+
// if (!qm) {
|
|
296
|
+
// throw new Error(`Quick command not found: ${args.id}`)
|
|
297
|
+
// }
|
|
298
|
+
|
|
299
|
+
// store.runQuickCommandItem(args.id)
|
|
300
|
+
|
|
301
|
+
// return {
|
|
302
|
+
// success: true,
|
|
303
|
+
// message: `Executed quick command "${qm.name}"`
|
|
304
|
+
// }
|
|
305
|
+
// }
|
|
306
|
+
|
|
307
|
+
// Store.prototype.mcpDeleteQuickCommand = function (args) {
|
|
308
|
+
// const { store } = window
|
|
309
|
+
// const qm = store.quickCommands.find(q => q.id === args.id)
|
|
310
|
+
// if (!qm) {
|
|
311
|
+
// throw new Error(`Quick command not found: ${args.id}`)
|
|
312
|
+
// }
|
|
313
|
+
|
|
314
|
+
// store.delQuickCommand({ id: args.id })
|
|
315
|
+
|
|
316
|
+
// return {
|
|
317
|
+
// success: true,
|
|
318
|
+
// message: `Deleted quick command "${qm.name}"`
|
|
319
|
+
// }
|
|
320
|
+
// }
|
|
304
321
|
|
|
305
322
|
// ==================== Tab APIs ====================
|
|
306
323
|
|
|
@@ -502,92 +519,261 @@ export default Store => {
|
|
|
502
519
|
}
|
|
503
520
|
}
|
|
504
521
|
|
|
505
|
-
// ====================
|
|
522
|
+
// ==================== Settings APIs ====================
|
|
506
523
|
|
|
507
|
-
Store.prototype.
|
|
524
|
+
Store.prototype.mcpGetSettings = function () {
|
|
508
525
|
const { store } = window
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
type: h.type,
|
|
517
|
-
time: h.time
|
|
518
|
-
}))
|
|
526
|
+
// Return safe settings (no sensitive data)
|
|
527
|
+
const config = store.config
|
|
528
|
+
const excludeKeys = ['apiKeyAI', 'syncSetting']
|
|
529
|
+
const safeConfig = Object.fromEntries(
|
|
530
|
+
Object.entries(config).filter(([key]) => !excludeKeys.includes(key))
|
|
531
|
+
)
|
|
532
|
+
return safeConfig
|
|
519
533
|
}
|
|
520
534
|
|
|
521
|
-
|
|
535
|
+
// ==================== SFTP APIs ====================
|
|
536
|
+
|
|
537
|
+
Store.prototype.mcpGetSshSftpRef = function (tabId) {
|
|
522
538
|
const { store } = window
|
|
523
|
-
|
|
539
|
+
const resolvedTabId = tabId || store.activeTabId
|
|
540
|
+
if (!resolvedTabId) {
|
|
541
|
+
throw new Error('No active tab')
|
|
542
|
+
}
|
|
543
|
+
const tab = store.tabs.find(t => t.id === resolvedTabId)
|
|
544
|
+
if (!tab) {
|
|
545
|
+
throw new Error(`Tab not found: ${resolvedTabId}`)
|
|
546
|
+
}
|
|
547
|
+
if (tab.type !== 'ssh' && tab.type !== 'ftp') {
|
|
548
|
+
throw new Error(`Tab "${resolvedTabId}" is not an SSH/SFTP tab (type: ${tab.type || 'local'})`)
|
|
549
|
+
}
|
|
550
|
+
const sftpEntry = refs.get('sftp-' + resolvedTabId)
|
|
551
|
+
if (!sftpEntry || !sftpEntry.sftp) {
|
|
552
|
+
throw new Error(`SFTP not initialized for tab "${resolvedTabId}". Open the SFTP panel first.`)
|
|
553
|
+
}
|
|
554
|
+
return { sftp: sftpEntry.sftp, tab, tabId: resolvedTabId }
|
|
555
|
+
}
|
|
524
556
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
557
|
+
Store.prototype.mcpSftpList = async function (args) {
|
|
558
|
+
const { sftp, tab, tabId } = window.store.mcpGetSshSftpRef(args.tabId)
|
|
559
|
+
const remotePath = args.remotePath
|
|
560
|
+
if (!remotePath) {
|
|
561
|
+
throw new Error('remotePath is required')
|
|
528
562
|
}
|
|
563
|
+
const list = await sftp.list(remotePath)
|
|
564
|
+
return { tabId, host: tab.host, path: remotePath, list }
|
|
529
565
|
}
|
|
530
566
|
|
|
531
|
-
|
|
567
|
+
Store.prototype.mcpSftpStat = async function (args) {
|
|
568
|
+
const { sftp, tab, tabId } = window.store.mcpGetSshSftpRef(args.tabId)
|
|
569
|
+
const remotePath = args.remotePath
|
|
570
|
+
if (!remotePath) {
|
|
571
|
+
throw new Error('remotePath is required')
|
|
572
|
+
}
|
|
573
|
+
const stat = await sftp.stat(remotePath)
|
|
574
|
+
return { tabId, host: tab.host, path: remotePath, stat }
|
|
575
|
+
}
|
|
532
576
|
|
|
533
|
-
Store.prototype.
|
|
534
|
-
const {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
status: t.status
|
|
542
|
-
}))
|
|
577
|
+
Store.prototype.mcpSftpReadFile = async function (args) {
|
|
578
|
+
const { sftp, tab, tabId } = window.store.mcpGetSshSftpRef(args.tabId)
|
|
579
|
+
const remotePath = args.remotePath
|
|
580
|
+
if (!remotePath) {
|
|
581
|
+
throw new Error('remotePath is required')
|
|
582
|
+
}
|
|
583
|
+
const content = await sftp.readFile(remotePath)
|
|
584
|
+
return { tabId, host: tab.host, path: remotePath, content }
|
|
543
585
|
}
|
|
544
586
|
|
|
545
|
-
Store.prototype.
|
|
546
|
-
const {
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
587
|
+
Store.prototype.mcpSftpDel = async function (args) {
|
|
588
|
+
const { sftp, tab, tabId } = window.store.mcpGetSshSftpRef(args.tabId)
|
|
589
|
+
const remotePath = args.remotePath
|
|
590
|
+
if (!remotePath) {
|
|
591
|
+
throw new Error('remotePath is required')
|
|
592
|
+
}
|
|
593
|
+
// Use stat to determine if it's a file or directory
|
|
594
|
+
const stat = await sftp.stat(remotePath)
|
|
595
|
+
const isDirectory = typeof stat.isDirectory === 'function'
|
|
596
|
+
? stat.isDirectory()
|
|
597
|
+
: !!stat.isDirectory
|
|
598
|
+
if (isDirectory) {
|
|
599
|
+
await sftp.rmdir(remotePath)
|
|
600
|
+
} else {
|
|
601
|
+
await sftp.rm(remotePath)
|
|
602
|
+
}
|
|
603
|
+
return { success: true, tabId, host: tab.host, path: remotePath, type: isDirectory ? 'directory' : 'file' }
|
|
556
604
|
}
|
|
557
605
|
|
|
558
|
-
// ====================
|
|
606
|
+
// ==================== File Transfer APIs ====================
|
|
559
607
|
|
|
560
|
-
Store.prototype.
|
|
608
|
+
Store.prototype.mcpSftpUpload = async function (args) {
|
|
561
609
|
const { store } = window
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
const
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
610
|
+
const { tab, tabId } = store.mcpGetSshSftpRef(args.tabId)
|
|
611
|
+
const localPath = args.localPath
|
|
612
|
+
const remotePath = args.remotePath
|
|
613
|
+
if (!localPath) {
|
|
614
|
+
throw new Error('localPath is required')
|
|
615
|
+
}
|
|
616
|
+
if (!remotePath) {
|
|
617
|
+
throw new Error('remotePath is required')
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
window._transferConflictPolicy = args.conflictPolicy || 'overwrite'
|
|
621
|
+
|
|
622
|
+
const fromFile = await getLocalFileInfo(localPath)
|
|
623
|
+
const transferItem = {
|
|
624
|
+
host: tab.host,
|
|
625
|
+
tabType: tab.type || 'ssh',
|
|
626
|
+
typeFrom: 'local',
|
|
627
|
+
typeTo: 'remote',
|
|
628
|
+
fromPath: localPath,
|
|
629
|
+
toPath: remotePath,
|
|
630
|
+
fromFile: {
|
|
631
|
+
...fromFile,
|
|
632
|
+
host: tab.host,
|
|
633
|
+
tabType: tab.type || 'ssh',
|
|
634
|
+
tabId,
|
|
635
|
+
title: tab.title
|
|
636
|
+
},
|
|
637
|
+
id: uid(),
|
|
638
|
+
title: tab.title,
|
|
639
|
+
tabId,
|
|
640
|
+
operation: ''
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
store.addTransferList([transferItem])
|
|
644
|
+
|
|
645
|
+
return {
|
|
646
|
+
success: true,
|
|
647
|
+
message: `Upload started: ${localPath} → ${tab.host}:${remotePath}`,
|
|
648
|
+
transferId: transferItem.id,
|
|
649
|
+
tabId
|
|
573
650
|
}
|
|
574
|
-
return safeConfig
|
|
575
651
|
}
|
|
576
652
|
|
|
577
|
-
Store.prototype.
|
|
653
|
+
Store.prototype.mcpSftpDownload = async function (args) {
|
|
578
654
|
const { store } = window
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
655
|
+
const { sftp, tab, tabId } = store.mcpGetSshSftpRef(args.tabId) // sftp used for getRemoteFileInfo
|
|
656
|
+
const remotePath = args.remotePath
|
|
657
|
+
const localPath = args.localPath
|
|
658
|
+
if (!remotePath) {
|
|
659
|
+
throw new Error('remotePath is required')
|
|
660
|
+
}
|
|
661
|
+
if (!localPath) {
|
|
662
|
+
throw new Error('localPath is required')
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
window._transferConflictPolicy = args.conflictPolicy || 'overwrite'
|
|
666
|
+
|
|
667
|
+
const fromFile = await getRemoteFileInfo(sftp, remotePath)
|
|
668
|
+
const transferItem = {
|
|
669
|
+
host: tab.host,
|
|
670
|
+
tabType: tab.type || 'ssh',
|
|
671
|
+
typeFrom: 'remote',
|
|
672
|
+
typeTo: 'local',
|
|
673
|
+
fromPath: remotePath,
|
|
674
|
+
toPath: localPath,
|
|
675
|
+
fromFile: {
|
|
676
|
+
...fromFile,
|
|
677
|
+
id: uid(),
|
|
678
|
+
isSymbolicLink: false
|
|
679
|
+
},
|
|
680
|
+
id: uid(),
|
|
681
|
+
title: tab.title,
|
|
682
|
+
tabId
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
store.addTransferList([transferItem])
|
|
686
|
+
|
|
687
|
+
return {
|
|
688
|
+
success: true,
|
|
689
|
+
message: `Download started: ${tab.host}:${remotePath} → ${localPath}`,
|
|
690
|
+
transferId: transferItem.id,
|
|
691
|
+
tabId
|
|
692
|
+
}
|
|
584
693
|
}
|
|
585
694
|
|
|
586
|
-
|
|
695
|
+
// ==================== Zmodem (trzsz/rzsz) APIs ====================
|
|
696
|
+
|
|
697
|
+
Store.prototype.mcpZmodemUpload = function (args) {
|
|
587
698
|
const { store } = window
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
699
|
+
const tabId = args.tabId || store.activeTabId
|
|
700
|
+
if (!tabId) {
|
|
701
|
+
throw new Error('No active tab')
|
|
702
|
+
}
|
|
703
|
+
const tab = store.tabs.find(t => t.id === tabId)
|
|
704
|
+
if (!tab) {
|
|
705
|
+
throw new Error(`Tab not found: ${tabId}`)
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const files = args.files
|
|
709
|
+
if (!files || !Array.isArray(files) || files.length === 0) {
|
|
710
|
+
throw new Error('files array is required (list of local file paths to upload)')
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const protocol = args.protocol || 'rzsz'
|
|
714
|
+
const uploadCmd = protocol === 'trzsz' ? 'trz' : 'rz'
|
|
715
|
+
|
|
716
|
+
// Set the control variable to bypass native file dialog
|
|
717
|
+
window._apiControlSelectFile = files
|
|
718
|
+
|
|
719
|
+
const term = refs.get('term-' + tabId)
|
|
720
|
+
if (!term) {
|
|
721
|
+
throw new Error(`Terminal not found for tab: ${tabId}`)
|
|
722
|
+
}
|
|
723
|
+
term.runQuickCommand(uploadCmd)
|
|
724
|
+
|
|
725
|
+
return {
|
|
726
|
+
success: true,
|
|
727
|
+
protocol,
|
|
728
|
+
command: uploadCmd,
|
|
729
|
+
message: `${uploadCmd} upload initiated for ${files.length} file(s)`,
|
|
730
|
+
files,
|
|
731
|
+
tabId
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
Store.prototype.mcpZmodemDownload = function (args) {
|
|
736
|
+
const { store } = window
|
|
737
|
+
const tabId = args.tabId || store.activeTabId
|
|
738
|
+
if (!tabId) {
|
|
739
|
+
throw new Error('No active tab')
|
|
740
|
+
}
|
|
741
|
+
const tab = store.tabs.find(t => t.id === tabId)
|
|
742
|
+
if (!tab) {
|
|
743
|
+
throw new Error(`Tab not found: ${tabId}`)
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const saveFolder = args.saveFolder
|
|
747
|
+
if (!saveFolder) {
|
|
748
|
+
throw new Error('saveFolder is required (local folder to save downloaded files)')
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const remoteFiles = args.remoteFiles
|
|
752
|
+
if (!remoteFiles || !Array.isArray(remoteFiles) || remoteFiles.length === 0) {
|
|
753
|
+
throw new Error('remoteFiles array is required (list of remote file paths to download)')
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
const protocol = args.protocol || 'rzsz'
|
|
757
|
+
const downloadCmd = protocol === 'trzsz' ? 'tsz' : 'sz'
|
|
758
|
+
|
|
759
|
+
// Set the control variable to bypass native folder dialog
|
|
760
|
+
window._apiControlSelectFolder = saveFolder
|
|
761
|
+
|
|
762
|
+
const term = refs.get('term-' + tabId)
|
|
763
|
+
if (!term) {
|
|
764
|
+
throw new Error(`Terminal not found for tab: ${tabId}`)
|
|
765
|
+
}
|
|
766
|
+
const quotedFiles = remoteFiles.map(f => `"${f}"`).join(' ')
|
|
767
|
+
term.runQuickCommand(`${downloadCmd} ${quotedFiles}`)
|
|
768
|
+
|
|
769
|
+
return {
|
|
770
|
+
success: true,
|
|
771
|
+
protocol,
|
|
772
|
+
command: downloadCmd,
|
|
773
|
+
message: `${downloadCmd} download initiated for ${remoteFiles.length} file(s) to ${saveFolder}`,
|
|
774
|
+
remoteFiles,
|
|
775
|
+
saveFolder,
|
|
776
|
+
tabId
|
|
777
|
+
}
|
|
592
778
|
}
|
|
593
779
|
}
|
package/client/store/store.js
CHANGED
|
@@ -176,7 +176,7 @@ class Store {
|
|
|
176
176
|
|
|
177
177
|
get terminalCommandSuggestions () {
|
|
178
178
|
const { store } = window
|
|
179
|
-
const historyCommands =
|
|
179
|
+
const historyCommands = store.terminalCommandHistory.map(item => item.cmd)
|
|
180
180
|
const batchInputCommands = store.batchInputs || []
|
|
181
181
|
const quickCommands = (store.quickCommands || []).reduce(
|
|
182
182
|
(p, q) => {
|