[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

View 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,
};