Files
OpenTerminal/src/main/zmodem-js.d.ts
T
954801926@qq.com 0663e5affa feat(M6): broadcast input + main-process ZMODEM + quick input panel + shortcuts
- broadcast: session target picker popover, fan-out write path, <2 targets auto-disable, per-tab target dots
- zmodem: main-process engine over ssh sessions (rz send / sz receive), progress reuses transfer panel, offer 120s / stall 90s watchdogs, e2e test with real zmodem.js peer (both directions)
- quick input panel: bottom-right overlay (command line + auto-\r + library + recent), broadcast-aware send
- shortcuts: Ctrl+=/-/0 font size (persisted), globalShowHide accelerator via globalShortcut, Ctrl+PgUp/PgDn terminal cycling
- contracts: ZMODEM_OFFER/RESPOND/DONE channels, SystemSettings.globalShowHide, TransferKind zmodem-upload/download
- review fixes: xterm-helper-textarea font hotkey pass-through, zmodem error-vs-cancelled progress state, offer timer clear, receive stream destroy
2026-09-06 20:15:49 +08:00

125 lines
4.8 KiB
TypeScript

/**
* Minimal ambient typings for the `zmodem.js` package (v0.1.10).
*
* zmodem.js ships no type declarations and `@types/zmodem.js` is not installed;
* npm install is out of scope here, so this file declares exactly the surface
* src/main/zmodem.ts consumes. Verified against the sources in
* node_modules/zmodem.js/src/{zsentry,zsession,zvalidation,zheader}.js:
*
* - Sentry.consume() accepts a Uint8Array / ArrayBuffer / octet Array.
* - Detection.confirm() returns the matched Session; the Session's `.type`
* is "receive" (remote `sz` -> we download) or "send" (remote `rz` -> we
* upload).
* - The byte arrays passed to `to_terminal` / `sender` are plain number[].
*/
declare module 'zmodem.js' {
/** one batch of a file being received (see Offer) */
export interface FileDetails {
name: string
/** can be null when the peer streams an unknown size; zmodem.js accepts */
size?: number | null
/** POSIX mode bits (0x8000 | perm); optional */
mode?: number | null
/** Date or epoch seconds; optional */
mtime?: Date | number | null
files_remaining?: number | null
bytes_remaining?: number | null
}
/** A sender-side single-file transfer handle (result of send_offer()). */
export interface Transfer {
get_details(): FileDetails
get_offset(): number
/** Send a non-terminal chunk. */
send(arrayLike: number[] | Uint8Array): void
/** Send the terminal chunk; resolves when the receiver confirms file end. */
end(arrayLike?: number[] | Uint8Array): Promise<void>
}
/** A receiver-side offerable file (shown on a receive Session's "offer"). */
export interface Offer {
get_details(): FileDetails
get_offset(): number
/** Skip this file (not an error, unlike abort()). */
skip(): Promise<never>
/**
* Accept the offer. With { on_input: fn } the payload of every data
* subpacket is delivered to `fn` (streaming, no full spool in memory).
*/
accept(opts?: {
offset?: number
on_input?: 'spool_array' | 'spool_uint8array' | ((payload: Uint8Array) => void)
}): Promise<unknown>
on(event: 'input', cb: (payload: Uint8Array) => void): Offer
on(event: 'complete', cb: () => void): Offer
}
/** Base ZMODEM session (Send/Receive share this). */
export interface Session {
type: 'send' | 'receive'
set_sender(fn: (octets: number[]) => void): void
has_ended(): boolean
aborted(): boolean
/** Abort the transfer (sends the ZMODEM CAN sequence to the peer). */
abort(): void
/** Register a session_end handler (fires once the transfer is done). */
on(event: 'session_end', cb: () => void): void
/** Catch-all overload (keeps the Send|Receive union from collapsing to never). */
on(event: never, cb: (...args: unknown[]) => void): void
}
/** Send session (we upload, remote ran rz). */
export interface SendSession extends Session {
type: 'send'
/** Offer one file; resolves with the Transfer or undefined if skipped. */
send_offer(params: FileDetails): Promise<Transfer | undefined>
/** Ends a send session gracefully; resolves when the receiver acks ZFIN. */
close(): Promise<void>
}
/** Receive session (we download, remote ran sz). */
export interface ReceiveSession extends Session {
type: 'receive'
on(event: 'offer', cb: (offer: Offer) => void): void
/** Begin receiving: tells the peer we are ready for the first offer. */
start(): Promise<void>
}
export interface SentryOptions {
/** Non-ZMODEM octets to forward to the terminal. */
to_terminal(octets: number[]): void
/** Called with a Detection when a ZMODEM header is spotted. */
on_detect(detection: Detection): void
/** Called when a pending Detection is retracted (no session after all). */
on_retract(): void
/** Send octets to the peer (the ssh stream). */
sender(octets: number[]): void
}
export interface Detection {
/** Whether the detection is still valid (not retracted). */
is_valid(): boolean
/** Confirm the ZMODEM session and return the active Session object. */
confirm(): SendSession | ReceiveSession
/** Give the session's role: 'send' or 'receive'. */
get_session_role(): 'send' | 'receive'
/** Reject: tell the peer to abort the session. */
deny(): void
}
export const Sentry: new (options: SentryOptions) => Sentry
export interface Sentry {
consume(input: Uint8Array | ArrayBuffer | number[]): void
get_confirmed_session(): Session | null
}
export const ZMLIB: { ABORT_SEQUENCE: number[] }
export const ZDLE: new () => unknown
export const CRC: { crc16(input: ArrayLike<number>): number }
export const Header: Record<string, unknown>
export const Subpacket: Record<string, unknown>
export const Session: {
Send: new (zrinitHeader: unknown) => SendSession
Receive: new () => ReceiveSession
}
}