[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled

This commit is contained in:
2026-09-13 18:24:01 +08:00
commit 3c72efcb7f
3255 changed files with 907009 additions and 0 deletions

262
types/global/netcatty-bridge-ai.d.ts vendored Normal file
View File

@@ -0,0 +1,262 @@
import type { CodebuddyAdvancedOptions } from '../../infrastructure/ai/types';
declare global {
interface NetcattyBridge {
// AI / external agents
aiSyncProviders?(providers: Array<{ id: string; providerId: string; apiKey?: string; baseURL?: string; enabled: boolean }>): Promise<{ ok: boolean }>;
aiChatStream?(requestId: string, url: string, headers?: Record<string, string>, body?: string, providerId?: string, idleTimeoutMs?: number): Promise<{ ok: boolean; statusCode?: number; statusText?: string; error?: string; aborted?: boolean }>;
aiChatCancel?(requestId: string): Promise<boolean>;
aiFetch?(url: string, method?: string, headers?: Record<string, string>, body?: string, providerId?: string, skipHostCheck?: boolean, followRedirects?: boolean, skipTLSVerify?: boolean): Promise<{ ok: boolean; status?: number; data: string; error?: string }>;
aiAllowlistAddHost?(baseURL: string): Promise<{ ok: boolean; error?: string }>;
aiExec?(sessionId: string, command: string, chatSessionId?: string): Promise<{ ok: boolean; stdout?: string; stderr?: string; exitCode?: number | null; error?: string }>;
aiCattyCancelExec?(chatSessionId: string): Promise<{ ok: boolean; error?: string }>;
aiDiscoverAgents?(options?: { refreshShellEnv?: boolean; apiKeyPresent?: boolean }): Promise<Array<{
command: string;
name: string;
icon: string;
description: string;
args: string[];
path: string;
binPath?: string;
version: string;
available: boolean;
installed?: boolean;
authenticated?: boolean;
authSource?: string | null;
sdkBackend?: string;
/** @deprecated Legacy persisted field from the pre-SDK migration. */
acpCommand?: string;
acpArgs?: string[];
}>>;
aiPrewarmShellEnv?(): Promise<{ ok: boolean; error?: string }>;
aiCodexGetIntegration?(options?: { refreshShellEnv?: boolean; validateChatGptAuth?: boolean; codexPath?: string }): Promise<{
state: 'connected_chatgpt' | 'connected_api_key' | 'connected_custom_config' | 'not_logged_in' | 'unknown';
isConnected: boolean;
rawOutput: string;
exitCode: number | null;
customConfig?: {
providerName: string;
displayName: string;
baseUrl: string | null;
envKey: string | null;
envKeyPresent: boolean;
hasHardcodedApiKey: boolean;
model: string | null;
authHash: string | null;
} | null;
}>;
aiCodexStartLogin?(options?: { codexPath?: string }): Promise<{
ok: boolean;
session?: {
sessionId: string;
state: 'running' | 'success' | 'error' | 'cancelled';
url: string | null;
output: string;
error: string | null;
exitCode: number | null;
codexPath?: string | null;
};
error?: string;
}>;
aiCodexGetLoginSession?(sessionId: string): Promise<{
ok: boolean;
session?: {
sessionId: string;
state: 'running' | 'success' | 'error' | 'cancelled';
url: string | null;
output: string;
error: string | null;
exitCode: number | null;
codexPath?: string | null;
};
error?: string;
}>;
aiCodexCancelLogin?(sessionId: string): Promise<{
ok: boolean;
found?: boolean;
session?: {
sessionId: string;
state: 'running' | 'success' | 'error' | 'cancelled';
url: string | null;
output: string;
error: string | null;
exitCode: number | null;
codexPath?: string | null;
};
error?: string;
}>;
aiCodexLogout?(options?: { codexPath?: string }): Promise<{
ok: boolean;
state?: 'connected_chatgpt' | 'connected_api_key' | 'connected_custom_config' | 'not_logged_in' | 'unknown';
isConnected?: boolean;
rawOutput?: string;
logoutOutput?: string;
error?: string;
}>;
aiMcpUpdateSessions?(sessions: Array<{
sessionId: string;
hostId?: string;
hostname: string;
label: string;
os?: string;
username?: string;
protocol?: string;
shellType?: string;
deviceType?: string;
connected: boolean;
hostChain?: Array<{ hostId: string; label?: string; hostname?: string }>;
activePortForwards?: Array<{ ruleId: string; label?: string; type?: string; localPort?: number; status?: string }>;
}>, chatSessionId?: string): Promise<{ ok: boolean }>;
/** Update the app-owned live session snapshot used by existing AI scopes. */
aiMcpUpdateLiveSessions?(sessions: Array<{
sessionId: string;
hostId?: string;
hostname: string;
label: string;
os?: string;
username?: string;
protocol?: string;
shellType?: string;
deviceType?: string;
connected: boolean;
hostChain?: Array<{ hostId: string; label?: string; hostname?: string }>;
activePortForwards?: Array<{ ruleId: string; label?: string; type?: string; localPort?: number; status?: string }>;
}>): Promise<{ ok: boolean; count?: number; error?: string }>;
/** Merge sessions into a chat scope without dropping existing entries. */
aiMcpMergeSessions?(sessions: Array<{
sessionId: string;
hostId?: string;
hostname: string;
label: string;
os?: string;
username?: string;
protocol?: string;
shellType?: string;
deviceType?: string;
connected: boolean;
hostChain?: Array<{ hostId: string; label?: string; hostname?: string }>;
activePortForwards?: Array<{ ruleId: string; label?: string; type?: string; localPort?: number; status?: string }>;
}>, chatSessionId: string): Promise<{ ok: boolean; count?: number; error?: string }>;
onVaultAgentRequest?(cb: (payload: { requestId: string; op: string; params: Record<string, unknown> }) => void): () => void;
respondVaultAgent?(requestId: string, result: Record<string, unknown>): Promise<{ ok: boolean; error?: string }>;
aiMcpSetToolIntegrationMode?(mode: 'mcp' | 'skills'): Promise<{ ok: boolean; error?: string }>;
aiUserSkillsGetStatus?(): Promise<{
ok: boolean;
directoryPath?: string;
readyCount?: number;
warningCount?: number;
skills?: Array<{
id: string;
slug: string;
directoryName: string;
directoryPath: string;
skillPath: string;
name: string;
description: string;
status: 'ready' | 'warning';
warnings: string[];
}>;
warnings?: string[];
error?: string;
}>;
aiUserSkillsOpenFolder?(): Promise<{
ok: boolean;
directoryPath?: string;
readyCount?: number;
warningCount?: number;
skills?: Array<{
id: string;
slug: string;
directoryName: string;
directoryPath: string;
skillPath: string;
name: string;
description: string;
status: 'ready' | 'warning';
warnings: string[];
}>;
warnings?: string[];
error?: string;
}>;
aiUserSkillsBuildContext?(prompt: string, selectedSkillSlugs?: string[]): Promise<{
ok: boolean;
context?: string;
error?: string;
}>;
aiSkillsCliGetInvocation?(): Promise<{
ok: boolean;
skillPath?: string | null;
commandPrefix?: string;
launcherPath?: string | null;
usesLauncher?: boolean;
error?: string;
}>;
aiSdkAgentStream?(requestId: string, chatSessionId: string, sdkBackend: string, prompt: string, cwd?: string, providerId?: string, model?: string, existingSessionId?: string, historyMessages?: Array<{ role: 'user' | 'assistant'; content: string }>, images?: Array<{ base64Data: string; mediaType: string; filename?: string; filePath?: string }>, toolIntegrationMode?: 'mcp' | 'skills', defaultTargetSession?: { sessionId: string; hostname: string; label: string; os?: string; username?: string; protocol?: string; shellType?: string; deviceType?: string; connected: boolean; source: 'scope-target' | 'only-connected-in-scope' }, userSkillsContext?: string, agentEnv?: Record<string, string>, agentCommand?: string, codexRuntime?: 'sdk' | 'app-server', permissionMode?: 'observer' | 'confirm' | 'auto', codebuddyOptions?: CodebuddyAdvancedOptions): Promise<{ ok: boolean; error?: string }>;
aiSdkAgentSteer?(requestId: string, chatSessionId: string, prompt: string, images: Array<{ base64Data: string; mediaType: string; filename?: string; filePath?: string }> | undefined, clientUserMessageId: string): Promise<{
status: 'accepted' | 'not-steerable' | 'busy' | 'inactive' | 'unsupported' | 'cancelled' | 'failed';
message?: string;
turnKind?: 'review' | 'compact';
}>;
aiSdkAgentListModels?(sdkBackend: string, cwd?: string, providerId?: string, chatSessionId?: string, agentEnv?: Record<string, string>, agentCommand?: string, codexRuntime?: 'sdk' | 'app-server'): Promise<{ ok: boolean; models?: Array<{ id: string; name: string; description?: string; thinkingLevels?: string[]; defaultThinkingLevel?: string }>; currentModelId?: string | null; warning?: string; error?: string }>;
codexAppServerGetStatus?(agentCommand?: string, agentEnv?: Record<string, string>): Promise<{ ok: boolean; available: boolean; error?: string }>;
onCodexAppServerInteractionRequest?(cb: (payload: Record<string, unknown>) => void): () => void;
onCodexAppServerInteractionCleared?(cb: (payload: { interactionIds: string[]; chatSessionId?: string }) => void): () => void;
respondCodexAppServerInteraction?(payload: Record<string, unknown>): Promise<{ ok: boolean; error?: string }>;
cancelCodexAppServerInteractionTimeout?(interactionId: string): Promise<{ ok: boolean; cancelled?: boolean; error?: string }>;
aiCattyCancelExec?(chatSessionId: string): Promise<unknown>;
aiSetChatSessionCancelled?(chatSessionId: string, cancelled?: boolean): Promise<{ ok: boolean; error?: string }>;
aiMcpSyncPermissionGrants?(grants: Array<Record<string, unknown>>): Promise<{ ok: boolean; count?: number; error?: string }>;
externalMcpGetStatus?(): Promise<{
ok: boolean;
enabled?: boolean;
state?: string;
host?: string;
port?: number | null;
discoveryPath?: string | null;
launcherPath?: string | null;
chatSessionId?: string;
exposedSessionCount?: number;
mode?: 'temporary' | 'persistent';
idleTimeoutMinutes?: number;
sessionIdleTimeoutMinutes?: number;
lastActivityAt?: number | null;
idleExpiresAt?: number | null;
permissionMode?: string;
hostRunning?: boolean;
error?: string | null;
}>;
externalMcpSetEnabled?(enabled: boolean): Promise<Record<string, unknown>>;
externalMcpSetConfig?(config: {
mode?: 'temporary' | 'persistent';
idleTimeoutMinutes?: number;
sessionIdleTimeoutMinutes?: number;
}): Promise<Record<string, unknown>>;
externalMcpCodexGetStatus?(): Promise<Record<string, unknown>>;
externalMcpCodexAdd?(): Promise<Record<string, unknown>>;
externalMcpClaudeGetStatus?(): Promise<Record<string, unknown>>;
externalMcpClaudeAdd?(): Promise<Record<string, unknown>>;
externalMcpGrokGetStatus?(): Promise<Record<string, unknown>>;
externalMcpGrokAdd?(): Promise<Record<string, unknown>>;
aiSdkAgentCancel?(requestId: string, chatSessionId?: string): Promise<{ ok: boolean; error?: string }>;
aiSdkAgentCleanup?(chatSessionId: string): Promise<{ ok: boolean }>;
aiSdkAgentElicitationResponse?(elicitationId: string, action: string, content?: Record<string, unknown>): Promise<{ ok: boolean; error?: string }>;
aiSdkAgentMcpStatus?(agentEnv?: Record<string, string>, agentCommand?: string): Promise<{ ok: boolean; servers?: Array<Record<string, unknown>>; error?: string }>;
aiSdkAgentAccountInfo?(agentEnv?: Record<string, string>, agentCommand?: string): Promise<{ ok: boolean; account?: Record<string, unknown> | null; error?: string }>;
aiSdkAgentPluginInstall?(options: { name: string; marketplace: string }): Promise<{ ok: boolean; result?: { success: boolean; message: string }; error?: string }>;
aiSdkAgentPluginEnable?(name: string, marketplace: string): Promise<{ ok: boolean; result?: { success: boolean; message: string }; error?: string }>;
aiSdkAgentPluginDisable?(name: string, marketplace: string): Promise<{ ok: boolean; result?: { success: boolean; message: string }; error?: string }>;
aiSdkAgentMarketplaceInstall?(options: { name: string; repo: string; autoUpdate?: boolean }): Promise<{ ok: boolean; result?: { success: boolean; message: string }; error?: string }>;
aiSdkAgentMarketplaceRemove?(options: { name: string; removePlugins?: boolean }): Promise<{ ok: boolean; result?: { success: boolean; message: string }; error?: string }>;
onAiSdkAgentEvent?(requestId: string, cb: (event: Record<string, unknown>) => void): () => void;
onAiSdkAgentDone?(requestId: string, cb: () => void): () => void;
onAiSdkAgentError?(requestId: string, cb: (error: string) => void): () => void;
onAiStreamData?(requestId: string, cb: (data: string) => void): () => void;
onAiStreamEnd?(requestId: string, cb: () => void): () => void;
onAiStreamError?(requestId: string, cb: (error: string) => void): () => void;
onAiAgentStdout?(agentId: string, cb: (data: string) => void): () => void;
onAiAgentStderr?(agentId: string, cb: (data: string) => void): () => void;
onAiAgentExit?(agentId: string, cb: (code: number | null) => void): () => void;
}
}
export {};

131
types/global/netcatty-bridge-app.d.ts vendored Normal file
View File

@@ -0,0 +1,131 @@
declare global {
interface NetcattyBridge {
// Auto-update
checkForUpdate?(): Promise<{
available: boolean;
supported?: boolean;
checking?: boolean;
version?: string;
releaseNotes?: string;
releaseDate?: string | null;
error?: string;
}>;
downloadUpdate?(): Promise<{ success: boolean; error?: string }>;
installUpdate?(): void;
getUpdateStatus?(): Promise<{ status: 'idle' | 'available' | 'downloading' | 'ready' | 'error'; percent: number; error: string | null; version: string | null; isChecking?: boolean }>;
onUpdateDownloadProgress?(cb: (progress: {
percent: number;
bytesPerSecond: number;
transferred: number;
total: number;
}) => void): () => void;
onUpdateAvailable?(cb: (info: {
version: string;
releaseNotes: string;
releaseDate: string | null;
}) => void): () => void;
onUpdateNotAvailable?(cb: () => void): () => void;
onUpdateDownloaded?(cb: () => void): () => void;
onUpdateError?(cb: (payload: { error: string }) => void): () => void;
// Fired when an install was requested but blocked by unsaved editors (#1215).
onUpdateNeedsSave?(cb: () => void): () => void;
onSshDeepLink?(cb: (payload: { url?: string }) => void): () => void;
onTelnetDeepLink?(cb: (payload: { url?: string }) => void): () => void;
onOpenTerminalPath?(cb: (payload: { path?: string }) => void): () => void;
/** Fired once after cold-start deep-link / open-terminal queues have been drained. */
onColdStartIntentsSettled?(cb: () => void): () => void;
setSshDeepLinkEnabled?(enabled: boolean): Promise<boolean | { success: boolean; enabled: boolean }>;
getSshDeepLinkEnabled?(): Promise<boolean>;
onJmsDeepLink?(cb: (payload: { url?: string }) => void): () => void;
setJmsDeepLinkEnabled?(enabled: boolean): Promise<boolean | { success: boolean; enabled: boolean }>;
getJmsDeepLinkEnabled?(): Promise<boolean>;
setExplorerContextMenuEnabled?(enabled: boolean): Promise<boolean | { success: boolean; enabled: boolean; supported?: boolean }>;
getExplorerContextMenuEnabled?(): Promise<boolean | { enabled: boolean; supported?: boolean }>;
// Global Toggle Hotkey (Quake Mode)
registerGlobalHotkey?(hotkey: string): Promise<{ success: boolean; enabled?: boolean; error?: string; accelerator?: string }>;
unregisterGlobalHotkey?(): Promise<{ success: boolean }>;
getGlobalHotkeyStatus?(): Promise<{ enabled: boolean; hotkey: string | null }>;
// Auto-Update toggle
getAutoUpdate?(): Promise<{ enabled: boolean }>;
setAutoUpdate?(enabled: boolean): Promise<{ success: boolean }>;
// SSH diagnostic logs
getSshDebugLogInfo?(): Promise<{
enabled: boolean;
path: string;
exists: boolean;
size: number;
}>;
openSshDebugLogDir?(): Promise<{ success: boolean; error?: string }>;
// System Tray / Close to Tray
setCloseToTray?(enabled: boolean): Promise<{ success: boolean; enabled: boolean }>;
isCloseToTray?(): Promise<{ enabled: boolean }>;
// Auto Launch at system login (hidden to tray)
getAutoLaunch?(): Promise<{ success: boolean; enabled: boolean; supported: boolean }>;
setAutoLaunch?(enabled: boolean): Promise<{ success: boolean; enabled: boolean; supported: boolean }>;
// App-level HTTP(S) network proxy (cloud sync / AI — not SSH ProxyJump)
setHttpNetworkProxy?(settings: {
mode: 'system' | 'direct' | 'custom';
url: string;
bypass: string;
}): Promise<{
success: boolean;
settings: { mode: 'system' | 'direct' | 'custom'; url: string; bypass: string };
electronConfig?: unknown;
}>;
getHttpNetworkProxy?(): Promise<{
settings: { mode: 'system' | 'direct' | 'custom'; url: string; bypass: string };
}>;
updateTrayMenuData?(data: {
sessions?: Array<{ id: string; label: string; hostLabel: string; status: "connecting" | "connected" | "disconnected"; workspaceId?: string; workspaceTitle?: string }>;
hosts?: Array<{ id: string; label?: string; hostname?: string; group?: string; pinned?: boolean; lastConnectedAt?: number; protocol?: string }>;
portForwardRules?: Array<{
id: string;
label: string;
type: "local" | "remote" | "dynamic";
localPort: number;
remoteHost?: string;
remotePort?: number;
status: "inactive" | "connecting" | "active" | "error";
}>;
}): Promise<{ success: boolean }>;
onTrayFocusSession?(callback: (sessionId: string) => void): () => void;
onTrayTogglePortForward?(callback: (ruleId: string, start: boolean) => void): () => void;
onTrayPanelJumpToSession?(callback: (sessionId: string) => void): () => void;
onTrayPanelConnectToHost?(callback: (hostId: string) => void): () => void;
onTrayPanelCloseSession?(callback: (sessionId: string) => void): () => void;
hideTrayPanel?(): Promise<{ success: boolean }>;
openMainWindow?(): Promise<{ success: boolean }>;
quitApp?(): Promise<{ success: boolean }>;
jumpToSessionFromTrayPanel?(sessionId: string): Promise<{ success: boolean }>;
connectToHostFromTrayPanel?(hostId: string): Promise<{ success: boolean }>;
closeSessionFromTrayPanel?(sessionId: string): Promise<{ success: boolean }>;
onTrayPanelCloseRequest?(callback: () => void): () => void;
onTrayPanelRefresh?(callback: () => void): () => void;
onTrayPanelMenuData?(callback: (data: {
sessions?: Array<{ id: string; label: string; hostLabel: string; status: "connecting" | "connected" | "disconnected"; workspaceId?: string; workspaceTitle?: string }>;
hosts?: Array<{ id: string; label?: string; hostname?: string; group?: string; pinned?: boolean; lastConnectedAt?: number; protocol?: string }>;
portForwardRules?: Array<{
id: string;
label: string;
type: "local" | "remote" | "dynamic";
localPort: number;
remoteHost?: string;
remotePort?: number;
status: "inactive" | "connecting" | "active" | "error";
hostId?: string;
}>;
}) => void): () => void;
}
}
export {};

152
types/global/netcatty-bridge-files.d.ts vendored Normal file
View File

@@ -0,0 +1,152 @@
import type { SftpFilenameEncoding } from "../../types";
declare global {
interface NetcattyBridge {
// File opener helpers (for "Open With" feature)
selectApplication?(): Promise<{ path: string; name: string } | null>;
openWithApplication?(filePath: string, appPath: string): Promise<boolean>;
openWithSystemDefault?(filePath: string): Promise<{ success: boolean; error?: string }>;
downloadSftpToTempWithProgress?(
sftpId: string,
remotePath: string,
fileName: string,
encoding: SftpFilenameEncoding | undefined,
transferId: string
): Promise<{ localPath: string; cancelled: boolean }>;
// Save dialog for file downloads
showSaveDialog?(defaultPath: string, filters?: Array<{ name: string; extensions: string[] }>): Promise<string | null>;
selectDirectory?(title?: string, defaultPath?: string): Promise<string | null>;
selectFile?(title?: string, defaultPath?: string, filters?: Array<{ name: string; extensions: string[] }>): Promise<string | null>;
// File watcher for auto-sync feature
startFileWatch?(localPath: string, remotePath: string, sftpId: string, encoding?: SftpFilenameEncoding): Promise<{ watchId: string }>;
stopFileWatch?(watchId: string, cleanupTempFile?: boolean): Promise<{ success: boolean }>;
listFileWatches?(): Promise<Array<{ watchId: string; localPath: string; remotePath: string; sftpId: string }>>;
registerTempFile?(sftpId: string, localPath: string): Promise<{ success: boolean }>;
unregisterTempFile?(sftpId: string, localPath: string): Promise<{ success: boolean; retained?: boolean }>;
onFileWatchSynced?(cb: (payload: { watchId: string; localPath: string; remotePath: string; bytesWritten: number }) => void): () => void;
onFileWatchError?(cb: (payload: { watchId: string; localPath: string; remotePath: string; error: string }) => void): () => void;
onFileWatchStopped?(cb: (payload: { watchId: string; localPath: string; remotePath: string; sftpId: string }) => void): () => void;
// Temp file cleanup
deleteTempFile?(filePath: string): Promise<{ success: boolean }>;
stageUploadFile?(file: File, transferId: string): Promise<string>;
cancelStagedUploadFile?(transferId: string): Promise<{ success: boolean }>;
// Crash Logs
getCrashLogs?(): Promise<Array<{ fileName: string; date: string; size: number; entryCount: number }>>;
readCrashLog?(fileName: string): Promise<Array<{
timestamp: string;
source: string;
message: string;
stack?: string;
errorMeta?: Record<string, unknown>;
extra?: Record<string, unknown>;
pid?: number;
platform?: string;
arch?: string;
version?: string;
electronVersion?: string;
osVersion?: string;
memoryMB?: { rss: number; heapUsed: number; heapTotal: number };
activeSessionCount?: number;
uptimeSeconds?: number;
}>>;
clearCrashLogs?(): Promise<{ deletedCount: number }>;
openCrashLogsDir?(): Promise<{ success: boolean }>;
// Temp directory management
getTempDirInfo?(): Promise<{ path: string; fileCount: number; totalSize: number }>;
clearTempDir?(): Promise<{ deletedCount: number; failedCount: number; error?: string }>;
getTempDirPath?(): Promise<string>;
openTempDir?(): Promise<{ success: boolean }>;
getToolOutputPersistenceStatus?(): Promise<{ durable: boolean; reason?: string }>;
writeToolOutputTemp?(record: import('../../infrastructure/ai/harness/toolOutputStore').PersistedToolOutputRecord, content: string): Promise<{ ok: boolean; path?: string; manifestPath?: string; error?: string }>;
restoreToolOutputTemp?(handleId: string, chatSessionId: string): Promise<{
path: string;
record: import('../../infrastructure/ai/harness/toolOutputStore').PersistedToolOutputRecord;
} | null>;
readToolOutputTemp?(filePath: string, request?: {
mode?: 'head' | 'tail' | 'full' | 'range' | 'search';
maxChars?: number;
offset?: number;
query?: string;
}): Promise<unknown | null>;
deleteToolOutputTemp?(filePath: string): Promise<{ ok: boolean }>;
deleteChatToolOutputsTemp?(chatSessionId: string): Promise<{ deletedCount: number }>;
deleteTerminalToolOutputsTemp?(chatSessionId: string, terminalSessionId: string): Promise<{ deletedCount: number }>;
deleteTerminalToolOutputsEverywhereTemp?(terminalSessionId: string): Promise<{ deletedCount: number }>;
// Session Logs
exportSessionLog?(payload: {
terminalData: string;
hostLabel: string;
hostname: string;
startTime: number;
format: 'txt' | 'raw' | 'html';
}): Promise<{ success: boolean; canceled?: boolean; filePath?: string }>;
selectSessionLogsDir?(): Promise<{ success: boolean; canceled?: boolean; directory?: string }>;
autoSaveSessionLog?(payload: {
terminalData: string;
hostLabel: string;
hostname: string;
hostId: string;
startTime: number;
format: 'txt' | 'raw' | 'html';
directory: string;
}): Promise<{ success: boolean; error?: string; filePath?: string }>;
openSessionLogsDir?(directory: string): Promise<{ success: boolean; error?: string }>;
clearSessionLogsDir?(directory: string): Promise<{ success: boolean; deletedCount: number; failedCount: number; error?: string }>;
chooseManualSessionLogPath?(payload: {
sessionId: string;
sessionName?: string;
preferredDirectory?: string;
format?: 'txt' | 'raw' | 'html';
}): Promise<{
success: boolean;
canceled?: boolean;
error?: string;
selectionToken?: string;
filePath?: string;
format?: 'txt' | 'raw' | 'html';
}>;
startManualSessionLog?(payload: {
sessionId: string;
sessionName?: string;
preferredDirectory?: string;
/** Opaque token from chooseManualSessionLogPath (path is main-process only). */
selectionToken?: string;
format?: 'txt' | 'raw' | 'html';
timestampsEnabled?: boolean;
initialLine?: string;
alternateScreenActive?: boolean;
}): Promise<{ success: boolean; started: boolean; canceled?: boolean; error?: string; filePath?: string }>;
stopManualSessionLog?(payload: {
sessionId: string;
}): Promise<{ success: boolean; stopped: boolean; error?: string; filePath?: string }>;
getManualSessionLogStatus?(payload: {
sessionId: string;
}): Promise<{ success: boolean; isLogging: boolean; error?: string }>;
// Get file path from File object (for drag-and-drop, uses Electron's webUtils)
getPathForFile?(file: File): string | undefined;
showSystemNotification?(payload: {
title: string;
body: string;
sessionId?: string;
}): Promise<{ shown: boolean; reason?: string }>;
readClipboardText?(): Promise<string>;
writeClipboardText?(text: string): Promise<boolean>;
readClipboardFiles?(): Promise<Array<{ path: string; name: string; isDirectory: boolean; size?: number }>>;
readClipboardImage?(): Promise<{ path: string; name: string; mediaType: string; size?: number } | null>;
hasClipboardImage?(): Promise<boolean>;
// Credential encryption (field-level safeStorage for sensitive data at rest)
credentialsAvailable?(): Promise<boolean>;
credentialsEncrypt?(plaintext: string): Promise<string>;
credentialsDecrypt?(value: string): Promise<string>;
}
}
export {};

172
types/global/netcatty-bridge-script.d.ts vendored Normal file
View File

@@ -0,0 +1,172 @@
/// <reference path="./netcatty-bridge-script.d.ts" />
export type ScriptRunStatus = 'running' | 'paused' | 'completed' | 'failed';
export type ScriptProgressMode = 'activity' | 'determinate';
export interface ScriptRunLogEntry {
at: number;
message: string;
}
export interface ScriptRun {
runId: string;
scriptId?: string;
scriptLabel?: string;
sessionId: string;
status: ScriptRunStatus;
startedAt: number;
endedAt?: number;
/** @deprecated Use activityLabel for UI; kept for backward compatibility */
currentStep?: string;
stepIndex?: number;
/** Internal telemetry only; do not use for overlay percentage */
totalSteps?: number;
progressMode?: ScriptProgressMode;
activityLabel?: string;
progressLabel?: string;
progressCurrent?: number;
progressTotal?: number;
elapsedMs?: number;
waitingFor?: string;
logs: ScriptRunLogEntry[];
error?: string;
}
export interface ScriptScreenSnapshot {
rows: number;
cols: number;
currentRow: number;
lines: string[];
}
export interface ScriptDialogOption {
label: string;
value: string;
description?: string;
disabled?: boolean;
}
export type ScriptDialogConditionValue = string | number | boolean;
export type ScriptDialogCondition =
| { field: string; equals: ScriptDialogConditionValue }
| { field: string; notEquals: ScriptDialogConditionValue }
| { field: string; truthy: true }
| { field: string; falsy: true };
export interface ScriptDialogFieldBase {
name: string;
label: string;
description?: string;
required?: boolean;
visibleWhen?: ScriptDialogCondition;
}
export interface ScriptDialogChoiceField extends ScriptDialogFieldBase {
type: 'select' | 'radio';
options: ScriptDialogOption[];
defaultValue: string;
}
export interface ScriptDialogCheckboxField extends ScriptDialogFieldBase {
type: 'checkbox';
defaultValue: boolean;
}
export interface ScriptDialogTextareaField extends ScriptDialogFieldBase {
type: 'textarea';
defaultValue: string;
placeholder?: string;
}
export interface ScriptDialogNumberField extends ScriptDialogFieldBase {
type: 'number';
defaultValue?: number;
placeholder?: string;
min?: number;
max?: number;
step?: number;
}
export type ScriptDialogField =
| ScriptDialogChoiceField
| ScriptDialogCheckboxField
| ScriptDialogTextareaField
| ScriptDialogNumberField;
export interface ScriptDialogForm {
title?: string;
message: string;
submitLabel?: string;
cancelLabel?: string;
fields: ScriptDialogField[];
}
export type ScriptDialogFormValue = string | boolean | number | undefined;
export interface ScriptDialogRequest {
requestId: string;
type: 'alert' | 'confirm' | 'prompt' | 'waitForTimeout' | 'form';
message: string;
defaultValue?: string;
sensitive?: boolean;
pattern?: string;
timeoutMs?: number;
form?: ScriptDialogForm;
}
export interface ScriptRunParams {
/** Optional caller-generated id so a queued run can be cancelled before it starts. */
runId?: string;
/** Return after the run enters the backend queue instead of waiting for completion. */
returnWhenQueued?: boolean;
scriptId?: string;
scriptLabel?: string;
content: string;
sessionId?: string;
sessionIds?: string[];
mode?: 'sequential' | 'parallel';
permissionMode?: 'observer' | 'confirm' | 'auto';
/** Renderer-provided session state (worker SSH sessions are not in main-process map). */
sessionMeta?: {
connected?: boolean;
name?: string;
hostname?: string;
username?: string;
};
}
export type ScriptRecordingStep =
| { type: 'send'; value: string; sensitive?: boolean }
| { type: 'waitFor'; value: string; timeoutMs?: number }
| { type: 'waitForPrompt'; timeoutMs?: number }
| { type: 'sleep'; value: number };
declare global {
interface NetcattyBridge {
scriptRun(params: ScriptRunParams): Promise<{ runId: string; runIds: string[] }>;
scriptStop(runId: string): Promise<{ ok: boolean }>;
scriptPause(runId: string): Promise<{ ok: boolean }>;
scriptResume(runId: string): Promise<{ ok: boolean }>;
scriptGetRuns(sessionId?: string): Promise<ScriptRun[]>;
scriptDialogResponse(requestId: string, value?: unknown, cancelled?: boolean): Promise<{ ok: boolean }>;
scriptScreenSnapshotResponse(requestId: string, snapshot: ScriptScreenSnapshot): Promise<{ ok: boolean }>;
scriptRecordingStart(sessionId: string): Promise<{ ok: boolean }>;
scriptRecordingStop(sessionId: string): Promise<{ steps: ScriptRecordingStep[]; code: string }>;
scriptRecordingAppendStep(sessionId: string, step: ScriptRecordingStep): Promise<{
ok: boolean;
stopped?: boolean;
reason?: 'limit';
error?: string;
steps?: ScriptRecordingStep[];
code?: string;
}>;
onScriptRunsUpdated(cb: (payload: { runs: ScriptRun[] }) => void): () => void;
onScriptDialogRequest(cb: (payload: ScriptDialogRequest) => void): () => void;
onScriptScreenSnapshotRequest(cb: (payload: { requestId: string; sessionId: string }) => void): () => void;
onScriptSessionInput(cb: (payload: { sessionId: string; data: string }) => void): () => void;
}
}
export {};

View File

@@ -0,0 +1,620 @@
declare global {
interface NetcattyKittyKeyboardModeState {
mainFlags: number;
alternateFlags: number;
mainStack: number[];
alternateStack: number[];
alternateScreenActive: boolean;
}
interface NetcattyTerminalInterruptTrace {
debug?: boolean;
traceId?: string;
source?: string;
sessionId?: string;
rendererKeyAt?: number;
rendererSendAt?: number;
rendererStatus?: string;
rendererHasSelection?: boolean;
rendererPriority?: {
sessionId: string | null;
backlogBytes: number;
writeQueueDepth: number;
deferredAckBytes: number;
ackAfterInputBytes: number;
scheduledBackendResume: boolean;
skippedReason?: string;
};
}
interface NetcattyTerminalOutputPerfMeta {
id: string;
emittedAt: number;
sessionId?: string;
chars: number;
lineFeeds: number;
}
interface NetcattyBridge {
getWindowsPtyInfo?(): NetcattyWindowsPtyInfo | null;
startSSHSession(options: NetcattySSHOptions): Promise<string>;
startTelnetSession?(options: {
sessionId?: string;
hostname: string;
port?: number;
username?: string;
password?: string;
cols?: number;
rows?: number;
charset?: string;
env?: Record<string, string>;
sessionLog?: { enabled: boolean; directory: string; format: string; timestampsEnabled?: boolean };
}): Promise<string>;
startMoshSession?(options: {
sessionId?: string;
hostname: string;
username?: string;
password?: string;
privateKey?: string;
certificate?: string;
keyId?: string;
passphrase?: string;
authMethod?: import("../../domain/models").HostAuthMethod;
requiresMfa?: boolean;
identityFilePaths?: string[];
useSshAgent?: boolean;
agentPublicKeys?: string[];
identityAgent?: string;
identitiesOnly?: boolean;
addKeysToAgent?: string;
useKeychain?: boolean;
port?: number;
moshServerPath?: string;
moshClientPath?: string;
agentForwarding?: boolean;
sudoAutofillPassword?: string;
// Algorithm settings, forwarded so the host-info stats companion SSH
// connection (issue #1198) negotiates the same KEX / cipher / host-key
// set the interactive session would.
legacyAlgorithms?: boolean;
skipEcdsaHostKey?: boolean;
algorithmOverrides?: import("../../domain/models").HostAlgorithmOverrides;
// Known hosts, used to verify the host key before the stats companion
// connection (issue #1198) sends a saved password.
knownHosts?: import("../../domain/models").KnownHost[];
verifyHostKeys?: boolean;
cols?: number;
rows?: number;
charset?: string;
env?: Record<string, string>;
sessionLog?: { enabled: boolean; directory: string; format: string; timestampsEnabled?: boolean };
}): Promise<string>;
startEtSession?(options: {
sessionId?: string;
hostname: string;
hostId?: string;
username?: string;
password?: string;
privateKey?: string;
certificate?: string;
keyId?: string;
passphrase?: string;
authMethod?: import("../../domain/models").HostAuthMethod;
requiresMfa?: boolean;
identityFilePaths?: string[];
useSshAgent?: boolean;
agentPublicKeys?: string[];
identityAgent?: string;
identitiesOnly?: boolean;
addKeysToAgent?: string;
useKeychain?: boolean;
port?: number;
etPort?: number;
legacyAlgorithms?: boolean;
skipEcdsaHostKey?: boolean;
algorithmOverrides?: import("../../domain/models").HostAlgorithmOverrides;
knownHosts?: import("../../domain/models").KnownHost[];
verifyHostKeys?: boolean;
jumpHosts?: NetcattyJumpHost[];
agentForwarding?: boolean;
sudoAutofillPassword?: string;
cols?: number;
rows?: number;
charset?: string;
env?: Record<string, string>;
sessionLog?: { enabled: boolean; directory: string; format: string; timestampsEnabled?: boolean };
}): Promise<string>;
startLocalSession?(options: {
sessionId?: string;
cols?: number;
rows?: number;
shell?: string;
shellArgs?: string[];
cwd?: string;
env?: Record<string, string>;
sessionLog?: { enabled: boolean; directory: string; format: string; timestampsEnabled?: boolean };
bootEpoch?: number;
}): Promise<string>;
startSerialSession?(options: {
sessionId?: string;
path: string;
baudRate?: number;
dataBits?: 5 | 6 | 7 | 8;
stopBits?: 1 | 1.5 | 2;
parity?: 'none' | 'even' | 'odd' | 'mark' | 'space';
flowControl?: 'none' | 'xon/xoff' | 'rts/cts';
charset?: string;
sessionLog?: { enabled: boolean; directory: string; format: string; timestampsEnabled?: boolean };
}): Promise<string>;
listSerialPorts?(): Promise<Array<{
path: string;
manufacturer: string;
serialNumber: string;
vendorId: string;
productId: string;
pnpId: string;
}>>;
sendSerialYmodem?(sessionId: string, filePath: string): Promise<{
success: boolean;
fileName?: string;
totalBytes?: number;
writtenBytes?: number;
error?: string;
code?: string;
}>;
receiveSerialYmodem?(sessionId: string, destinationDir: string): Promise<{
success: boolean;
files?: Array<{
fileName: string;
filePath: string;
totalBytes: number;
writtenBytes: number;
}>;
fileCount?: number;
fileName?: string;
filePath?: string;
totalBytes?: number;
writtenBytes?: number;
error?: string;
code?: string;
}>;
getDefaultShell?(): Promise<string>;
discoverShells?(): Promise<DiscoveredShell[]>;
validatePath?(path: string, type?: 'file' | 'directory' | 'any'): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean; isExecutable: boolean }>;
generateKeyPair?(options: {
type: 'RSA' | 'ECDSA' | 'ED25519';
bits?: number;
comment?: string;
}): Promise<{ success: boolean; privateKey?: string; publicKey?: string; error?: string }>;
checkSshAgent?(options?: {
identityAgent?: string;
agentForwarding?: boolean;
hostname?: string;
port?: number;
username?: string;
}): Promise<{ running: boolean; startupType: string | null; error: string | null }>;
getDefaultKeys?(): Promise<Array<{ name: string; path: string }>>;
execCommand(options: {
hostname: string;
hostId?: string;
username: string;
port?: number;
authMethod?: import("../../domain/models").HostAuthMethod;
requiresMfa?: boolean;
password?: string;
privateKey?: string;
certificate?: string;
publicKey?: string;
keyId?: string;
keySource?: 'generated' | 'imported' | 'reference';
identityFilePaths?: string[];
useSshAgent?: boolean;
agentPublicKeys?: string[];
identityAgent?: string;
identitiesOnly?: boolean;
addKeysToAgent?: string;
useKeychain?: boolean;
passphrase?: string;
command: string;
timeout?: number;
sshTcpConnectTimeoutMs?: number;
sshAuthReadyTimeoutMs?: number;
enableKeyboardInteractive?: boolean;
sessionId?: string;
legacyAlgorithms?: boolean;
skipEcdsaHostKey?: boolean;
algorithmOverrides?: import("../../domain/models").HostAlgorithmOverrides;
}): Promise<{ stdout: string; stderr: string; code: number | null }>;
/** Get current working directory from an active SSH session */
getSessionPwd?(
sessionId: string,
options?: {
allowHomeFallback?: boolean;
allowLoginShellFallback?: boolean;
timeoutMs?: number;
},
): Promise<{ success: boolean; cwd?: string; error?: string }>;
/**
* Get metadata about an already-connected SSH session — currently the
* SSH server identification string (the `software` part of the
* SSH-2.0 banner). Used to classify network-device vendors from the
* banner without opening any additional exec channel.
*/
getSessionRemoteInfo?(sessionId: string): Promise<{
success: boolean;
remoteSshVersion?: string;
error?: string;
}>;
/**
* Probe the remote distro by running
* `cat /etc/os-release 2>/dev/null || uname -a` on the existing SSH
* connection's exec channel (not a brand-new connection). Used as a
* fallback when banner classification could not identify a network
* device vendor and we still want a distro-specific icon.
*/
getSessionDistroInfo?(sessionId: string): Promise<{
success: boolean;
stdout?: string;
stderr?: string;
error?: string;
}>;
/** Read the remote host's shell history file via an exec channel. */
readRemoteHistory?(sessionId: string, limit?: number): Promise<{
success: boolean;
pending?: boolean;
error?: string;
shell?: string;
bash?: string;
zsh?: string;
fish?: string;
}>;
/** Get server stats (CPU, Memory, Disk, Network) from an active SSH session */
getServerStats?(sessionId: string): Promise<{
success: boolean;
// Transient "not ready yet" (e.g. a Mosh session whose SSH handshake is
// still in progress, #1198). Callers should keep polling and NOT count
// this toward any consecutive-failure give-up.
pending?: boolean;
error?: string;
stats?: {
cpu: number | null; // CPU usage percentage (0-100)
cpuCores: number | null; // Number of CPU cores
cpuPerCore: number[]; // Per-core CPU usage array
memTotal: number | null; // Total memory in MB
memUsed: number | null; // Used memory in MB (excluding buffers/cache)
memFree: number | null; // Free memory in MB
memBuffers: number | null; // Buffers in MB
memCached: number | null; // Cached in MB
swapTotal: number | null; // Total swap in MB
swapUsed: number | null; // Used swap in MB
topProcesses: Array<{ // Top 10 processes by memory
pid: string;
memPercent: number;
command: string;
}>;
diskPercent: number | null; // Disk usage percentage for root partition
diskUsed: number | null; // Disk used in GB
diskTotal: number | null; // Total disk in GB
disks: Array<{ // All mounted disks
capacityKey?: string; // Filesystem or shared-pool identity
mountPoint: string;
used: number; // Used in GB
total: number; // Total in GB
percent: number; // Usage percentage
filesystemType?: string; // Filesystem type reported by df
}>;
netRxSpeed: number; // Total network receive speed (bytes/sec)
netTxSpeed: number; // Total network transmit speed (bytes/sec)
latencyMs: number | null; // TCP connection establishment latency to the SSH endpoint
netInterfaces: Array<{ // Per-interface network stats
name: string; // Interface name (e.g., eth0, ens33)
rxBytes: number; // Total received bytes
txBytes: number; // Total transmitted bytes
rxSpeed: number; // Receive speed (bytes/sec)
txSpeed: number; // Transmit speed (bytes/sec)
}>;
hostname?: string; // Hostname reported by the server
osName?: string; // Friendly OS name when available
kernelRelease?: string; // Kernel release from uname
uptimeSeconds?: number | null; // Server uptime in seconds
loadAverage?: number[]; // 1/5/15-minute load average
};
}>;
setSessionEncoding?(sessionId: string, encoding: string): Promise<{ ok: boolean; encoding: string }>;
writeToSession(
sessionId: string,
data: string,
options?: {
automated?: boolean;
/** Host-classified secret/no-echo input; always bypasses plugin observers and interceptors. */
sensitive?: boolean;
lineDelayMs?: number;
logRewrite?: { sentCommand: string; displayCommand: string };
},
): void;
interruptSession?(sessionId: string, trace?: NetcattyTerminalInterruptTrace): void;
resizeSession(sessionId: string, cols: number, rows: number): void;
/**
* Sync Windows ConPTY after the renderer clears the xterm viewport.
* No-op for SSH and non-ConPTY sessions.
*/
clearSessionPtyBuffer?(sessionId: string): void;
setSessionFlowPaused(sessionId: string, paused: boolean): void;
setSessionFlowPausedAndWait?(sessionId: string, paused: boolean): Promise<{ success: boolean; error?: string }>;
acquireSessionFlowPauseLease?(sessionId: string): Promise<{
success: boolean;
leaseId?: string;
error?: string;
}>;
waitSessionFlowPauseLease?(sessionId: string, leaseId: string): Promise<{
success: boolean;
error?: string;
}>;
releaseSessionFlowPauseLease?(
sessionId: string,
leaseId: string,
options?: { keepPaused?: boolean },
): Promise<{ success: boolean; error?: string }>;
onTerminalOutputDrainRequest?(
sessionId: string,
cb: (payload: { sessionId: string; requestId: string }) => void | Promise<void>,
): () => void;
respondTerminalOutputDrain?(requestId: string): void;
notifyTerminalSessionDisplayReady?(sessionId: string): void;
ackSessionFlow(sessionId: string, bytes: number): void;
closeSession(sessionId: string, options?: { bootEpoch?: number; retainOwnership?: boolean }): void | Promise<void>;
/** Move a live session's output port to this renderer (same PTY). */
rebindTerminalSessionOutput?(sessionId: string, authorization: string): Promise<{
success: boolean;
previousWebContentsId?: number | null;
webContentsId?: number;
error?: string;
}>;
/** Restore output after an attach popup closes. */
restoreTerminalSessionOutput?(
sessionId: string,
webContentsId?: number | null,
authorization?: string,
): Promise<{ success: boolean; restored?: boolean; webContentsId?: number; error?: string }>;
/** Ask the home renderer to serialize current terminal scrollback. */
requestTerminalSessionSnapshot?(sessionId: string, authorization: string): Promise<{
success: boolean;
snapshot?: string;
kittyKeyboardModeState?: NetcattyKittyKeyboardModeState;
kittyKeyboardProtocolEnabled?: boolean;
passwordPromptActive?: boolean;
cwd?: string | null;
title?: string | null;
error?: string;
}>;
/** Home renderer: listen for snapshot requests. */
onTerminalSessionSnapshotRequest?(
cb: (payload: { sessionId: string; requestId: string }) => void,
): () => void;
/** Home renderer: reply with serialized scrollback. */
respondTerminalSessionSnapshot?(
requestId: string,
snapshot: string,
kittyKeyboardModeState?: NetcattyKittyKeyboardModeState,
kittyKeyboardProtocolEnabled?: boolean,
passwordPromptActive?: boolean,
cwd?: string | null,
title?: string | null,
): void;
/** Observe popup: push current state back to the home renderer before restore. */
applyTerminalSessionSnapshot?(
sessionId: string,
snapshot: string,
context: {
contextSnapshot: string;
contextViewportSnapshot: string;
contextScrollbackSnapshot: string;
alternateScreen: boolean;
kittyKeyboardModeState?: NetcattyKittyKeyboardModeState;
kittyKeyboardProtocolEnabled?: boolean;
passwordPromptActive?: boolean;
cwd?: string | null;
title?: string | null;
},
authorization: string,
): Promise<{
success: boolean;
error?: string;
}>;
markAttachPopupClosePrepared?(sessionId: string, authorization: string): Promise<{ success: boolean; error?: string }>;
onTerminalPopupPrepareClose?(cb: (payload: { sessionId: string; authorization: string }) => void): () => void;
/** Home renderer: apply a pushed snapshot from an observe popup. */
onTerminalSessionApplySnapshot?(
cb: (payload: {
sessionId: string;
snapshot: string;
contextSnapshot: string;
contextViewportSnapshot: string;
contextScrollbackSnapshot: string;
alternateScreen: boolean;
kittyKeyboardModeState?: NetcattyKittyKeyboardModeState;
kittyKeyboardProtocolEnabled?: boolean;
passwordPromptActive?: boolean;
cwd?: string | null;
title?: string | null;
requestId: string;
}) => boolean | Promise<boolean>,
): () => void;
// ZMODEM file transfer
onZmodemEvent?(
sessionId: string,
cb: (event: {
type: 'detect' | 'progress' | 'complete' | 'error';
sessionId: string;
transferType?: 'upload' | 'download';
filename?: string;
transferred?: number;
total?: number;
fileIndex?: number;
fileCount?: number;
finalizing?: boolean;
error?: string;
}) => void
): () => void;
cancelZmodem?(sessionId: string, options?: { interrupt?: boolean }): void;
startZmodemDragDropUpload?(
sessionId: string,
files: Array<{
path?: string;
name: string;
remoteName: string;
data?: ArrayBuffer;
}>,
uploadCommand?: string,
): Promise<{ success: boolean; error?: string }>;
onZmodemOverwriteRequest?(
sessionId: string,
cb: (payload: { sessionId: string; requestId: string; filename: string }) => void
): () => void;
respondZmodemOverwrite?(payload: {
requestId: string;
action: "overwrite" | "skip" | "cancel";
applyToRest: boolean;
}): void;
onSessionData(
sessionId: string,
cb: (
data: string,
meta?: {
droppedOutputMayAffectTerminalState?: boolean;
droppedOutputAlternateScreenAction?: "enter" | "leave";
/** True while Mosh is still on the ephemeral SSH handshake PTY. */
moshHandshake?: boolean;
/** The Mosh SSH bootstrap is blocked on input that Netcatty cannot answer automatically. */
moshHandshakeRequiresUserInput?: boolean;
terminalPerf?: NetcattyTerminalOutputPerfMeta;
/** Original host output units acknowledged even when an interceptor changes display length. */
pluginPipelineIngressBytes?: number;
/** Host-owned provenance marker for output already processed by an interceptor. */
pluginPipelineProcessed?: boolean;
/** Host-classified authentication prompt state for protecting subsequent input. */
pluginPipelineSensitiveInput?: boolean;
/** Host-owned marker that a Plugin connection Provider has explicitly reached connected status. */
pluginConnectionReady?: boolean;
},
) => void,
options?: { replayBacklog?: boolean },
): () => void;
onSessionExit(
sessionId: string,
cb: (evt: { exitCode?: number; signal?: number; error?: string; reason?: "exited" | "error" | "timeout" | "closed" }) => void
): () => void;
onTelnetAutoLoginComplete?(
sessionId: string,
cb: (evt: { sessionId: string; bootEpoch?: number }) => void
): () => void;
onTelnetAutoLoginCancelled?(
sessionId: string,
cb: (evt: { sessionId: string; bootEpoch?: number }) => void
): () => void;
/** Fires after Mosh swaps from the SSH handshake PTY to mosh-client. */
onMoshSessionReady?(
sessionId: string,
cb: (evt: { sessionId: string; bootEpoch?: number }) => void
): () => void;
onTelnetEchoMode?(
sessionId: string,
cb: (evt: { sessionId: string; remoteEcho: boolean; localEcho: boolean }) => void
): () => void;
getTelnetEchoMode?(sessionId: string): Promise<{
success: boolean;
sessionId?: string;
remoteEcho?: boolean;
localEcho?: boolean;
error?: string;
}>;
onAuthFailed?(
sessionId: string,
cb: (evt: { sessionId: string; error: string; hostname: string }) => void
): () => void;
// Keyboard-interactive authentication (2FA/MFA)
onKeyboardInteractive?(
cb: (request: {
requestId: string;
sessionId: string;
hostId?: string;
name: string;
instructions: string;
prompts: Array<{ prompt: string; echo: boolean }>;
hostname: string;
savedPassword?: string | null;
/** When false, UI must not offer saving the response as the host password. */
allowSavePassword?: boolean;
scope?: "terminal" | "external";
bootEpoch?: number;
}) => void
): () => void;
onKeyboardInteractiveCancelled?(
cb: (event: {
requestId: string;
sessionId?: string;
reason?: string;
}) => void
): () => void;
respondKeyboardInteractive?(
requestId: string,
responses: string[],
cancelled?: boolean
): Promise<{ success: boolean; error?: string }>;
onHostKeyVerification?(
cb: (request: {
requestId: string;
sessionId: string;
hostname: string;
port: number;
status: 'unknown' | 'changed';
keyType: string;
fingerprint: string;
publicKey?: string;
knownHostId?: string;
knownFingerprint?: string;
bootEpoch?: number;
}) => void
): () => void;
respondHostKeyVerification?(
requestId: string,
accept: boolean,
addToKnownHosts?: boolean
): Promise<{ success: boolean; error?: string }>;
// Passphrase request for encrypted SSH keys
onPassphraseRequest?(
cb: (request: {
requestId: string;
keyPath: string;
keyName: string;
hostname?: string;
passphraseInvalid?: boolean;
sessionId?: string;
bootEpoch?: number;
}) => void
): () => void;
respondPassphrase?(
requestId: string,
passphrase: string,
cancelled?: boolean
): Promise<{ success: boolean; error?: string }>;
respondPassphraseSkip?(
requestId: string
): Promise<{ success: boolean; error?: string }>;
onPassphraseTimeout?(
cb: (event: { requestId: string }) => void
): () => void;
onPassphraseCancelled?(
cb: (event: { requestId: string; reason?: string }) => void
): () => void;
onPassphraseAuthFailed?(
cb: (event: { keyPaths: string[]; keyIds?: string[] }) => void
): () => void;
}
}
export {};

