feat: move opkssh and warpgate into plugins (C3)

This commit is contained in:
LukeGus committed 2026-09-24 19:28:19 -05:00
1 parent 925da4a661
commit 98d367d7e2
336 files changed
+22380 -4746

No files matched your search

@@ -59,7 +59,7 @@ import {
moveSSHItem,
connectSSH,
verifySSHTOTP,
verifySSHWarpgate,
verifySSHBrowserSignIn,
getSSHStatus,
keepSSHAlive,
identifySSHSymlink,
@@ -199,12 +199,13 @@ function FileManagerContent({
const [totpRequired, setTotpRequired] = useState(false);
const [totpSessionId, setTotpSessionId] = useState<string | null>(null);
const [totpPrompt, setTotpPrompt] = useState<string>("");
const [warpgateRequired, setWarpgateRequired] = useState(false);
const [warpgateSessionId, setWarpgateSessionId] = useState<string | null>(
null,
);
const [warpgateUrl, setWarpgateUrl] = useState<string>("");
const [warpgateSecurityKey, setWarpgateSecurityKey] = useState<string>("");
const [browserSignInRequired, setBrowserSignInRequired] = useState(false);
const [browserSignInSessionId, setBrowserSignInSessionId] = useState<
string | null
>(null);
const [browserSignInUrl, setBrowserSignInUrl] = useState<string>("");
const [browserSignInCode, setBrowserSignInCode] = useState<string>("");
const [browserSignInLabel, setBrowserSignInLabel] = useState<string>("");
const [showAuthDialog, setShowAuthDialog] = useState(false);
const [authDialogReason, setAuthDialogReason] = useState<
"no_keyboard" | "auth_failed" | "timeout"
@@ -421,7 +422,7 @@ function FileManagerContent({
!!currentHost &&
!sshSessionId &&
!totpRequired &&
!warpgateRequired &&
!browserSignInRequired &&
!showAuthDialog &&
!showPassphraseDialog,
autoStart: false,
@@ -627,11 +628,12 @@ function FileManagerContent({
socks5ProxyChain: currentHost.socks5ProxyChain,
});
if (result?.requires_warpgate) {
setWarpgateRequired(true);
setWarpgateSessionId(sessionId);
setWarpgateUrl(result.url || "");
setWarpgateSecurityKey(result.securityKey || "N/A");
if (result?.requires_browser_sign_in) {
setBrowserSignInRequired(true);
setBrowserSignInSessionId(sessionId);
setBrowserSignInUrl(result.url || "");
setBrowserSignInCode(result.code || "N/A");
setBrowserSignInLabel(result.label || "");
setIsLoading(false);
return;
}
@@ -698,11 +700,14 @@ function FileManagerContent({
setIsLoading(false);
return;
}
if (sshError.requires_warpgate) {
setWarpgateRequired(true);
setWarpgateSessionId(sshError.sessionId || currentHost.id.toString());
setWarpgateUrl(sshError.url || "");
setWarpgateSecurityKey(sshError.securityKey || "N/A");
if (sshError.requires_browser_sign_in) {
setBrowserSignInRequired(true);
setBrowserSignInSessionId(
sshError.sessionId || currentHost.id.toString(),
);
setBrowserSignInUrl(sshError.url || "");
setBrowserSignInCode(sshError.code || "N/A");
setBrowserSignInLabel(sshError.label || "");
setIsLoading(false);
return;
}
@@ -2660,23 +2665,26 @@ function FileManagerContent({
if (onClose) onClose();
}
async function handleWarpgateContinue() {
if (!warpgateSessionId) return;
async function handleBrowserSignInContinue() {
if (!browserSignInSessionId) return;
try {
setIsLoading(true);
const result = await verifySSHWarpgate(warpgateSessionId);
const result = await verifySSHBrowserSignIn(browserSignInSessionId);
if (result?.status === "success") {
setWarpgateRequired(false);
setWarpgateUrl("");
setWarpgateSecurityKey("");
setSshSessionId(warpgateSessionId);
setWarpgateSessionId(null);
setBrowserSignInRequired(false);
setBrowserSignInUrl("");
setBrowserSignInCode("");
setSshSessionId(browserSignInSessionId);
setBrowserSignInSessionId(null);
connectRetryRef.current.markConnected();
try {
const response = await listSSHFiles(warpgateSessionId, currentPath);
const response = await listSSHFiles(
browserSignInSessionId,
currentPath,
);
const files = Array.isArray(response)
? response
: response?.files || [];
@@ -2691,24 +2699,24 @@ function FileManagerContent({
}
}
} catch (error: unknown) {
console.error("Warpgate verification failed:", error);
toast.error(t("fileManager.warpgateVerificationFailed"));
console.error("Browser sign-in failed:", error);
toast.error(t("fileManager.browserSignInFailed"));
} finally {
setIsLoading(false);
}
}
function handleWarpgateCancel() {
setWarpgateRequired(false);
setWarpgateUrl("");
setWarpgateSecurityKey("");
setWarpgateSessionId(null);
function handleBrowserSignInCancel() {
setBrowserSignInRequired(false);
setBrowserSignInUrl("");
setBrowserSignInCode("");
setBrowserSignInSessionId(null);
if (onClose) onClose();
}
function handleWarpgateOpenUrl() {
if (warpgateUrl) {
window.open(warpgateUrl, "_blank", "noopener,noreferrer");
function handleBrowserSignInOpenUrl() {
if (browserSignInUrl) {
window.open(browserSignInUrl, "_blank", "noopener,noreferrer");
}
}
@@ -2745,11 +2753,12 @@ function FileManagerContent({
socks5ProxyChain: currentHost.socks5ProxyChain,
});
if (result?.requires_warpgate) {
setWarpgateRequired(true);
setWarpgateSessionId(sessionId);
setWarpgateUrl(result.url || "");
setWarpgateSecurityKey(result.securityKey || "N/A");
if (result?.requires_browser_sign_in) {
setBrowserSignInRequired(true);
setBrowserSignInSessionId(sessionId);
setBrowserSignInUrl(result.url || "");
setBrowserSignInCode(result.code || "N/A");
setBrowserSignInLabel(result.label || "");
setIsLoading(false);
return;
}
@@ -3626,12 +3635,13 @@ function FileManagerContent({
totpPrompt={totpPrompt}
handleTotpSubmit={handleTotpSubmit}
handleTotpCancel={handleTotpCancel}
warpgateRequired={warpgateRequired}
warpgateUrl={warpgateUrl}
warpgateSecurityKey={warpgateSecurityKey}
handleWarpgateContinue={handleWarpgateContinue}
handleWarpgateCancel={handleWarpgateCancel}
handleWarpgateOpenUrl={handleWarpgateOpenUrl}
browserSignInRequired={browserSignInRequired}
browserSignInUrl={browserSignInUrl}
browserSignInCode={browserSignInCode}
browserSignInLabel={browserSignInLabel}
handleBrowserSignInContinue={handleBrowserSignInContinue}
handleBrowserSignInCancel={handleBrowserSignInCancel}
handleBrowserSignInOpenUrl={handleBrowserSignInOpenUrl}
currentHost={currentHost}
showAuthDialog={showAuthDialog}
authDialogReason={authDialogReason}
@@ -2,7 +2,7 @@ import type { FileItem, SSHHost } from "@/types/index";
import {
TOTPDialog,
SSHAuthDialog,
WarpgateDialog,
BrowserSignInDialog,
} from "@termix/plugin-sdk/ui";
import { PermissionsDialog } from "./components/PermissionsDialog.tsx";
import { CompressDialog } from "./components/CompressDialog.tsx";
@@ -17,12 +17,13 @@ type FileManagerDialogsProps = {
totpPrompt: string;
handleTotpSubmit: (code: string) => void | Promise<void>;
handleTotpCancel: () => void;
warpgateRequired: boolean;
warpgateUrl: string;
warpgateSecurityKey: string;
handleWarpgateContinue: () => void | Promise<void>;
handleWarpgateCancel: () => void;
handleWarpgateOpenUrl: () => void;
browserSignInRequired: boolean;
browserSignInUrl: string;
browserSignInCode: string;
browserSignInLabel: string;
handleBrowserSignInContinue: () => void | Promise<void>;
handleBrowserSignInCancel: () => void;
handleBrowserSignInOpenUrl: () => void;
currentHost: SSHHost | null;
showAuthDialog: boolean;
authDialogReason: "no_keyboard" | "auth_failed" | "timeout";
@@ -49,12 +50,13 @@ export function FileManagerDialogs({
totpPrompt,
handleTotpSubmit,
handleTotpCancel,
warpgateRequired,
warpgateUrl,
warpgateSecurityKey,
handleWarpgateContinue,
handleWarpgateCancel,
handleWarpgateOpenUrl,
browserSignInRequired,
browserSignInUrl,
browserSignInCode,
browserSignInLabel,
handleBrowserSignInContinue,
handleBrowserSignInCancel,
handleBrowserSignInOpenUrl,
currentHost,
showAuthDialog,
authDialogReason,
@@ -85,13 +87,14 @@ export function FileManagerDialogs({
backgroundColor="var(--bg-canvas)"
/>
<WarpgateDialog
isOpen={warpgateRequired}
url={warpgateUrl}
securityKey={warpgateSecurityKey}
onContinue={handleWarpgateContinue}
onCancel={handleWarpgateCancel}
onOpenUrl={handleWarpgateOpenUrl}
<BrowserSignInDialog
isOpen={browserSignInRequired}
label={browserSignInLabel}
url={browserSignInUrl}
code={browserSignInCode}
onContinue={handleBrowserSignInContinue}
onCancel={handleBrowserSignInCancel}
onOpenUrl={handleBrowserSignInOpenUrl}
backgroundColor="var(--bg-canvas)"
/>
@@ -30,11 +30,12 @@ type ConnectErrorResponse = {
message?: string;
connectionLogs?: ApiConnectionLog[];
requires_totp?: boolean;
requires_warpgate?: boolean;
requires_browser_sign_in?: boolean;
sessionId?: string;
prompt?: string;
url?: string;
securityKey?: string;
code?: string;
label?: string;
status?: string;
reason?: string;
};
@@ -48,11 +49,12 @@ export interface SSHConnectResult {
status?: string;
reason?: "timeout" | "no_keyboard" | "auth_failed";
requires_totp?: boolean;
requires_warpgate?: boolean;
requires_browser_sign_in?: boolean;
sessionId?: string;
prompt?: string;
url?: string;
securityKey?: string;
code?: string;
label?: string;
}
function triggerBlobDownload(blob: Blob, fileName: string): void {
@@ -125,12 +127,13 @@ export async function connectSSH(
prompt: data.prompt,
});
}
if (data.requires_warpgate) {
if (data.requires_browser_sign_in) {
Object.assign(errorWithLogs, {
requires_warpgate: true,
requires_browser_sign_in: true,
sessionId: data.sessionId,
url: data.url,
securityKey: data.securityKey,
code: data.code,
label: data.label,
});
}
if (data.status === "auth_required") {
@@ -176,17 +179,17 @@ export async function verifySSHTOTP(
}
}
export async function verifySSHWarpgate(
export async function verifySSHBrowserSignIn(
sessionId: string,
): Promise<Record<string, unknown>> {
try {
const response = await getFileManagerApiForSession(sessionId).post(
"/ssh/connect-warpgate",
"/ssh/connect-browser-sign-in",
{ sessionId },
);
return response.data;
} catch (error) {
handleApiError(error, "verify SSH Warpgate");
handleApiError(error, "verify SSH browser sign-in");
}
}
@@ -1020,7 +1023,7 @@ export async function ensureSSHSessionForHost(
if (
result?.requires_totp ||
result?.requires_warpgate ||
result?.requires_browser_sign_in ||
result?.status === "auth_required"
) {
return { state: "auth_required", sessionId };
@@ -16,11 +16,12 @@ export type ConnectionLogPayload = Omit<LogEntry, "id" | "timestamp">;
export type SSHConnectionError = Error & {
connectionLogs?: ConnectionLogPayload[];
requires_totp?: boolean;
requires_warpgate?: boolean;
requires_browser_sign_in?: boolean;
sessionId?: string;
prompt?: string;
url?: string;
securityKey?: string;
code?: string;
label?: string;
status?: string;
reason?: "no_keyboard" | "auth_failed" | "timeout";
};