feat(M4): file permissions/ownership + monitor charts
- SftpEntry carries uid/gid/mode (drwxr-xr-x style) from readdir attrs - chmod/chown via SSH exec (JD sftp subsystem silently ignores SETSTAT) - FilePanel: permission + owner columns, chmod/chown dialog (rwx grid + octal input + uid/gid) - MonitorPanel: canvas sparkline charts — CPU history (60 samples, 3min) and network rx/tx rates, dpr-aware, no deps - tests: sftp-chmod real-server verification (chmod 700/755 round trip)
This commit is contained in:
1 parent
06fa4ca023
commit
2849fc30dc
60 files changed
+1975
-17
No files matched your search
@@ -7,6 +7,7 @@ import { broadcast } from './broadcast'
|
||||
import { connectSsh, type SshSessionHandle } from './ssh'
|
||||
import type { HostKeyCheckResult } from './knownHosts'
|
||||
import { configureSysinfo, registerSysinfoClient, stopPolling } from './sysinfo'
|
||||
import { registerSftpClientProvider, closeSftp } from './sftp'
|
||||
|
||||
// Re-export so the session-layer loopback bundle (tests/*-e2e.mjs) can drive the
|
||||
// M3 polling engine without importing src/main/sysinfo.ts separately.
|
||||
@@ -19,6 +20,12 @@ export { startPolling, stopPolling } from './sysinfo'
|
||||
*/
|
||||
type Session = { kind: 'local'; pty: IPty } | { kind: 'ssh'; ssh: SshSessionHandle }
|
||||
|
||||
/** The live ssh2 client behind an ssh session, or undefined. */
|
||||
export function getSshClient(id: string): SshSessionHandle['client'] | undefined {
|
||||
const session = sessions.get(id)
|
||||
return session?.kind === 'ssh' ? session.ssh.client : undefined
|
||||
}
|
||||
|
||||
const sessions = new Map<string, Session>()
|
||||
|
||||
/**
|
||||
@@ -77,6 +84,11 @@ export function configureSessionRuntime(deps: SessionRuntimeDeps): void {
|
||||
if (session?.kind === 'ssh') return session.ssh.client
|
||||
return undefined
|
||||
})
|
||||
registerSftpClientProvider((id) => {
|
||||
const session = sessions.get(id)
|
||||
if (session?.kind === 'ssh') return session.ssh.client
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
function defaultShell(): string {
|
||||
@@ -170,6 +182,7 @@ export async function openSession(opts: SessionOpenOptions): Promise<{ id: strin
|
||||
handle.stream.on('close', () => {
|
||||
try {
|
||||
stopPolling(handle.id)
|
||||
closeSftp(handle.id)
|
||||
sessions.delete(handle.id)
|
||||
deps.broadcast(Ipc.PTY_EXIT, { id: handle.id, exitCode: 0 })
|
||||
} catch {
|
||||
@@ -205,6 +218,8 @@ export function resizePty(id: string, cols: number, rows: number): void {
|
||||
|
||||
export function killPty(id: string): void {
|
||||
stopPolling(id)
|
||||
closeSftp(id)
|
||||
closeSftp(id)
|
||||
replayBuffers.delete(id)
|
||||
const session = sessions.get(id)
|
||||
if (!session) return
|
||||
@@ -232,6 +247,7 @@ export function killPty(id: string): void {
|
||||
export function killAllPtys(): void {
|
||||
for (const id of sessions.keys()) {
|
||||
stopPolling(id)
|
||||
closeSftp(id)
|
||||
const session = sessions.get(id)
|
||||
if (!session) continue
|
||||
if (session.kind === 'ssh') {
|
||||
|
||||
Reference in new issue
Block a user