[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:
186
components/terminal/hooks/useTerminalAuthState.ts
Normal file
186
components/terminal/hooks/useTerminalAuthState.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import type { Terminal as XTerm } from "@xterm/xterm";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import type { RefObject } from "react";
|
||||
import type { Host, TerminalSession } from "../../../types";
|
||||
import type { PendingAuth } from "../runtime/createTerminalSessionStarters";
|
||||
import type { TerminalAuthMethod } from "../TerminalAuthDialog";
|
||||
import { logger } from "../../../lib/logger";
|
||||
|
||||
/**
|
||||
* Password auth is valid when the user typed something — including a single
|
||||
* space. SSH passwords may be whitespace-only; do not trim before this check
|
||||
* (issue #2036).
|
||||
*/
|
||||
export const isAuthPasswordProvided = (password: string): boolean =>
|
||||
password.length > 0;
|
||||
|
||||
export const buildSavedAuthHostUpdate = (
|
||||
host: Host,
|
||||
auth: {
|
||||
authMethod: TerminalAuthMethod;
|
||||
username: string;
|
||||
password: string;
|
||||
keyId: string | null;
|
||||
},
|
||||
): Host => ({
|
||||
...host,
|
||||
username: auth.username,
|
||||
authMethod: auth.authMethod,
|
||||
password: auth.authMethod === "password" ? auth.password : undefined,
|
||||
savePassword: auth.authMethod === "password" ? true : host.savePassword,
|
||||
identityFileId:
|
||||
auth.authMethod === "key" || auth.authMethod === "certificate"
|
||||
? (auth.keyId ?? undefined)
|
||||
: undefined,
|
||||
// Detach stale Keychain identity on explicit credential save (#1956):
|
||||
// resolveHostAuth prefers identity credentials over host fields.
|
||||
// Empty string (not undefined) so applyGroupDefaults treats this as an explicit
|
||||
// host-level override and does not re-inherit a group-level identity; consumers
|
||||
// check host.identityId truthiness so "" behaves as "no identity".
|
||||
identityId: "",
|
||||
});
|
||||
|
||||
export const useTerminalAuthState = ({
|
||||
host,
|
||||
pendingAuthRef,
|
||||
termRef,
|
||||
onUpdateHost,
|
||||
onStartSession,
|
||||
setStatus,
|
||||
setProgressLogs,
|
||||
}: {
|
||||
host: Host;
|
||||
pendingAuthRef: RefObject<PendingAuth>;
|
||||
termRef: RefObject<XTerm | null>;
|
||||
onUpdateHost?: (host: Host) => void;
|
||||
onStartSession: (term: XTerm) => void;
|
||||
setStatus: (status: TerminalSession["status"]) => void;
|
||||
setProgressLogs: (next: string[] | ((prev: string[]) => string[])) => void;
|
||||
}) => {
|
||||
const [needsAuth, setNeedsAuth] = useState(false);
|
||||
const [authRetryMessage, setAuthRetryMessage] = useState<string | null>(null);
|
||||
const [authUsername, setAuthUsername] = useState(host.username || "root");
|
||||
const [authMethod, setAuthMethod] = useState<TerminalAuthMethod>("password");
|
||||
const [authPassword, setAuthPassword] = useState("");
|
||||
const [authKeyId, setAuthKeyId] = useState<string | null>(null);
|
||||
const [authPassphrase, setAuthPassphrase] = useState("");
|
||||
const [showAuthPassword, setShowAuthPassword] = useState(false);
|
||||
const [showAuthPassphrase, setShowAuthPassphrase] = useState(false);
|
||||
const [saveCredentials, setSaveCredentials] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
setNeedsAuth(false);
|
||||
setAuthRetryMessage(null);
|
||||
setAuthUsername(host.username || "root");
|
||||
setAuthPassword("");
|
||||
setAuthKeyId(null);
|
||||
setAuthPassphrase("");
|
||||
setShowAuthPassword(false);
|
||||
setShowAuthPassphrase(false);
|
||||
setSaveCredentials(true);
|
||||
}, [host.id, host.username]);
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
if (!authUsername.trim()) return false;
|
||||
if (authMethod === "password") return isAuthPasswordProvided(authPassword);
|
||||
if (authMethod === "key" || authMethod === "certificate") return !!authKeyId;
|
||||
return false;
|
||||
}, [authKeyId, authMethod, authPassword, authUsername]);
|
||||
|
||||
const resetForRetry = useCallback(() => {
|
||||
setNeedsAuth(false);
|
||||
setAuthRetryMessage(null);
|
||||
pendingAuthRef.current = null;
|
||||
}, [pendingAuthRef]);
|
||||
|
||||
const submit = useCallback(
|
||||
(opts?: { saveToHost?: boolean }) => {
|
||||
if (!isValid) return;
|
||||
|
||||
const shouldSave = opts?.saveToHost ?? saveCredentials;
|
||||
pendingAuthRef.current = {
|
||||
authMethod,
|
||||
username: authUsername,
|
||||
password: authMethod === "password" ? authPassword : undefined,
|
||||
keyId:
|
||||
authMethod === "key" || authMethod === "certificate"
|
||||
? (authKeyId ?? undefined)
|
||||
: undefined,
|
||||
passphrase:
|
||||
authMethod === "key" || authMethod === "certificate"
|
||||
? authPassphrase || undefined
|
||||
: undefined,
|
||||
savedToHost: shouldSave && Boolean(onUpdateHost),
|
||||
};
|
||||
|
||||
if (shouldSave && onUpdateHost) {
|
||||
onUpdateHost(
|
||||
buildSavedAuthHostUpdate(host, {
|
||||
authMethod,
|
||||
username: authUsername,
|
||||
password: authPassword,
|
||||
keyId: authKeyId,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
setNeedsAuth(false);
|
||||
setAuthRetryMessage(null);
|
||||
setStatus("connecting");
|
||||
setProgressLogs(["Authenticating with provided credentials..."]);
|
||||
|
||||
const term = termRef.current;
|
||||
if (!term) return;
|
||||
|
||||
try {
|
||||
term.clear?.();
|
||||
} catch (err) {
|
||||
logger.warn("Failed to clear terminal", err);
|
||||
}
|
||||
|
||||
onStartSession(term);
|
||||
},
|
||||
[
|
||||
authKeyId,
|
||||
authMethod,
|
||||
authPassphrase,
|
||||
authPassword,
|
||||
authUsername,
|
||||
host,
|
||||
isValid,
|
||||
onStartSession,
|
||||
onUpdateHost,
|
||||
pendingAuthRef,
|
||||
saveCredentials,
|
||||
setProgressLogs,
|
||||
setStatus,
|
||||
termRef,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
needsAuth,
|
||||
setNeedsAuth,
|
||||
authRetryMessage,
|
||||
setAuthRetryMessage,
|
||||
authUsername,
|
||||
setAuthUsername,
|
||||
authMethod,
|
||||
setAuthMethod,
|
||||
authPassword,
|
||||
setAuthPassword,
|
||||
authKeyId,
|
||||
setAuthKeyId,
|
||||
authPassphrase,
|
||||
setAuthPassphrase,
|
||||
showAuthPassword,
|
||||
setShowAuthPassword,
|
||||
showAuthPassphrase,
|
||||
setShowAuthPassphrase,
|
||||
saveCredentials,
|
||||
setSaveCredentials,
|
||||
isValid,
|
||||
resetForRetry,
|
||||
submit,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user