[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
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:
156
electron/bridges/keyboardInteractiveHandler.cjs
Normal file
156
electron/bridges/keyboardInteractiveHandler.cjs
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* Keyboard Interactive Handler - Shared state for keyboard-interactive authentication
|
||||
* This module provides a centralized storage for keyboard-interactive auth requests
|
||||
* used by SSH, SFTP, and Port Forwarding bridges.
|
||||
*/
|
||||
|
||||
// Keyboard-interactive authentication pending requests
|
||||
// Map of requestId -> { finishCallback, webContentsId, sessionId, createdAt, timeoutId }
|
||||
const { randomUUID } = require("node:crypto");
|
||||
|
||||
const keyboardInteractiveRequests = new Map();
|
||||
|
||||
// TTL for abandoned requests (5 minutes)
|
||||
const REQUEST_TTL_MS = 5 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Generate a unique request ID for keyboard-interactive requests
|
||||
*/
|
||||
function generateRequestId(prefix = 'ki') {
|
||||
return `${prefix}-${randomUUID()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a keyboard-interactive request with TTL cleanup
|
||||
*/
|
||||
function storeRequest(requestId, finishCallback, webContentsId, sessionId, sender) {
|
||||
// Set up TTL timeout to clean up abandoned requests
|
||||
const timeoutId = setTimeout(() => {
|
||||
const pending = keyboardInteractiveRequests.get(requestId);
|
||||
if (pending) {
|
||||
console.warn(`[KeyboardInteractive] Request ${requestId} timed out after ${REQUEST_TTL_MS / 1000}s, cleaning up`);
|
||||
keyboardInteractiveRequests.delete(requestId);
|
||||
// Call finish with empty responses to abort the authentication
|
||||
try {
|
||||
pending.finishCallback([]);
|
||||
} catch (err) {
|
||||
console.warn(`[KeyboardInteractive] Failed to call finishCallback for timed out request:`, err.message);
|
||||
}
|
||||
notifyCancellation(pending, requestId, "timeout");
|
||||
}
|
||||
}, REQUEST_TTL_MS);
|
||||
|
||||
keyboardInteractiveRequests.set(requestId, {
|
||||
finishCallback,
|
||||
webContentsId,
|
||||
sessionId,
|
||||
sender,
|
||||
createdAt: Date.now(),
|
||||
timeoutId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle keyboard-interactive authentication response from renderer
|
||||
*/
|
||||
function handleResponse(_event, payload) {
|
||||
console.log(`[KeyboardInteractive] handleResponse called`, {
|
||||
requestId: payload?.requestId,
|
||||
cancelled: Boolean(payload?.cancelled),
|
||||
responsesCount: Array.isArray(payload?.responses) ? payload.responses.length : 0,
|
||||
});
|
||||
|
||||
const { requestId, responses, cancelled } = payload;
|
||||
const pending = keyboardInteractiveRequests.get(requestId);
|
||||
|
||||
console.log(`[KeyboardInteractive] Looking for request ${requestId}, found:`, !!pending);
|
||||
console.log(`[KeyboardInteractive] Current pending requests:`, Array.from(keyboardInteractiveRequests.keys()));
|
||||
|
||||
if (!pending) {
|
||||
console.warn(`[KeyboardInteractive] No pending request for ${requestId}`);
|
||||
return { success: false, error: 'Request not found' };
|
||||
}
|
||||
|
||||
if (_event?.sender?.id !== pending.webContentsId) {
|
||||
console.warn(`[KeyboardInteractive] Wrong sender for request ${requestId}`);
|
||||
return { success: false, error: 'Wrong sender' };
|
||||
}
|
||||
|
||||
if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
||||
keyboardInteractiveRequests.delete(requestId);
|
||||
|
||||
try {
|
||||
if (cancelled) {
|
||||
console.log(`[KeyboardInteractive] Auth cancelled for ${requestId}`);
|
||||
pending.finishCallback([]); // Empty responses to cancel
|
||||
} else {
|
||||
console.log(`[KeyboardInteractive] Auth response received for ${requestId}, responses count:`, responses?.length);
|
||||
pending.finishCallback(responses);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[KeyboardInteractive] Failed to deliver response for ${requestId}:`, err?.message);
|
||||
notifyCancellation(pending, requestId, "delivery-failed");
|
||||
return { success: false, error: "Failed to deliver response" };
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel every pending request owned by a session or external operation.
|
||||
*/
|
||||
function notifyCancellation(pending, requestId, reason) {
|
||||
try {
|
||||
if (!pending.sender?.isDestroyed?.()) {
|
||||
pending.sender?.send?.("netcatty:keyboard-interactive-cancelled", {
|
||||
requestId,
|
||||
sessionId: pending.sessionId,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`[KeyboardInteractive] Failed to notify cancellation for ${requestId}:`, err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function cancelRequestsForSession(sessionId, reason = "cancelled") {
|
||||
let cancelled = 0;
|
||||
for (const [requestId, pending] of keyboardInteractiveRequests) {
|
||||
if (pending.sessionId !== sessionId) continue;
|
||||
if (pending.timeoutId) {
|
||||
clearTimeout(pending.timeoutId);
|
||||
}
|
||||
keyboardInteractiveRequests.delete(requestId);
|
||||
try {
|
||||
pending.finishCallback([]);
|
||||
} catch (err) {
|
||||
console.warn(`[KeyboardInteractive] Failed to cancel request ${requestId}:`, err.message);
|
||||
}
|
||||
notifyCancellation(pending, requestId, reason);
|
||||
cancelled += 1;
|
||||
}
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the requests map (for debugging/testing)
|
||||
*/
|
||||
function getRequests() {
|
||||
return keyboardInteractiveRequests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register IPC handler for keyboard-interactive responses
|
||||
*/
|
||||
function registerHandler(ipcMain) {
|
||||
ipcMain.handle("netcatty:keyboard-interactive:respond", handleResponse);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateRequestId,
|
||||
storeRequest,
|
||||
handleResponse,
|
||||
cancelRequestsForSession,
|
||||
getRequests,
|
||||
registerHandler,
|
||||
};
|
||||
Reference in New Issue
Block a user