197
types/global/netcatty-bridge-sftp.d.ts vendored Normal file
View File

@@ -0,0 +1,197 @@
import type { RemoteFile, SftpFilenameEncoding, TransferDirection } from "../../types";
declare global {
interface NetcattyBridge {
// SFTP operations
openSftp(options: NetcattySSHOptions): Promise<string>;
openSftpForSession?(sessionId: string, options?: NetcattySSHOptions): Promise<string>;
listSftp(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<RemoteFile[]>;
realpathSftp?(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<string>;
readSftp(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<string>;
readSftpBinary?(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<ArrayBuffer>;
writeSftp(sftpId: string, path: string, content: string, encoding?: SftpFilenameEncoding): Promise<void>;
writeSftpBinary?(sftpId: string, path: string, content: ArrayBuffer, encoding?: SftpFilenameEncoding): Promise<void>;
closeSftp(sftpId: string): Promise<void | { success?: boolean; deferred?: boolean; leaseCount?: number }>;
retainSftpTransferSession?(sftpId: string, leaseId: string): Promise<{ success: boolean; reason?: string }>;
releaseSftpTransferSession?(sftpId: string, leaseId: string): Promise<{ success: boolean; reason?: string }>;
mkdirSftp(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<void>;
deleteSftp?(
sftpId: string,
path: string,
encoding?: SftpFilenameEncoding,
expectedType?: SftpStatResult["type"],
): Promise<void>;
renameSftp?(sftpId: string, oldPath: string, newPath: string, encoding?: SftpFilenameEncoding): Promise<void>;
statSftp?(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<SftpStatResult>;
/** No-follow remote metadata for conflict detection (symlink vs target). Missing path → null. */
lstatSftp?(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<SftpStatResult | null>;
chmodSftp?(sftpId: string, path: string, mode: string, encoding?: SftpFilenameEncoding): Promise<void>;
/** Extract a remote archive into its parent directory via SSH exec. */
extractSftpArchive?(sftpId: string, path: string, encoding?: SftpFilenameEncoding): Promise<{ success: boolean }>;
getSftpHomeDir?(sftpId: string, encoding?: SftpFilenameEncoding): Promise<{ success: boolean; homeDir?: string; error?: string }>;
// Transfer with progress
cancelTransfer?(transferId: string): Promise<void>;
/** Clear a pre-start cancel latch so intentional same-id resume/retry can run. */
clearPendingTransferCancel?(transferId: string): Promise<{ success: boolean } | void>;
sameHostCopyDirectory?(sftpId: string, sourcePath: string, targetPath: string, encoding?: SftpFilenameEncoding, transferId?: string): Promise<{ success: boolean }>;
// Compressed folder upload
startCompressedUpload?(
options: {
compressionId: string;
folderPath: string;
targetPath: string;
sftpId: string;
folderName: string;
totalBytes: number;
}
): Promise<{ compressionId: string; success?: boolean; error?: string }>;
cancelCompressedUpload?(compressionId: string): Promise<{ success: boolean }>;
pauseCompressedUpload?(compressionId: string): Promise<{ success: boolean; deferred?: boolean; lifecycleEpoch?: number; reason?: string }>;
resumeCompressedUpload?(compressionId: string): Promise<{ success: boolean; lifecycleEpoch?: number; reason?: string }>;
checkCompressedUploadSupport?(sftpId: string): Promise<{
supported: boolean;
localTar: boolean;
remoteTar: boolean;
error?: string;
}>;
// Streaming transfer with real progress and cancellation
startStreamTransfer?(
options: {
transferId: string;
sourcePath: string;
targetPath: string;
sourceType: 'local' | 'sftp';
targetType: 'local' | 'sftp';
sourceSftpId?: string;
targetSftpId?: string;
sourceHostId?: string;
targetHostId?: string;
parentTaskId?: string;
directoryEntryIndex?: number;
directoryEntryIdentity?: string;
totalBytes?: number;
sourceEncoding?: SftpFilenameEncoding;
targetEncoding?: SftpFilenameEncoding;
sameHost?: boolean;
resumable?: boolean;
checkpointBytes?: number;
resumeStage?: 'direct' | 'download' | 'upload';
downloadCheckpointBytes?: number;
uploadCheckpointBytes?: number;
sourceFingerprint?: string;
lifecycleEpoch?: number;
lifecycleState?: 'queued' | 'pausing' | 'paused' | 'transferring';
pauseUnavailableReason?: string;
globalConcurrency?: number;
/** When true, skip main-process admission (renderer already scheduled). */
skipAdmission?: boolean;
}
): Promise<{ transferId: string; totalBytes?: number; error?: string; cancelled?: boolean }>;
pauseTransfer?(transferId: string): Promise<{
success: boolean;
superseded?: boolean;
supersededBy?: "pause" | "resume" | "cancel";
checkpointBytes?: number;
resumeStage?: 'direct' | 'download' | 'upload';
downloadCheckpointBytes?: number;
uploadCheckpointBytes?: number;
sourceFingerprint?: string;
lifecycleEpoch?: number;
reason?: string;
}>;
resumeTransfer?(transferId: string): Promise<{ success: boolean; reason?: string; lifecycleEpoch?: number; superseded?: boolean; supersededBy?: "pause" | "resume" | "cancel" }>;
prioritizeTransfer?(transferId: string): Promise<{ success: boolean }>;
setGlobalTransferConcurrency?(limit: number): Promise<{ success: boolean; limit: number }>;
cleanupTransferArtifacts?(payload: {
transferId: string;
sourcePath: string;
targetPath: string;
targetSftpId?: string;
targetEncoding?: SftpFilenameEncoding;
stagedTargetPath?: string;
}): Promise<{ success: boolean }>;
onGlobalSftpTransferEvent?(callback: (event: {
type: 'queued' | 'started' | 'progress' | 'pausing' | 'paused' | 'resumed' | 'cancelled' | 'completed' | 'failed';
transferId: string;
direction?: TransferDirection;
fileName?: string;
sourcePath?: string;
targetPath?: string;
startedAt?: number;
endedAt?: number;
error?: string;
transferred?: number;
totalBytes?: number;
speed?: number;
checkpointBytes?: number;
resumeStage?: 'direct' | 'download' | 'upload';
downloadCheckpointBytes?: number;
uploadCheckpointBytes?: number;
sourceFingerprint?: string;
isDirectory?: boolean;
controlKind?: 'stream' | 'compressed-upload';
phase?: 'scanning' | 'compressing' | 'uploading' | 'transferring' | 'extracting' | 'verifying';
sessionId?: string;
sourceHostId?: string;
targetHostId?: string;
parentTaskId?: string;
directoryEntryIndex?: number;
directoryEntryIdentity?: string;
lifecycleEpoch?: number;
lifecycleState?: 'queued' | 'pausing' | 'paused' | 'transferring';
resumable?: boolean;
pauseUnavailableReason?: string;
}) => void): () => void;
// Local filesystem operations
listLocalDir?(path: string): Promise<RemoteFile[]>;
readLocalFile?(path: string, options?: { maxBytes?: number }): Promise<ArrayBuffer>;
writeLocalFile?(path: string, content: ArrayBuffer): Promise<void>;
deleteLocalFile?(path: string, expectedType?: SftpStatResult["type"]): Promise<void>;
renameLocalFile?(oldPath: string, newPath: string): Promise<void>;
extractLocalArchive?(path: string): Promise<{ success: boolean }>;
mkdirLocal?(path: string): Promise<void>;
statLocal?(path: string): Promise<SftpStatResult>;
/** No-follow local metadata for conflict detection (symlink vs target). */
lstatLocal?(path: string): Promise<SftpStatResult>;
listLocalTree?(
path: string,
options?: {
onProgress?: (progress: {
fileCount: number;
directoryCount: number;
entryCount: number;
}) => void;
/** Stream discovered rows while the walk continues (edge-scan/upload). */
onEntries?: (entries: Array<{
localPath: string;
relativePath: string;
type: 'file' | 'directory';
size: number;
lastModified: number;
}>) => void;
/** Renderer-generated ID used by cancelLocalTreeScan. */
scanId?: string;
limits?: {
maxDirectories?: number;
maxEntries?: number;
};
},
): Promise<Array<{
localPath: string;
relativePath: string;
type: 'file' | 'directory';
size: number;
lastModified: number;
}>>;
cancelLocalTreeScan?(scanId: string): Promise<void>;
getHomeDir?(): Promise<string>;
listDrives?(): Promise<string[]>;
getSystemInfo?(): Promise<{ username: string; hostname: string }>;
}
}
export {};

364
types/global/netcatty-bridge-sync.d.ts vendored Normal file
View File

@@ -0,0 +1,364 @@
import type { S3Config, SyncedFile, WebDAVConfig } from "../../domain/sync";
import type { AppLockSettings } from "../../domain/appLock";
type AppLockRuntimeReason = 'startup' | 'idle' | 'manual' | 'background' | null;
interface AppLockRuntimeState {
initialized: boolean;
locked: boolean;
reason: AppLockRuntimeReason;
version: number;
lastLockedAt: number | null;
lastUnlockedAt: number | null;
lastActivityAt: number | null;
}
type AppLockSettingsMutationError =
| { ok: false; error: 'empty-current' | 'empty-next' | 'incorrect' };
type AppLockUnlockResult =
| { ok: true }
| { ok: false; error: 'empty' | 'incorrect' };
type AppLockSystemUnlockStatus = {
supported: boolean;
available: boolean;
enabled: boolean;
platform: 'darwin' | 'win32' | 'unsupported';
label: 'Touch ID' | 'Windows Hello' | null;
reason: string | null;
};
type AppLockSystemUnlockResult =
| { ok: true }
| { ok: false; error: 'disabled' | 'not-locked' | 'unsupported' | 'unavailable' | 'cancelled' | 'failed' };
type AppLockSystemUnlockSettingsResult =
| AppLockSettings
| { ok: false; error: 'empty-current' | 'incorrect' | 'locked' | 'unsupported' | 'unavailable' | 'cancelled' | 'failed' };
declare global {
interface NetcattyBridge {
setTheme?(theme: 'light' | 'dark' | 'system'): Promise<boolean>;
setBackgroundColor?(color: string): Promise<boolean>;
setWindowOpacity?(opacity: number): Promise<boolean>;
setAppIconVariant?(variant: import('../../domain/appIconVariant').AppIconVariant): Promise<boolean>;
setLanguage?(language: string): Promise<boolean>;
// Window controls for custom title bar (Windows/Linux)
windowMinimize?(): Promise<void>;
windowMaximize?(): Promise<boolean>;
windowClose?(): Promise<void>;
windowIsMaximized?(): Promise<boolean>;
windowIsFullscreen?(): Promise<boolean>;
windowFocus?(): Promise<boolean>;
setTerminalKeyboardFocus?(focused: boolean): void;
setWindowTitle?(title: string): Promise<boolean>;
openSessionInNewWindow?(payload: {
title: string;
sourceSession: import("../../domain/models").TerminalSession;
localShellType?: import("../../domain/models").TerminalSession['shellType'];
}): Promise<{ success: boolean; error?: string }>;
onOpenSessionInNewWindow?(cb: (payload: {
title: string;
sourceSession: import("../../domain/models").TerminalSession;
localShellType?: import("../../domain/models").TerminalSession['shellType'];
}) => void): () => void;
onWindowCommandCloseRequested?(cb: () => void): () => void;
onWindowFullScreenChanged?(cb: (isFullscreen: boolean) => void): () => void;
onWindowShown?(cb: () => void): () => void;
onWindowFocusRequested?(cb: () => void): () => void;
onWindowWillHide?(cb: () => void): () => void;
// Settings window
openSettingsWindow?(): Promise<boolean>;
closeSettingsWindow?(): Promise<void>;
// Cross-window settings sync
notifySettingsChanged?(payload: { key: string; value: unknown }): void;
onSettingsChanged?(cb: (payload: { key: string; value: unknown }) => void): () => void;
getAppLockRuntimeState?(): Promise<AppLockRuntimeState>;
getAppLockSettings?(): Promise<AppLockSettings>;
setAppLockTimeoutMinutes?(timeoutMinutes: number): Promise<AppLockSettings>;
requestAppLockEnable?(): Promise<AppLockSettings | AppLockSettingsMutationError>;
requestAppLockDisable?(currentPassword: string): Promise<AppLockSettings | AppLockSettingsMutationError>;
requestAppLockReset?(currentPassword: string): Promise<AppLockSettings | AppLockSettingsMutationError>;
requestAppLockPasswordChange?(input: {
currentPassword?: string;
nextPassword: string;
}): Promise<AppLockSettings | AppLockSettingsMutationError>;
setAppLockRuntimeLocked?(reason: Exclude<AppLockRuntimeReason, null>): Promise<AppLockRuntimeState>;
requestAppLockUnlock?(password: string): Promise<AppLockUnlockResult>;
getAppLockSystemUnlockStatus?(): Promise<AppLockSystemUnlockStatus>;
setAppLockSystemUnlockEnabled?(input: {
enabled: boolean;
currentPassword?: string;
autoPromptEnabled?: boolean;
}): Promise<AppLockSystemUnlockSettingsResult>;
requestAppLockSystemUnlock?(): Promise<AppLockSystemUnlockResult>;
reportAppLockActivity?(): Promise<void>;
onAppLockSettingsChanged?(cb: (settings: AppLockSettings) => void): () => void;
onAppLockRuntimeStateChanged?(cb: (state: AppLockRuntimeState) => void): () => void;
// Cloud sync master password (stored in-memory + persisted via Electron safeStorage)
cloudSyncSetSessionPassword?(password: string): Promise<boolean>;
cloudSyncGetSessionPassword?(): Promise<string | null>;
onCloudSyncSessionPasswordAvailable?(callback: () => void): () => void;
cloudSyncClearSessionPassword?(): Promise<boolean>;
// Cloud sync network operations (proxied via main process)
cloudSyncWebdavInitialize?(config: WebDAVConfig): Promise<{ resourceId: string | null }>;
cloudSyncWebdavUpload?(
config: WebDAVConfig,
syncedFile: SyncedFile
): Promise<{ resourceId: string }>;
cloudSyncWebdavDownload?(config: WebDAVConfig): Promise<{ syncedFile: SyncedFile | null }>;
cloudSyncWebdavDelete?(config: WebDAVConfig): Promise<{ ok: true }>;
cloudSyncS3Initialize?(config: S3Config): Promise<{ resourceId: string | null }>;
cloudSyncS3Upload?(
config: S3Config,
syncedFile: SyncedFile
): Promise<{ resourceId: string }>;
cloudSyncS3Download?(config: S3Config): Promise<{ syncedFile: SyncedFile | null }>;
cloudSyncS3Delete?(config: S3Config): Promise<{ ok: true }>;
// Port Forwarding
startPortForward?(options: PortForwardOptions): Promise<PortForwardResult>;
stopPortForward?(tunnelId: string): Promise<PortForwardResult>;
getPortForwardStatus?(tunnelId: string): Promise<PortForwardStatusResult>;
listPortForwards?(): Promise<{ ruleId?: string; tunnelId: string; type: string; status: string; error?: string }[]>;
getPortForwardSnapshot?(): Promise<PortForwardRuntimeSnapshot>;
subscribePortForwardRuntime?(): Promise<PortForwardRuntimeSnapshot>;
unsubscribePortForwardRuntime?(): Promise<{ success: boolean }>;
subscribePortForward?(tunnelId: string): Promise<{
tunnelId: string;
type?: string;
status: 'inactive' | 'connecting' | 'active' | 'error';
error?: string;
}>;
stopAllPortForwards?(): Promise<void>;
stopPortForwardByRuleId?(ruleId: string): Promise<{
stopped: number;
failed?: number;
errors?: string[];
}>;
onPortForwardStatus?(tunnelId: string, cb: PortForwardStatusCallback): () => void;
onPortForwardRuntime?(cb: PortForwardRuntimeEventCallback): () => void;
// Known Hosts
readKnownHosts?(): Promise<string | null>;
// Open URL in default browser. Resolves when the URL is handled by
// either the system browser or the in-app fallback BrowserWindow.
// Rejects only in the rare case where both paths fail.
openExternal?(url: string): Promise<void>;
openPath?(path: string): Promise<{ success: boolean; error?: string }>;
// App info (name/version/platform) for About screens
getAppInfo?(): Promise<{ name: string; version: string; platform: string }>;
ptyGetChildProcesses?(sessionId: string): Promise<Array<{ pid: number; command: string }>>;
confirmCloseBusy?(payload: {
command: string;
title?: string;
message?: string;
cancelLabel?: string;
closeLabel?: string;
}): Promise<boolean>;
getVaultBackupCapabilities?(): Promise<{ encryptionAvailable: boolean }>;
createVaultBackup?(payload: {
payload: import('./domain/sync').SyncPayload;
reason: 'app_version_change' | 'before_restore';
sourceAppVersion?: string;
targetAppVersion?: string;
syncDataVersion?: number;
maxCount?: number;
}): Promise<{
created: boolean;
backup: {
id: string;
createdAt: number;
reason: 'app_version_change' | 'before_restore';
sourceAppVersion?: string;
targetAppVersion?: string;
fingerprint: string;
preview: {
hostCount: number;
keyCount: number;
snippetCount: number;
noteCount: number;
identityCount: number;
portForwardingRuleCount: number;
};
} | null;
}>;
listVaultBackups?(): Promise<Array<{
id: string;
createdAt: number;
reason: 'app_version_change' | 'before_restore';
sourceAppVersion?: string;
targetAppVersion?: string;
fingerprint: string;
preview: {
hostCount: number;
keyCount: number;
snippetCount: number;
noteCount: number;
identityCount: number;
portForwardingRuleCount: number;
};
}>>;
readVaultBackup?(payload: { id: string }): Promise<{
backup: {
id: string;
createdAt: number;
reason: 'app_version_change' | 'before_restore';
sourceAppVersion?: string;
targetAppVersion?: string;
fingerprint: string;
preview: {
hostCount: number;
keyCount: number;
snippetCount: number;
noteCount: number;
identityCount: number;
portForwardingRuleCount: number;
};
};
payload: import('./domain/sync').SyncPayload;
}>;
trimVaultBackups?(payload: { maxCount: number }): Promise<{ deletedCount: number; keptCount: number }>;
openVaultBackupDir?(): Promise<{ success: boolean; path: string }>;
// Subscribe to main-process-driven "vault backups changed" events.
// Returns an unsubscribe callback. Undefined in non-Electron builds.
onVaultBackupsChanged?(handler: () => void): () => void;
// Notify main process the renderer has mounted/painted (used to avoid initial blank screen).
rendererReady?(): void;
// Fired when an existing main renderer window is shown again from tray,
// global hotkey, dock activation, or second-instance focusing.
onAppLockReopen?(listener: () => void): () => void;
// Quit guard: subscribe to main-process quit requests that query for dirty editors.
// Listener is called with no arguments; return value is an unsubscribe function.
onCheckDirtyEditors?(listener: () => void): () => void;
// Report the dirty-check result back to the main process.
reportDirtyEditorsResult?(hasDirty: boolean): void;
onLanguageChanged?(cb: (language: string) => void): () => void;
// Chain progress listener for jump host connections
// Callback receives: (sessionId: string, currentHop: number, totalHops: number, hostLabel: string, status: string, error?: string)
onChainProgress?(cb: (sessionId: string, hop: number, total: number, label: string, status: string, error?: string) => void): () => void;
// Fired when a requested SSH connection reuse cannot be honored and the
// session falls back to a regular fresh connection.
onConnectionReuseFallback?(cb: (sessionId: string, sourceSessionId?: string) => void): () => void;
// SFTP connection progress listener (auth method logs)
onSftpConnectionProgress?(cb: (sessionId: string, label: string, status: string, detail?: string) => void): () => void;
// OAuth callback server for cloud sync. `prepareOAuthCallback` binds the
// loopback listener and returns the chosen port (preferred 45678, falls
// back to an OS-assigned free port if busy). The caller then builds the
// OAuth URL against `redirectUri`, opens the browser, and finally awaits
// the code via `awaitOAuthCallback`.
prepareOAuthCallback?(): Promise<{ sessionId: string; port: number; redirectUri: string }>;
awaitOAuthCallback?(expectedState?: string, sessionId?: string): Promise<{ code: string; state?: string }>;
cancelOAuthCallback?(sessionId?: string): Promise<void>;
// GitHub Device Flow (cloud sync)
githubStartDeviceFlow?(options?: { clientId?: string; scope?: string }): Promise<{
deviceCode: string;
userCode: string;
verificationUri: string;
expiresAt: number;
interval: number;
}>;
githubPollDeviceFlowToken?(options: { clientId?: string; deviceCode: string; pollId?: string }): Promise<{
access_token?: string;
token_type?: string;
scope?: string;
error?: string;
error_description?: string;
}>;
githubCancelDeviceFlowPoll?(pollId: string): Promise<void>;
githubDownloadGistRawContent?(options: { accessToken: string; rawUrl: string }): Promise<string>;
// Google OAuth (cloud sync) - proxied via main process to avoid CORS
googleExchangeCodeForTokens?(options: {
clientId: string;
clientSecret?: string;
code: string;
codeVerifier: string;
redirectUri: string;
}): Promise<{
accessToken: string;
refreshToken?: string;
expiresAt?: number;
tokenType: string;
scope?: string;
}>;
googleRefreshAccessToken?(options: {
clientId: string;
clientSecret?: string;
refreshToken: string;
}): Promise<{
accessToken: string;
refreshToken: string;
expiresAt?: number;
tokenType: string;
scope?: string;
}>;
googleGetUserInfo?(options: { accessToken: string }): Promise<{
id: string;
email: string;
name: string;
picture?: string;
}>;
// Google Drive API (cloud sync) - proxied via main process to avoid CORS/COEP issues
googleDriveFindSyncFile?(options: { accessToken: string; fileName?: string }): Promise<{ fileId: string | null }>;
googleDriveCreateSyncFile?(options: { accessToken: string; fileName?: string; syncedFile: unknown }): Promise<{ fileId: string }>;
googleDriveUpdateSyncFile?(options: { accessToken: string; fileId: string; syncedFile: unknown }): Promise<{ ok: true }>;
googleDriveDownloadSyncFile?(options: { accessToken: string; fileId: string }): Promise<{ syncedFile: unknown | null }>;
googleDriveDeleteSyncFile?(options: { accessToken: string; fileId: string }): Promise<{ ok: true }>;
// OneDrive OAuth + Graph (cloud sync) - proxied via main process to avoid CORS
onedriveExchangeCodeForTokens?(options: {
clientId: string;
code: string;
codeVerifier: string;
redirectUri: string;
scope?: string;
}): Promise<{
accessToken: string;
refreshToken?: string;
expiresAt?: number;
tokenType: string;
scope?: string;
}>;
onedriveRefreshAccessToken?(options: {
clientId: string;
refreshToken: string;
scope?: string;
}): Promise<{
accessToken: string;
refreshToken: string;
expiresAt?: number;
tokenType: string;
scope?: string;
}>;
onedriveGetUserInfo?(options: { accessToken: string }): Promise<{
id: string;
email: string;
name: string;
avatarDataUrl?: string;
}>;
onedriveFindSyncFile?(options: { accessToken: string; fileName?: string }): Promise<{ fileId: string | null }>;
onedriveUploadSyncFile?(options: { accessToken: string; fileName?: string; syncedFile: unknown }): Promise<{ fileId: string | null }>;
onedriveDownloadSyncFile?(options: { accessToken: string; fileId?: string; fileName?: string }): Promise<{ syncedFile: unknown | null }>;
onedriveDeleteSyncFile?(options: { accessToken: string; fileId: string }): Promise<{ ok: true }>;
}
}
export {};

152
types/global/netcatty-bridge-system.d.ts vendored Normal file
View File

@@ -0,0 +1,152 @@
declare global {
interface NetcattyBridge {
probeSystemCapabilities?(sessionId: string): Promise<{
success: boolean;
pending?: boolean;
error?: string;
capabilities?: import("../../domain/systemManager/types").SessionCapabilities;
}>;
listSystemProcesses?(sessionId: string): Promise<{
success: boolean;
pending?: boolean;
error?: string;
processes?: import("../../domain/systemManager/types").SystemProcessInfo[];
}>;
signalSystemProcess?(options: {
sessionId: string;
pid: number;
signal?: string;
nice?: number;
}): Promise<{ success: boolean; pending?: boolean; error?: string; code?: number }>;
setupOsc7Tracking?(sessionId: string, command: string): Promise<{
success: boolean;
pending?: boolean;
stdout?: string;
stderr?: string;
code?: number | null;
error?: string;
}>;
listTmuxSessions?(sessionId: string): Promise<{
success: boolean;
error?: string;
tmuxVersion?: string;
sessions?: import("../../domain/systemManager/types").TmuxSessionInfo[];
}>;
createTmuxSession?(options: { sessionId: string; name: string; command?: string }): Promise<{
success: boolean;
error?: string;
name?: string;
}>;
listTmuxWindows?(options: { sessionId: string; sessionName: string }): Promise<{
success: boolean;
error?: string;
debug?: {
lastOutput?: string;
tried?: string[];
sockets?: Array<string | null>;
};
windows?: import("../../domain/systemManager/types").TmuxWindowInfo[];
}>;
listTmuxPanes?(options: {
sessionId: string;
sessionName: string;
windowIndex: number;
}): Promise<{
success: boolean;
error?: string;
debug?: {
lastOutput?: string;
tried?: string[];
sockets?: Array<string | null>;
};
panes?: import("../../domain/systemManager/types").TmuxPaneInfo[];
}>;
listTmuxClients?(options: { sessionId: string; sessionName?: string }): Promise<{
success: boolean;
error?: string;
clients?: import("../../domain/systemManager/types").TmuxClientInfo[];
}>;
tmuxAction?(options: {
sessionId: string;
} & import("../../domain/systemManager/types").TmuxManageAction): Promise<{ success: boolean; error?: string }>;
listDockerContainers?(sessionId: string): Promise<{
success: boolean;
error?: string;
containers?: import("../../domain/systemManager/types").DockerContainerInfo[];
}>;
listDockerImages?(sessionId: string): Promise<{
success: boolean;
error?: string;
images?: import("../../domain/systemManager/types").DockerImageInfo[];
}>;
getDockerStats?(options: { sessionId: string; ids?: string[] }): Promise<{
success: boolean;
error?: string;
stats?: import("../../domain/systemManager/types").DockerStatInfo[];
}>;
listAccelerators?(sessionId: string): Promise<{
success: boolean;
pending?: boolean;
error?: string;
devices?: import("../../domain/systemManager/types").AcceleratorDeviceInfo[];
processes?: import("../../domain/systemManager/types").AcceleratorProcessInfo[];
nvidiaDriverVersion?: string | null;
probedAt?: number;
}>;
listListeningPorts?(sessionId: string): Promise<{
success: boolean;
pending?: boolean;
error?: string;
ports?: import("../../domain/systemManager/types").ListeningPortInfo[];
}>;
listSystemServices?(sessionId: string): Promise<{
success: boolean;
pending?: boolean;
error?: string;
units?: import("../../domain/systemManager/types").SystemdUnitInfo[];
}>;
systemServiceAction?(options: {
sessionId: string;
unitName: string;
action: import("../../domain/systemManager/types").SystemdUnitAction;
scope?: import("../../domain/systemManager/types").SystemdUnitInfo['scope'];
}): Promise<{ success: boolean; pending?: boolean; error?: string }>;
dockerInspect?(options: { sessionId: string; containerId: string }): Promise<{
success: boolean;
error?: string;
inspect?: Record<string, unknown>;
}>;
dockerImageInspect?(options: { sessionId: string; imageId: string }): Promise<{
success: boolean;
error?: string;
inspect?: Record<string, unknown>;
}>;
dockerAction?(options: {
sessionId: string;
containerId: string;
action: import("../../domain/systemManager/types").DockerContainerAction;
newName?: string;
}): Promise<{ success: boolean; error?: string }>;
dockerImageAction?(options: {
sessionId: string;
} & import("../../domain/systemManager/types").DockerImageManageAction): Promise<{
success: boolean;
error?: string;
output?: string;
}>;
openTerminalPopup?(payload: import("../../domain/systemManager/types").TerminalPopupPayload): Promise<{
success: boolean;
error?: string;
popupId?: string;
}>;
logDiagnostic?(payload: {
source: string;
message: string;
extra?: Record<string, unknown>;
}): Promise<{ success: boolean; error?: string }>;
onTerminalPopupConfig?(cb: (payload: import("../../domain/systemManager/types").TerminalPopupPayload) => void): () => void;
}
}
export {};