feat: finish the ai plugin (B18)

This commit is contained in:
LukeGus committed 2026-09-24 13:43:26 -05:00
1 parent 6b4d1432ab
commit 21937a6b02
193 files changed
+23429 -4244

No files matched your search

@@ -1,6 +1,5 @@
import { authLogger } from "../../utils/logger.js";
import {
createCurrentAiRepository,
createCurrentNotificationChannelRepository,
createCurrentApiKeyRepository,
createCurrentAuditLogRepository,
@@ -73,7 +72,6 @@ export async function deleteUserAndRelatedData(
await createCurrentTrustedDeviceRepository().deleteByUserId(userId);
await createCurrentRoleRepository().removeAllRolesFromUser(userId);
await createCurrentAiRepository().deleteByUserId(userId);
await createCurrentNotificationChannelRepository().deleteByUserId(userId);
await createCurrentAuditLogRepository().anonymizeByUserId(userId);
@@ -288,8 +288,6 @@ export function registerHostBulkRoutes(
simpleUpdates.enableTmuxMonitor = updates.enableTmuxMonitor;
if (typeof updates.enableTerminalToolbar === "boolean")
simpleUpdates.enableTerminalToolbar = updates.enableTerminalToolbar;
if (typeof updates.enableAiAssistant === "boolean")
simpleUpdates.enableAiAssistant = updates.enableAiAssistant;
if (typeof updates.statusCheckEnabled === "boolean")
simpleUpdates.statusCheckEnabled = updates.statusCheckEnabled;
@@ -751,7 +749,6 @@ export function registerHostBulkRoutes(
enableFileManager: hostData.enableFileManager !== false,
enableTmuxMonitor: hostData.enableTmuxMonitor || false,
enableTerminalToolbar: hostData.enableTerminalToolbar !== false,
enableAiAssistant: hostData.enableAiAssistant || false,
enableCommandHistory: hostData.enableCommandHistory !== false,
showTerminalInSidebar: hostData.showTerminalInSidebar ? 1 : 0,
showFileManagerInSidebar: hostData.showFileManagerInSidebar ? 1 : 0,
@@ -1012,7 +1009,6 @@ export function registerHostBulkRoutes(
enableFileManager: true,
enableTmuxMonitor: false,
enableTerminalToolbar: true,
enableAiAssistant: false,
showTerminalInSidebar: 0,
showFileManagerInSidebar: 0,
showTunnelInSidebar: 0,
@@ -178,7 +178,6 @@ export type NormalizedImportedHost = Record<string, unknown> & {
enableProxmox?: unknown;
enableTmuxMonitor?: unknown;
enableTerminalToolbar?: unknown;
enableAiAssistant?: unknown;
enableCommandHistory?: unknown;
showTerminalInSidebar?: unknown;
showFileManagerInSidebar?: unknown;
@@ -340,7 +339,6 @@ const CONNECT_LEVEL_FIELDS = new Set([
"enableFileManager",
"enableTmuxMonitor",
"enableTerminalToolbar",
"enableAiAssistant",
"showTerminalInSidebar",
"showFileManagerInSidebar",
"showTunnelInSidebar",
@@ -436,7 +434,6 @@ export function transformHostResponse(
enableFileManager: host.enableFileManager !== false,
enableTmuxMonitor: !!host.enableTmuxMonitor,
enableTerminalToolbar: host.enableTerminalToolbar !== false,
enableAiAssistant: !!host.enableAiAssistant,
showTerminalInSidebar: !!host.showTerminalInSidebar,
showFileManagerInSidebar: !!host.showFileManagerInSidebar,
showTunnelInSidebar: !!host.showTunnelInSidebar,
+4 -62
View File
@@ -15,17 +15,15 @@ import {
pickResolvedUsername,
} from "../../hosts/credential-username.js";
import { emitInternalEvent } from "../../hosts/internal-events.js";
import { deleteOwnedHost } from "../../hosts/delete-host.js";
import {
createCurrentCredentialRepository,
createCurrentOpksshTokenRepository,
createCurrentRecentActivityRepository,
createCurrentSshCredentialUsageRepository,
createCurrentRbacAccessRepository,
createCurrentRoleRepository,
createCurrentHostResolutionRepository,
createCurrentHostRepository,
createCurrentUserRepository,
createCurrentSyncTombstoneRepository,
createCurrentSharedHostAuthOverrideRepository,
} from "../repositories/factory.js";
import {
@@ -202,7 +200,6 @@ router.post(
enableProxmox,
enableTmuxMonitor,
enableTerminalToolbar,
enableAiAssistant,
showTerminalInSidebar,
showFileManagerInSidebar,
showTunnelInSidebar,
@@ -332,7 +329,6 @@ router.post(
scpLegacy: scpLegacy ? 1 : 0,
enableTmuxMonitor: enableTmuxMonitor ? 1 : 0,
enableTerminalToolbar: enableTerminalToolbar === false ? 0 : 1,
enableAiAssistant: enableAiAssistant ? 1 : 0,
showTerminalInSidebar: showTerminalInSidebar ? 1 : 0,
showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0,
showTunnelInSidebar: showTunnelInSidebar ? 1 : 0,
@@ -756,7 +752,6 @@ router.post(
enableWebUi: false,
enableTmuxMonitor: false,
enableTerminalToolbar: true,
enableAiAssistant: false,
showTerminalInSidebar: true,
showFileManagerInSidebar: false,
showTunnelInSidebar: false,
@@ -886,7 +881,6 @@ router.put(
enableProxmox,
enableTmuxMonitor,
enableTerminalToolbar,
enableAiAssistant,
showTerminalInSidebar,
showFileManagerInSidebar,
showTunnelInSidebar,
@@ -1017,7 +1011,6 @@ router.put(
scpLegacy: scpLegacy ? 1 : 0,
enableTmuxMonitor: enableTmuxMonitor ? 1 : 0,
enableTerminalToolbar: enableTerminalToolbar === false ? 0 : 1,
enableAiAssistant: enableAiAssistant ? 1 : 0,
showTerminalInSidebar: showTerminalInSidebar ? 1 : 0,
showFileManagerInSidebar: showFileManagerInSidebar ? 1 : 0,
showTunnelInSidebar: showTunnelInSidebar ? 1 : 0,
@@ -2084,7 +2077,6 @@ router.get(
enableProxmoxStats: !!proxmoxSettings?.enableProxmoxStats,
enableTmuxMonitor: !!resolvedHost.enableTmuxMonitor,
enableTerminalToolbar: resolvedHost.enableTerminalToolbar !== false,
enableAiAssistant: !!resolvedHost.enableAiAssistant,
showTerminalInSidebar: !!resolvedHost.showTerminalInSidebar,
showFileManagerInSidebar: !!resolvedHost.showFileManagerInSidebar,
showTunnelInSidebar: !!resolvedHost.showTunnelInSidebar,
@@ -2241,7 +2233,6 @@ router.get(
enableTmuxMonitor: !!resolvedHost.enableTmuxMonitor,
enableTerminalToolbar:
resolvedHost.enableTerminalToolbar !== false,
enableAiAssistant: !!resolvedHost.enableAiAssistant,
showTerminalInSidebar: !!resolvedHost.showTerminalInSidebar,
showFileManagerInSidebar: !!resolvedHost.showFileManagerInSidebar,
showTunnelInSidebar: !!resolvedHost.showTunnelInSidebar,
@@ -2418,13 +2409,9 @@ router.delete(
hostId: parseInt(hostId),
});
try {
const hostToDelete =
await createCurrentHostResolutionRepository().findHostByIdForUser(
Number(hostId),
userId,
);
const deleted = await deleteOwnedHost(userId, Number(hostId));
if (!hostToDelete) {
if (!deleted) {
sshLogger.warn("SSH host not found for deletion", {
operation: "host_delete",
hostId: parseInt(hostId),
@@ -2433,34 +2420,6 @@ router.delete(
return res.status(404).json({ error: "SSH host not found" });
}
const numericHostId = Number(hostId);
// file manager recent/pinned/shortcuts, transfer_recent, command
// history and session recordings cascade on the host's refHost()
// foreign key, as the file-manager, ssh-terminal and
// session-recording plugins' adopted tables.
await createCurrentSshCredentialUsageRepository().deleteByHostId(
numericHostId,
);
await createCurrentRecentActivityRepository().deleteByHostId(
numericHostId,
);
await createCurrentRbacAccessRepository().deleteHostAccessForHost(
numericHostId,
);
await createCurrentHostRepository().deleteForUser(userId, numericHostId);
if (hostToDelete.syncId) {
await createCurrentSyncTombstoneRepository().record(
userId,
"hosts",
hostToDelete.syncId,
);
}
databaseLogger.success("SSH host deleted", {
operation: "host_delete_success",
userId,
@@ -2474,29 +2433,12 @@ router.delete(
action: "delete_host",
resourceType: "host",
resourceId: hostId,
resourceName: hostToDelete.name ?? hostToDelete.ip,
resourceName: deleted.name,
ipAddress: dhIp,
userAgent: dhUa,
success: true,
});
emitInternalEvent("host_deleted", userId, numericHostId, {
name: hostToDelete.name ?? hostToDelete.ip,
});
try {
pluginEvents.emit(TOPICS.hostDeleted, {
hostId: numericHostId,
userId,
});
} catch (err) {
sshLogger.warn("Failed to publish host deletion event", {
operation: "host_delete",
hostId: numericHostId,
error: err instanceof Error ? err.message : String(err),
});
}
res.json({ message: "SSH host deleted" });
} catch (err) {
sshLogger.error("Failed to delete SSH host from database", err, {
@@ -32,8 +32,6 @@ const pickPreferences = (row?: UserPreferenceRecord | null) => ({
disableUpdateCheck: row?.disableUpdateCheck ?? null,
confirmTabClose: row?.confirmTabClose ?? null,
hiddenRailTabs: row?.hiddenRailTabs ?? null,
aiAssistantEnabled: row?.aiAssistantEnabled ?? null,
aiReadOnlyCommands: row?.aiReadOnlyCommands ?? null,
compactHostView: row?.compactHostView ?? null,
statusColorScheme: row?.statusColorScheme ?? null,
customThemes: row?.customThemes ?? null,
@@ -227,8 +225,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
disableUpdateCheck,
confirmTabClose,
hiddenRailTabs,
aiAssistantEnabled,
aiReadOnlyCommands,
customThemes,
customKeybindings,
terminalDefaults,
@@ -249,8 +245,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
disableUpdateCheck?: boolean | null;
confirmTabClose?: boolean | null;
hiddenRailTabs?: string | null;
aiAssistantEnabled?: boolean | null;
aiReadOnlyCommands?: boolean | null;
customThemes?: string | null;
customKeybindings?: string | null;
terminalDefaults?: string | null;
@@ -392,8 +386,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
confirmSnippetExecution,
disableUpdateCheck,
confirmTabClose,
aiAssistantEnabled,
aiReadOnlyCommands,
};
for (const [key, value] of Object.entries(boolFields)) {
if (value !== undefined && value !== null && typeof value !== "boolean") {
@@ -407,10 +399,6 @@ router.put("/", authenticateJWT, async (req: Request, res: Response) => {
if (language !== undefined) updates.language = language;
if (storageMode !== undefined) updates.storageMode = storageMode;
if (hiddenRailTabs !== undefined) updates.hiddenRailTabs = hiddenRailTabs;
if (aiAssistantEnabled !== undefined)
updates.aiAssistantEnabled = aiAssistantEnabled;
if (aiReadOnlyCommands !== undefined)
updates.aiReadOnlyCommands = aiReadOnlyCommands;
if (commandAutocomplete !== undefined)
updates.commandAutocomplete = commandAutocomplete;
if (commandPaletteEnabled !== undefined)