'use strict'; const { contextBridge, ipcRenderer } = require('electron'); async function invoke(channel, payload) { const result = await ipcRenderer.invoke(channel, payload); if (!result?.ok) { const error = new Error(result?.error?.message || 'ForgeFlow operation failed.'); error.code = result?.error?.code; error.status = result?.error?.status; error.recoverable = result?.error?.recoverable; error.commitSha = result?.error?.commitSha; throw error; } return result.data; } function subscribe(channel, listener) { if (typeof listener !== 'function') return () => {}; const handler = (_event, payload) => listener(payload); ipcRenderer.on(channel, handler); return () => ipcRenderer.removeListener(channel, handler); } contextBridge.exposeInMainWorld( 'forgeflow', Object.freeze({ bootstrap: () => invoke('app:bootstrap'), selectDirectory: (payload) => invoke('dialog:select-directory', payload), selectKeyFile: (payload) => invoke('dialog:select-key-file', payload), selectImageFile: (payload) => invoke('dialog:select-image-file', payload), setupPreflight: (payload) => invoke('setup:preflight', payload), validateGitea: (payload) => invoke('setup:validate-gitea', payload), completeSetup: (payload) => invoke('setup:complete', payload), updateGitea: (payload) => invoke('settings:update-gitea', payload), setWorkspaceRoots: (roots) => invoke('settings:set-roots', { roots }), setAppearance: (appearance) => invoke('settings:set-appearance', { appearance }), setPreferences: (preferences) => invoke('settings:set-preferences', { preferences }), exportConfigurationBackup: (passphrase) => invoke('settings:export-backup', { passphrase }), importConfigurationBackup: (passphrase) => invoke('settings:import-backup', { passphrase }), listAuditEvents: (limit = 250) => invoke('audit:list', { limit }), exportAuditLog: (format = 'json') => invoke('audit:export', { format }), setUpdatePreferences: (updates) => invoke('updates:preferences', { updates }), checkForUpdates: () => invoke('updates:check'), downloadUpdate: () => invoke('updates:download'), applyUpdate: () => invoke('updates:apply'), saveServer: (server, password = '', passphrase = '') => invoke('server:save', { server, password, passphrase }), deleteServer: (serverId) => invoke('server:delete', { serverId }), testServer: (serverId) => invoke('server:test', { serverId }), inspectServerProject: (repository, profileId) => invoke('server:inspect-project', { repository, profileId }), discoverExistingDeployment: (repository, serverId, remoteFolder) => invoke('server:discover-existing', { repository, serverId, remoteFolder, }), refreshRepositories: () => invoke('repositories:refresh'), discoverRepositories: (roots) => invoke('repositories:discover', { roots }), favoriteRepository: (fullName, favorite) => invoke('repository:favorite', { fullName, favorite }), linkRepository: (fullName, localPath) => invoke('repository:link', { fullName, localPath }), unlinkRepository: (fullName) => invoke('repository:unlink', { fullName }), repositoryStatus: (localPath) => invoke('repository:status', { localPath }), repositoryDiff: (localPath, filePath, staged = false) => invoke('repository:diff', { localPath, filePath, staged }), repositoryDiffHunks: (localPath, filePath) => invoke('repository:diff-hunks', { localPath, filePath }), stageHunks: (localPath, filePath, hunkIndexes) => invoke('repository:stage-hunks', { localPath, filePath, hunkIndexes }), conflictState: (localPath) => invoke('repository:conflicts', { localPath }), resolveConflict: (localPath, filePath, resolution) => invoke('repository:resolve-conflict', { localPath, filePath, resolution, }), continueGitOperation: (localPath) => invoke('repository:continue-operation', { localPath }), abortGitOperation: (localPath) => invoke('repository:abort-operation', { localPath }), stageFiles: (localPath, files) => invoke('repository:stage', { localPath, files }), unstageFiles: (localPath, files) => invoke('repository:unstage', { localPath, files }), commit: (localPath, message, files) => invoke('repository:commit', { localPath, message, files }), commitStaged: (localPath, message) => invoke('repository:commit-staged', { localPath, message }), commitStagedAndPush: (localPath, message) => invoke('repository:commit-staged-push', { localPath, message }), commitAndPush: (localPath, message, files) => invoke('repository:commit-push', { localPath, message, files }), push: (localPath) => invoke('repository:push', { localPath }), fetch: (localPath) => invoke('repository:fetch', { localPath }), pull: (localPath) => invoke('repository:pull', { localPath }), history: (localPath, limit = 20) => invoke('repository:history', { localPath, limit }), branchProtection: (fullName, branch) => invoke('repository:branch-protection', { fullName, branch }), pullRequests: (fullName, state = 'open') => invoke('repository:pull-requests', { fullName, state }), createPullRequest: (fullName, title, body, base) => invoke('repository:create-pull-request', { fullName, title, body, base }), branches: (localPath) => invoke('repository:branches', { localPath }), checkoutBranch: (localPath, branch) => invoke('repository:checkout-branch', { localPath, branch }), createBranch: (localPath, branch) => invoke('repository:create-branch', { localPath, branch }), stash: (localPath, message) => invoke('repository:stash', { localPath, message }), stashList: (localPath) => invoke('repository:stash-list', { localPath }), popStash: (localPath, ref) => invoke('repository:stash-pop', { localPath, ref }), indexLockInfo: (localPath) => invoke('repository:index-lock', { localPath }), repairIndexLock: (localPath) => invoke('repository:repair-index-lock', { localPath }), gitRecoveryStatus: (localPath) => invoke('repository:git-recovery-status', { localPath }), repairGitLocks: (localPath, force = false) => invoke('repository:repair-git-locks', { localPath, force }), reconcileRepository: (localPath) => invoke('repository:reconcile', { localPath }), repairRepositorySync: (localPath, strategy) => invoke('repository:repair-sync', { localPath, strategy }), setOrigin: (localPath, remoteUrl) => invoke('repository:set-origin', { localPath, remoteUrl }), normalizeOrigins: () => invoke('repositories:normalize-origins'), cloneRepository: (fullName, mode = 'default') => invoke('repository:clone', { fullName, mode }), openPath: (localPath) => invoke('repository:open-path', { localPath }), openEditor: (localPath, filePath = '', line = 1) => invoke('repository:open-editor', { localPath, filePath, line }), openTerminal: (localPath) => invoke('repository:open-terminal', { localPath }), openExternal: (url) => invoke('external:open', { url }), saveDeploymentProfile: (fullName, profile) => invoke('deployment:save-profile', { fullName, profile }), deploymentPreflight: (repository, profileId) => invoke('deployment:preflight', { repository, profileId }), deleteDeploymentProfile: (fullName, profileId) => invoke('deployment:delete-profile', { fullName, profileId }), deploy: (repository, profileId, sha, options = {}) => invoke('deployment:dispatch', { repository, profileId, sha, note: options.note || '', override: options.override === true, overrideReason: options.overrideReason || '', }), rollback: (repository, profileId, targetSha) => invoke('deployment:rollback', { repository, profileId, targetSha }), healthcheck: (url) => invoke('deployment:health', { url }), refreshProfileState: (fullName, profileId) => invoke('deployment:profile-state', { fullName, profileId }), discoverServerDeployments: () => invoke('deployment:discover-server-workloads'), linkServerWorkload: (repository, serverId, workloadId, deploymentMode = 'push-bundle', remoteFolder = '') => invoke('deployment:link-server-workload', { repository, serverId, workloadId, deploymentMode, remoteFolder }), applyDockerManMetadata: (repository, profileId) => invoke('deployment:apply-dockerman-metadata', { repository, profileId }), reconcileDeployment: (fullName, profileId) => invoke('deployment:reconcile', { fullName, profileId }), refreshOperations: (operationId = null) => invoke('operations:refresh', { operationId }), getOperation: (operationId) => invoke('operations:get', { operationId }), troubleshooterScan: (fullName = null) => invoke('troubleshooter:scan', { fullName }), troubleshooterRepair: (issue) => invoke('troubleshooter:repair', { issue }), troubleshooterAutoRepair: (issues) => invoke('troubleshooter:auto-repair', { issues }), gitValidatorScan: (fullName) => invoke('git-validator:scan', { fullName }), gitValidatorRepair: (fullName, check) => invoke('git-validator:repair', { fullName, check }), diagnosticsStatus: () => invoke('diagnostics:status'), clearDiagnostics: () => invoke('diagnostics:clear'), openDiagnosticsFolder: () => invoke('diagnostics:open-folder'), exportDiagnostics: (privacyMode = 'standard') => invoke('diagnostics:export', { privacyMode }), showDiagnosticBundle: (filePath) => invoke('diagnostics:show-bundle', { filePath }), reportRendererEvent: (level, event, details = {}) => invoke('renderer:report', { level, event, details }), onRepositoriesChanged: (listener) => subscribe('repositories:changed', listener), onOperationsChanged: (listener) => subscribe('operations:changed', listener), onUpdatesChanged: (listener) => subscribe('updates:changed', listener), reset: () => invoke('app:reset'), }), );