Use middleware to verify secret key (#4338)
This commit is contained in:
@@ -1504,6 +1504,26 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/status": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"super::routes::health"
|
||||
],
|
||||
"operationId": "status",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "ok",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// This file is auto-generated by @hey-api/openapi-ts
|
||||
|
||||
import type { Options as ClientOptions, TDataShape, Client } from './client';
|
||||
import type { AddSubRecipesData, AddSubRecipesResponses, AddSubRecipesErrors, ExtendPromptData, ExtendPromptResponses, ExtendPromptErrors, ResumeAgentData, ResumeAgentResponses, ResumeAgentErrors, UpdateSessionConfigData, UpdateSessionConfigResponses, UpdateSessionConfigErrors, StartAgentData, StartAgentResponses, StartAgentErrors, GetToolsData, GetToolsResponses, GetToolsErrors, UpdateAgentProviderData, UpdateAgentProviderResponses, UpdateAgentProviderErrors, UpdateRouterToolSelectorData, UpdateRouterToolSelectorResponses, UpdateRouterToolSelectorErrors, ReadAllConfigData, ReadAllConfigResponses, BackupConfigData, BackupConfigResponses, BackupConfigErrors, CreateCustomProviderData, CreateCustomProviderResponses, CreateCustomProviderErrors, RemoveCustomProviderData, RemoveCustomProviderResponses, RemoveCustomProviderErrors, GetExtensionsData, GetExtensionsResponses, GetExtensionsErrors, AddExtensionData, AddExtensionResponses, AddExtensionErrors, RemoveExtensionData, RemoveExtensionResponses, RemoveExtensionErrors, InitConfigData, InitConfigResponses, InitConfigErrors, UpsertPermissionsData, UpsertPermissionsResponses, UpsertPermissionsErrors, ProvidersData, ProvidersResponses, GetProviderModelsData, GetProviderModelsResponses, GetProviderModelsErrors, ReadConfigData, ReadConfigResponses, ReadConfigErrors, RecoverConfigData, RecoverConfigResponses, RecoverConfigErrors, RemoveConfigData, RemoveConfigResponses, RemoveConfigErrors, UpsertConfigData, UpsertConfigResponses, UpsertConfigErrors, ValidateConfigData, ValidateConfigResponses, ValidateConfigErrors, ConfirmPermissionData, ConfirmPermissionResponses, ConfirmPermissionErrors, ManageContextData, ManageContextResponses, ManageContextErrors, CreateRecipeData, CreateRecipeResponses, CreateRecipeErrors, DecodeRecipeData, DecodeRecipeResponses, DecodeRecipeErrors, DeleteRecipeData, DeleteRecipeResponses, DeleteRecipeErrors, EncodeRecipeData, EncodeRecipeResponses, EncodeRecipeErrors, ListRecipesData, ListRecipesResponses, ListRecipesErrors, ScanRecipeData, ScanRecipeResponses, CreateScheduleData, CreateScheduleResponses, CreateScheduleErrors, DeleteScheduleData, DeleteScheduleResponses, DeleteScheduleErrors, ListSchedulesData, ListSchedulesResponses, ListSchedulesErrors, UpdateScheduleData, UpdateScheduleResponses, UpdateScheduleErrors, InspectRunningJobData, InspectRunningJobResponses, InspectRunningJobErrors, KillRunningJobData, KillRunningJobResponses, PauseScheduleData, PauseScheduleResponses, PauseScheduleErrors, RunNowHandlerData, RunNowHandlerResponses, RunNowHandlerErrors, SessionsHandlerData, SessionsHandlerResponses, SessionsHandlerErrors, UnpauseScheduleData, UnpauseScheduleResponses, UnpauseScheduleErrors, ListSessionsData, ListSessionsResponses, ListSessionsErrors, GetSessionHistoryData, GetSessionHistoryResponses, GetSessionHistoryErrors } from './types.gen';
|
||||
import type { AddSubRecipesData, AddSubRecipesResponses, AddSubRecipesErrors, ExtendPromptData, ExtendPromptResponses, ExtendPromptErrors, ResumeAgentData, ResumeAgentResponses, ResumeAgentErrors, UpdateSessionConfigData, UpdateSessionConfigResponses, UpdateSessionConfigErrors, StartAgentData, StartAgentResponses, StartAgentErrors, GetToolsData, GetToolsResponses, GetToolsErrors, UpdateAgentProviderData, UpdateAgentProviderResponses, UpdateAgentProviderErrors, UpdateRouterToolSelectorData, UpdateRouterToolSelectorResponses, UpdateRouterToolSelectorErrors, ReadAllConfigData, ReadAllConfigResponses, BackupConfigData, BackupConfigResponses, BackupConfigErrors, CreateCustomProviderData, CreateCustomProviderResponses, CreateCustomProviderErrors, RemoveCustomProviderData, RemoveCustomProviderResponses, RemoveCustomProviderErrors, GetExtensionsData, GetExtensionsResponses, GetExtensionsErrors, AddExtensionData, AddExtensionResponses, AddExtensionErrors, RemoveExtensionData, RemoveExtensionResponses, RemoveExtensionErrors, InitConfigData, InitConfigResponses, InitConfigErrors, UpsertPermissionsData, UpsertPermissionsResponses, UpsertPermissionsErrors, ProvidersData, ProvidersResponses, GetProviderModelsData, GetProviderModelsResponses, GetProviderModelsErrors, ReadConfigData, ReadConfigResponses, ReadConfigErrors, RecoverConfigData, RecoverConfigResponses, RecoverConfigErrors, RemoveConfigData, RemoveConfigResponses, RemoveConfigErrors, UpsertConfigData, UpsertConfigResponses, UpsertConfigErrors, ValidateConfigData, ValidateConfigResponses, ValidateConfigErrors, ConfirmPermissionData, ConfirmPermissionResponses, ConfirmPermissionErrors, ManageContextData, ManageContextResponses, ManageContextErrors, CreateRecipeData, CreateRecipeResponses, CreateRecipeErrors, DecodeRecipeData, DecodeRecipeResponses, DecodeRecipeErrors, DeleteRecipeData, DeleteRecipeResponses, DeleteRecipeErrors, EncodeRecipeData, EncodeRecipeResponses, EncodeRecipeErrors, ListRecipesData, ListRecipesResponses, ListRecipesErrors, ScanRecipeData, ScanRecipeResponses, CreateScheduleData, CreateScheduleResponses, CreateScheduleErrors, DeleteScheduleData, DeleteScheduleResponses, DeleteScheduleErrors, ListSchedulesData, ListSchedulesResponses, ListSchedulesErrors, UpdateScheduleData, UpdateScheduleResponses, UpdateScheduleErrors, InspectRunningJobData, InspectRunningJobResponses, InspectRunningJobErrors, KillRunningJobData, KillRunningJobResponses, PauseScheduleData, PauseScheduleResponses, PauseScheduleErrors, RunNowHandlerData, RunNowHandlerResponses, RunNowHandlerErrors, SessionsHandlerData, SessionsHandlerResponses, SessionsHandlerErrors, UnpauseScheduleData, UnpauseScheduleResponses, UnpauseScheduleErrors, ListSessionsData, ListSessionsResponses, ListSessionsErrors, GetSessionHistoryData, GetSessionHistoryResponses, GetSessionHistoryErrors, StatusData, StatusResponses } from './types.gen';
|
||||
import { client as _heyApiClient } from './client.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
|
||||
@@ -415,4 +415,11 @@ export const getSessionHistory = <ThrowOnError extends boolean = false>(options:
|
||||
url: '/sessions/{session_id}',
|
||||
...options
|
||||
});
|
||||
};
|
||||
|
||||
export const status = <ThrowOnError extends boolean = false>(options?: Options<StatusData, ThrowOnError>) => {
|
||||
return (options?.client ?? _heyApiClient).get<StatusResponses, unknown, ThrowOnError>({
|
||||
url: '/status',
|
||||
...options
|
||||
});
|
||||
};
|
||||
@@ -2129,6 +2129,22 @@ export type GetSessionHistoryResponses = {
|
||||
|
||||
export type GetSessionHistoryResponse = GetSessionHistoryResponses[keyof GetSessionHistoryResponses];
|
||||
|
||||
export type StatusData = {
|
||||
body?: never;
|
||||
path?: never;
|
||||
query?: never;
|
||||
url: '/status';
|
||||
};
|
||||
|
||||
export type StatusResponses = {
|
||||
/**
|
||||
* ok
|
||||
*/
|
||||
200: string;
|
||||
};
|
||||
|
||||
export type StatusResponse = StatusResponses[keyof StatusResponses];
|
||||
|
||||
export type ClientOptions = {
|
||||
baseUrl: `${string}://${string}` | (string & {});
|
||||
};
|
||||
+3
@@ -53,6 +53,9 @@ export const ToolSelectionStrategySection = () => {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': await window.electron.getSecretKey(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_id: '', // TODO(jack) add the session id, or remove from this request payload
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react';
|
||||
import { client } from '../api/client.gen';
|
||||
|
||||
interface ClientInitializationContextType {
|
||||
isInitialized: boolean;
|
||||
initializationError: Error | null;
|
||||
}
|
||||
|
||||
// Track if client has been initialized to avoid duplicate initialization
|
||||
let clientInitialized = false;
|
||||
|
||||
async function ensureClientInitialized() {
|
||||
if (clientInitialized) return;
|
||||
client.setConfig({
|
||||
baseUrl: window.appConfig.get('GOOSE_API_HOST') + ':' + window.appConfig.get('GOOSE_PORT'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': await window.electron.getSecretKey(),
|
||||
},
|
||||
});
|
||||
clientInitialized = true;
|
||||
}
|
||||
|
||||
const ClientInitializationContext = createContext<ClientInitializationContextType | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
interface ClientInitializationProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ClientInitializationProvider: React.FC<ClientInitializationProviderProps> = ({
|
||||
children,
|
||||
}) => {
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [initializationError, setInitializationError] = useState<Error | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const initializeClient = async () => {
|
||||
try {
|
||||
await ensureClientInitialized();
|
||||
setIsInitialized(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize API client:', error);
|
||||
setInitializationError(error instanceof Error ? error : new Error('Unknown error'));
|
||||
}
|
||||
};
|
||||
|
||||
initializeClient();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ClientInitializationContext.Provider value={{ isInitialized, initializationError }}>
|
||||
{children}
|
||||
</ClientInitializationContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useClientInitialization = () => {
|
||||
const context = useContext(ClientInitializationContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useClientInitialization must be used within a ClientInitializationProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
// Helper component to ensure initialization before rendering children
|
||||
export const RequireClientInitialization: React.FC<{ children: ReactNode }> = ({ children }) => {
|
||||
const { isInitialized, initializationError } = useClientInitialization();
|
||||
|
||||
if (initializationError) {
|
||||
throw initializationError;
|
||||
}
|
||||
|
||||
if (!isInitialized) {
|
||||
return (
|
||||
<div className="flex justify-center items-center py-12">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-textStandard"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
+19
-25
@@ -8,6 +8,9 @@ import log from './utils/logger';
|
||||
import { App } from 'electron';
|
||||
import { Buffer } from 'node:buffer';
|
||||
|
||||
import { status } from './api';
|
||||
import { client } from './api/client.gen';
|
||||
|
||||
// Find an available port to start goosed on
|
||||
export const findAvailablePort = (): Promise<number> => {
|
||||
return new Promise((resolve, _reject) => {
|
||||
@@ -25,33 +28,16 @@ export const findAvailablePort = (): Promise<number> => {
|
||||
|
||||
// Goose process manager. Take in the app, port, and directory to start goosed in.
|
||||
// Check if goosed server is ready by polling the status endpoint
|
||||
const checkServerStatus = async (
|
||||
port: number,
|
||||
maxAttempts?: number,
|
||||
interval: number = 100
|
||||
): Promise<boolean> => {
|
||||
if (maxAttempts === undefined) {
|
||||
const isTemporalEnabled = process.env.GOOSE_SCHEDULER_TYPE === 'temporal';
|
||||
maxAttempts = isTemporalEnabled ? 200 : 80;
|
||||
log.info(
|
||||
`Using ${maxAttempts} max attempts (temporal scheduling: ${isTemporalEnabled ? 'enabled' : 'disabled'})`
|
||||
);
|
||||
}
|
||||
|
||||
const statusUrl = `http://127.0.0.1:${port}/status`;
|
||||
log.info(`Checking server status at ${statusUrl}`);
|
||||
|
||||
const checkServerStatus = async (): Promise<boolean> => {
|
||||
const interval = 100;
|
||||
const maxAttempts = 200;
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
try {
|
||||
const response = await fetch(statusUrl);
|
||||
if (response.ok) {
|
||||
log.info(`Server is ready after ${attempt} attempts`);
|
||||
return true;
|
||||
}
|
||||
await status({ throwOnError: true });
|
||||
return true;
|
||||
} catch {
|
||||
// Expected error when server isn't ready yet
|
||||
if (attempt === maxAttempts) {
|
||||
log.error(`Server failed to respond after ${maxAttempts} attempts`);
|
||||
log.error(`Server failed to respond after ${(interval * maxAttempts) / 1000} seconds`);
|
||||
}
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, interval));
|
||||
@@ -65,7 +51,7 @@ const connectToExternalBackend = async (
|
||||
): Promise<[number, string, ChildProcess]> => {
|
||||
log.info(`Using external goosed backend on port ${port}`);
|
||||
|
||||
const isReady = await checkServerStatus(port);
|
||||
const isReady = await checkServerStatus();
|
||||
if (!isReady) {
|
||||
throw new Error(`External goosed server not accessible on port ${port}`);
|
||||
}
|
||||
@@ -266,8 +252,16 @@ export const startGoosed = async (
|
||||
throw err; // Propagate the error
|
||||
});
|
||||
|
||||
client.setConfig({
|
||||
baseUrl: `http://127.0.0.1:${port}`,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': serverSecret,
|
||||
},
|
||||
});
|
||||
|
||||
// Wait for the server to be ready
|
||||
const isReady = await checkServerStatus(port);
|
||||
const isReady = await checkServerStatus();
|
||||
log.info(`Goosed isReady ${isReady}`);
|
||||
|
||||
const try_kill_goose = () => {
|
||||
|
||||
@@ -610,7 +610,7 @@ const createChat = async (
|
||||
contextIsolation: true,
|
||||
additionalArguments: [
|
||||
JSON.stringify({
|
||||
...appConfig, // Use the potentially updated appConfig
|
||||
...appConfig,
|
||||
GOOSE_PORT: port,
|
||||
GOOSE_WORKING_DIR: working_dir,
|
||||
REQUEST_DIR: dir,
|
||||
|
||||
+18
-18
@@ -1,30 +1,30 @@
|
||||
import React, { Suspense, lazy } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { ConfigProvider } from './components/ConfigContext';
|
||||
import {
|
||||
ClientInitializationProvider,
|
||||
RequireClientInitialization,
|
||||
} from './contexts/ClientInitializationContext';
|
||||
import { ErrorBoundary } from './components/ErrorBoundary';
|
||||
import { patchConsoleLogging } from './utils';
|
||||
import SuspenseLoader from './suspense-loader';
|
||||
|
||||
patchConsoleLogging();
|
||||
import { client } from './api/client.gen';
|
||||
|
||||
const App = lazy(() => import('./App'));
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<ClientInitializationProvider>
|
||||
(async () => {
|
||||
client.setConfig({
|
||||
baseUrl: window.appConfig.get('GOOSE_API_HOST') + ':' + window.appConfig.get('GOOSE_PORT'),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': await window.electron.getSecretKey(),
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<Suspense fallback={SuspenseLoader()}>
|
||||
<RequireClientInitialization>
|
||||
<ConfigProvider>
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
</ConfigProvider>
|
||||
</RequireClientInitialization>
|
||||
<ConfigProvider>
|
||||
<ErrorBoundary>
|
||||
<App />
|
||||
</ErrorBoundary>
|
||||
</ConfigProvider>
|
||||
</Suspense>
|
||||
</React.StrictMode>
|
||||
</ClientInitializationProvider>
|
||||
);
|
||||
);
|
||||
})();
|
||||
|
||||
@@ -10,9 +10,4 @@ export function snakeToTitleCase(snake: string): string {
|
||||
.split('_')
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export function patchConsoleLogging() {
|
||||
// Intercept console methods
|
||||
return;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user