Improve config file editing and recovery fallback mechanisms (#3082)
This commit is contained in:
+40
-2
@@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user