Improve config file editing and recovery fallback mechanisms (#3082)

This commit is contained in:
Zane
2025-06-26 11:08:57 -07:00
committed by GitHub
parent 39b943ee85
commit 79553e0b8b
8 changed files with 722 additions and 187 deletions
+40 -2
View File
@@ -28,7 +28,13 @@ import 'react-toastify/dist/ReactToastify.css';
import { useConfig, MalformedConfigError } from './components/ConfigContext';
import { ModelAndProviderProvider } from './components/ModelAndProviderContext';
import { addExtensionFromDeepLink as addExtensionFromDeepLinkV2 } from './components/settings/extensions';
import { backupConfig, initConfig, readAllConfig } from './api/sdk.gen';
import {
backupConfig,
initConfig,
readAllConfig,
recoverConfig,
validateConfig,
} from './api/sdk.gen';
import PermissionSettingsView from './components/settings/permission/PermissionSetting';
import { type SessionDetails } from './sessions';
@@ -174,7 +180,39 @@ export default function App() {
await backupConfig({ throwOnError: true });
await initConfig();
} else {
throw new Error('Unable to read config file, it may be malformed');
// Config appears corrupted, try recovery
console.warn('Config file appears corrupted, attempting recovery...');
try {
// First try to validate the config
try {
await validateConfig({ throwOnError: true });
// Config is valid but readAllConfig failed for another reason
throw new Error('Unable to read config file, it may be malformed');
} catch (validateError) {
console.log('Config validation failed, attempting recovery...');
// Try to recover the config
try {
const recoveryResult = await recoverConfig({ throwOnError: true });
console.log('Config recovery result:', recoveryResult);
// Try to read config again after recovery
try {
await readAllConfig({ throwOnError: true });
console.log('Config successfully recovered and loaded');
} catch (retryError) {
console.warn('Config still corrupted after recovery, reinitializing...');
await initConfig();
}
} catch (recoverError) {
console.warn('Config recovery failed, reinitializing...');
await initConfig();
}
}
} catch (recoveryError) {
console.error('Config recovery process failed:', recoveryError);
throw new Error('Unable to read config file, it may be malformed');
}
}
}
+15 -1
View File
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
import type { GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
import { client as _heyApiClient } from './client.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
@@ -100,6 +100,13 @@ export const readConfig = <ThrowOnError extends boolean = false>(options: Option
});
};
export const recoverConfig = <ThrowOnError extends boolean = false>(options?: Options<RecoverConfigData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<RecoverConfigResponse, unknown, ThrowOnError>({
url: '/config/recover',
...options
});
};
export const removeConfig = <ThrowOnError extends boolean = false>(options: Options<RemoveConfigData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<RemoveConfigResponse, unknown, ThrowOnError>({
url: '/config/remove',
@@ -122,6 +129,13 @@ export const upsertConfig = <ThrowOnError extends boolean = false>(options: Opti
});
};
export const validateConfig = <ThrowOnError extends boolean = false>(options?: Options<ValidateConfigData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<ValidateConfigResponse, unknown, ThrowOnError>({
url: '/config/validate',
...options
});
};
export const confirmPermission = <ThrowOnError extends boolean = false>(options: Options<ConfirmPermissionData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
url: '/confirm',
+46
View File
@@ -810,6 +810,29 @@ export type ReadConfigResponses = {
200: unknown;
};
export type RecoverConfigData = {
body?: never;
path?: never;
query?: never;
url: '/config/recover';
};
export type RecoverConfigErrors = {
/**
* Internal server error
*/
500: unknown;
};
export type RecoverConfigResponses = {
/**
* Config recovery attempted
*/
200: string;
};
export type RecoverConfigResponse = RecoverConfigResponses[keyof RecoverConfigResponses];
export type RemoveConfigData = {
body: ConfigKeyQuery;
path?: never;
@@ -860,6 +883,29 @@ export type UpsertConfigResponses = {
export type UpsertConfigResponse = UpsertConfigResponses[keyof UpsertConfigResponses];
export type ValidateConfigData = {
body?: never;
path?: never;
query?: never;
url: '/config/validate';
};
export type ValidateConfigErrors = {
/**
* Config file is corrupted
*/
422: unknown;
};
export type ValidateConfigResponses = {
/**
* Config validation result
*/
200: string;
};
export type ValidateConfigResponse = ValidateConfigResponses[keyof ValidateConfigResponses];
export type ConfirmPermissionData = {
body: PermissionConfirmationRequest;
path?: never;