Platform extensions sketch (#4868)
Co-authored-by: Douwe Osinga <douwe@squareup.com> Co-authored-by: Michael Neale <michael.neale@gmail.com>
This commit is contained in:
+49
-17
@@ -2313,6 +2313,7 @@
|
||||
"description": "Server-sent events client with a URI endpoint",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"uri",
|
||||
"type"
|
||||
],
|
||||
@@ -2325,12 +2326,10 @@
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this extension is bundled with goose",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"type": "string"
|
||||
},
|
||||
"env_keys": {
|
||||
"type": "array",
|
||||
@@ -2367,6 +2366,7 @@
|
||||
"description": "Standard I/O client with command and arguments",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"cmd",
|
||||
"args",
|
||||
"type"
|
||||
@@ -2386,15 +2386,13 @@
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this extension is bundled with goose",
|
||||
"nullable": true
|
||||
},
|
||||
"cmd": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"type": "string"
|
||||
},
|
||||
"env_keys": {
|
||||
"type": "array",
|
||||
@@ -2425,9 +2423,10 @@
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Built-in extension that is part of the goose binary",
|
||||
"description": "Built-in extension that is part of the bundled goose MCP server",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
@@ -2439,12 +2438,10 @@
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this extension is bundled with goose",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"type": "string"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string",
|
||||
@@ -2468,11 +2465,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Platform extensions that have direct access to the agent etc and run in the agent process",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"available_tools": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name used to identify this extension"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"platform"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"description": "Streamable HTTP client with a URI endpoint using MCP Streamable HTTP specification",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"uri",
|
||||
"type"
|
||||
],
|
||||
@@ -2485,12 +2517,10 @@
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this extension is bundled with goose",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"nullable": true
|
||||
"type": "string"
|
||||
},
|
||||
"env_keys": {
|
||||
"type": "array",
|
||||
@@ -2533,6 +2563,7 @@
|
||||
"description": "Frontend-provided tools that will be called through the frontend",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"tools",
|
||||
"type"
|
||||
],
|
||||
@@ -2545,9 +2576,11 @@
|
||||
},
|
||||
"bundled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this extension is bundled with goose",
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"instructions": {
|
||||
"type": "string",
|
||||
"description": "Instructions for how to use these tools",
|
||||
@@ -2577,6 +2610,7 @@
|
||||
"description": "Inline Python code that will be executed using uvx",
|
||||
"required": [
|
||||
"name",
|
||||
"description",
|
||||
"code",
|
||||
"type"
|
||||
],
|
||||
@@ -2600,9 +2634,7 @@
|
||||
"nullable": true
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Description of what the extension does",
|
||||
"nullable": true
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
|
||||
@@ -192,11 +192,8 @@ export type ExtendPromptResponse = {
|
||||
*/
|
||||
export type ExtensionConfig = {
|
||||
available_tools?: Array<string>;
|
||||
/**
|
||||
* Whether this extension is bundled with goose
|
||||
*/
|
||||
bundled?: boolean | null;
|
||||
description?: string | null;
|
||||
description: string;
|
||||
env_keys?: Array<string>;
|
||||
envs?: Envs;
|
||||
/**
|
||||
@@ -209,12 +206,9 @@ export type ExtensionConfig = {
|
||||
} | {
|
||||
args: Array<string>;
|
||||
available_tools?: Array<string>;
|
||||
/**
|
||||
* Whether this extension is bundled with goose
|
||||
*/
|
||||
bundled?: boolean | null;
|
||||
cmd: string;
|
||||
description?: string | null;
|
||||
description: string;
|
||||
env_keys?: Array<string>;
|
||||
envs?: Envs;
|
||||
/**
|
||||
@@ -225,11 +219,8 @@ export type ExtensionConfig = {
|
||||
type: 'stdio';
|
||||
} | {
|
||||
available_tools?: Array<string>;
|
||||
/**
|
||||
* Whether this extension is bundled with goose
|
||||
*/
|
||||
bundled?: boolean | null;
|
||||
description?: string | null;
|
||||
description: string;
|
||||
display_name?: string | null;
|
||||
/**
|
||||
* The name used to identify this extension
|
||||
@@ -239,11 +230,17 @@ export type ExtensionConfig = {
|
||||
type: 'builtin';
|
||||
} | {
|
||||
available_tools?: Array<string>;
|
||||
/**
|
||||
* Whether this extension is bundled with goose
|
||||
*/
|
||||
bundled?: boolean | null;
|
||||
description?: string | null;
|
||||
description: string;
|
||||
/**
|
||||
* The name used to identify this extension
|
||||
*/
|
||||
name: string;
|
||||
type: 'platform';
|
||||
} | {
|
||||
available_tools?: Array<string>;
|
||||
bundled?: boolean | null;
|
||||
description: string;
|
||||
env_keys?: Array<string>;
|
||||
envs?: Envs;
|
||||
headers?: {
|
||||
@@ -258,10 +255,8 @@ export type ExtensionConfig = {
|
||||
uri: string;
|
||||
} | {
|
||||
available_tools?: Array<string>;
|
||||
/**
|
||||
* Whether this extension is bundled with goose
|
||||
*/
|
||||
bundled?: boolean | null;
|
||||
description: string;
|
||||
/**
|
||||
* Instructions for how to use these tools
|
||||
*/
|
||||
@@ -285,10 +280,7 @@ export type ExtensionConfig = {
|
||||
* Python package dependencies required by this extension
|
||||
*/
|
||||
dependencies?: Array<string> | null;
|
||||
/**
|
||||
* Description of what the extension does
|
||||
*/
|
||||
description?: string | null;
|
||||
description: string;
|
||||
/**
|
||||
* The name used to identify this extension
|
||||
*/
|
||||
|
||||
@@ -38,11 +38,6 @@ export default function UserMessage({ message, onMessageUpdate }: UserMessagePro
|
||||
|
||||
// Effect to handle message content changes and ensure persistence
|
||||
useEffect(() => {
|
||||
// Log content display for debugging
|
||||
window.electron.logInfo(
|
||||
`Displaying content for message: ${message.id} content: ${displayText}`
|
||||
);
|
||||
|
||||
// If we're not editing, update the edit content to match the current message
|
||||
if (!isEditing) {
|
||||
setEditContent(displayText);
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
createExtensionConfig,
|
||||
ExtensionFormData,
|
||||
extensionToFormData,
|
||||
extractExtensionConfig,
|
||||
getDefaultFormData,
|
||||
} from './utils';
|
||||
|
||||
@@ -84,33 +83,25 @@ export default function ExtensionsSection({
|
||||
await getExtensions(true); // Force refresh - this will update the context
|
||||
}, [getExtensions]);
|
||||
|
||||
const handleExtensionToggle = async (extension: FixedExtensionEntry) => {
|
||||
const handleExtensionToggle = async (extensionConfig: FixedExtensionEntry) => {
|
||||
if (customToggle) {
|
||||
await customToggle(extension);
|
||||
await customToggle(extensionConfig);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If extension is enabled, we are trying to toggle if off, otherwise on
|
||||
const toggleDirection = extension.enabled ? 'toggleOff' : 'toggleOn';
|
||||
const extensionConfig = extractExtensionConfig(extension);
|
||||
const toggleDirection = extensionConfig.enabled ? 'toggleOff' : 'toggleOn';
|
||||
|
||||
// eslint-disable-next-line no-useless-catch
|
||||
try {
|
||||
await toggleExtension({
|
||||
toggle: toggleDirection,
|
||||
extensionConfig: extensionConfig,
|
||||
addToConfig: addExtension,
|
||||
toastOptions: { silent: false },
|
||||
sessionId: sessionId,
|
||||
});
|
||||
await toggleExtension({
|
||||
toggle: toggleDirection,
|
||||
extensionConfig: extensionConfig,
|
||||
addToConfig: addExtension,
|
||||
toastOptions: { silent: false },
|
||||
sessionId: sessionId,
|
||||
});
|
||||
|
||||
await fetchExtensions(); // Refresh the list after successful toggle
|
||||
return true; // Indicate success
|
||||
} catch (error) {
|
||||
// Don't refresh the extension list on failure - this allows our visual state rollback to work
|
||||
// The actual state in the config hasn't changed anyway
|
||||
throw error; // Re-throw to let the ExtensionItem component know it failed
|
||||
}
|
||||
await fetchExtensions();
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleConfigureClick = (extension: FixedExtensionEntry) => {
|
||||
|
||||
@@ -49,6 +49,7 @@ describe('Agent API', () => {
|
||||
describe('extensionApiCall', () => {
|
||||
const mockExtensionConfig: ExtensionConfig = {
|
||||
type: 'stdio',
|
||||
description: 'description',
|
||||
name: 'test-extension',
|
||||
cmd: 'python',
|
||||
args: ['script.py'],
|
||||
@@ -237,6 +238,7 @@ describe('Agent API', () => {
|
||||
const mockExtensionConfig: ExtensionConfig = {
|
||||
type: 'stdio',
|
||||
name: 'Test Extension',
|
||||
description: 'Test description',
|
||||
cmd: 'python',
|
||||
args: ['script.py'],
|
||||
};
|
||||
@@ -286,6 +288,7 @@ describe('Agent API', () => {
|
||||
const sseConfig: ExtensionConfig = {
|
||||
type: 'sse',
|
||||
name: 'SSE Extension',
|
||||
description: 'Test description',
|
||||
uri: 'http://localhost:8080/events',
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ type BundledExtension = {
|
||||
id: string;
|
||||
name: string;
|
||||
display_name?: string;
|
||||
description?: string;
|
||||
description: string;
|
||||
enabled: boolean;
|
||||
type: 'builtin' | 'stdio' | 'sse';
|
||||
cmd?: string;
|
||||
@@ -59,18 +59,19 @@ export async function syncBundledExtensions(
|
||||
switch (bundledExt.type) {
|
||||
case 'builtin':
|
||||
extConfig = {
|
||||
name: bundledExt.name,
|
||||
display_name: bundledExt.display_name,
|
||||
type: bundledExt.type,
|
||||
name: bundledExt.name,
|
||||
description: bundledExt.description,
|
||||
display_name: bundledExt.display_name,
|
||||
timeout: bundledExt.timeout ?? 300,
|
||||
bundled: true,
|
||||
};
|
||||
break;
|
||||
case 'stdio':
|
||||
extConfig = {
|
||||
type: bundledExt.type,
|
||||
name: bundledExt.name,
|
||||
description: bundledExt.description,
|
||||
type: bundledExt.type,
|
||||
timeout: bundledExt.timeout,
|
||||
cmd: bundledExt.cmd || '',
|
||||
args: bundledExt.args || [],
|
||||
@@ -81,9 +82,9 @@ export async function syncBundledExtensions(
|
||||
break;
|
||||
case 'sse':
|
||||
extConfig = {
|
||||
type: bundledExt.type,
|
||||
name: bundledExt.name,
|
||||
description: bundledExt.description,
|
||||
type: bundledExt.type,
|
||||
timeout: bundledExt.timeout,
|
||||
uri: bundledExt.uri || '',
|
||||
bundled: true,
|
||||
|
||||
@@ -25,6 +25,7 @@ describe('Extension Manager', () => {
|
||||
const mockExtensionConfig = {
|
||||
type: 'stdio' as const,
|
||||
name: 'test-extension',
|
||||
description: 'test-extension',
|
||||
cmd: 'python',
|
||||
args: ['script.py'],
|
||||
timeout: 300,
|
||||
|
||||
@@ -80,79 +80,42 @@ export default function ExtensionList({
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
// Helper function to get a friendly title from extension name
|
||||
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
||||
let name = '';
|
||||
|
||||
// if it's a builtin, check if there's a display_name (old configs didn't have this field)
|
||||
if (
|
||||
'bundled' in extension &&
|
||||
extension.bundled === true &&
|
||||
'display_name' in extension &&
|
||||
extension.display_name
|
||||
) {
|
||||
// If we have a display_name for a builtin, use it directly
|
||||
return extension.display_name;
|
||||
} else {
|
||||
// For non-builtins or builtins without display_name
|
||||
name = extension.name;
|
||||
}
|
||||
|
||||
// Format the name to be more readable
|
||||
const name = (extension.type === 'builtin' && extension.display_name) || extension.name;
|
||||
return name
|
||||
.split(/[-_]/) // Split on hyphens and underscores
|
||||
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
export interface SubtitleParts {
|
||||
description: string | null;
|
||||
command: string | null;
|
||||
function normalizeExtensionName(name: string): string {
|
||||
return name.toLowerCase().replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
// Helper function to get a subtitle based on extension type and configuration
|
||||
export function getSubtitle(config: ExtensionConfig): SubtitleParts {
|
||||
if (config.type === 'builtin') {
|
||||
// Find matching extension in the data
|
||||
const extensionData = builtInExtensionsData.find(
|
||||
(ext) =>
|
||||
ext.name.toLowerCase().replace(/\s+/g, '') === config.name.toLowerCase().replace(/\s+/g, '')
|
||||
);
|
||||
return {
|
||||
description: extensionData?.description || 'Built-in extension',
|
||||
command: null,
|
||||
};
|
||||
}
|
||||
export function getSubtitle(config: ExtensionConfig) {
|
||||
switch (config.type) {
|
||||
case 'builtin': {
|
||||
const extensionData = builtInExtensionsData.find(
|
||||
(ext) => normalizeExtensionName(ext.name) === normalizeExtensionName(config.name)
|
||||
);
|
||||
return {
|
||||
description: extensionData?.description || config.description || 'Built-in extension',
|
||||
command: null,
|
||||
};
|
||||
}
|
||||
case 'sse':
|
||||
case 'streamable_http': {
|
||||
const prefix = `${config.type.toUpperCase().replace('_', ' ')} extension`;
|
||||
return {
|
||||
description: `${prefix}${config.description ? ': ' + config.description : ''}`,
|
||||
command: config.uri || null,
|
||||
};
|
||||
}
|
||||
|
||||
if (config.type === 'stdio') {
|
||||
// Only include command if it exists
|
||||
const full_command = config.cmd
|
||||
? combineCmdAndArgs(removeShims(config.cmd), config.args)
|
||||
: null;
|
||||
return {
|
||||
description: config.description || null,
|
||||
command: full_command,
|
||||
};
|
||||
default:
|
||||
return {
|
||||
description: config.description || null,
|
||||
command: 'cmd' in config ? combineCmdAndArgs(removeShims(config.cmd), config.args) : null,
|
||||
};
|
||||
}
|
||||
|
||||
if (config.type === 'sse') {
|
||||
const description = config.description
|
||||
? `SSE extension: ${config.description}`
|
||||
: 'SSE extension';
|
||||
const command = config.uri || null;
|
||||
return { description, command };
|
||||
}
|
||||
|
||||
if (config.type === 'streamable_http') {
|
||||
const description = config.description
|
||||
? `Streamable HTTP extension: ${config.description}`
|
||||
: 'Streamable HTTP extension';
|
||||
const command = config.uri || null;
|
||||
return { description, command };
|
||||
}
|
||||
|
||||
return {
|
||||
description: 'Unknown type of extension',
|
||||
command: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
createExtensionConfig,
|
||||
splitCmdAndArgs,
|
||||
combineCmdAndArgs,
|
||||
extractExtensionConfig,
|
||||
replaceWithShims,
|
||||
removeShims,
|
||||
extractCommand,
|
||||
@@ -153,6 +152,7 @@ describe('Extension Utils', () => {
|
||||
const extension: FixedExtensionEntry = {
|
||||
type: 'stdio',
|
||||
name: 'legacy-extension',
|
||||
description: 'legacy',
|
||||
cmd: 'node',
|
||||
args: ['app.js'],
|
||||
enabled: true,
|
||||
@@ -176,6 +176,7 @@ describe('Extension Utils', () => {
|
||||
const extension: FixedExtensionEntry = {
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'developer',
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
@@ -183,7 +184,7 @@ describe('Extension Utils', () => {
|
||||
|
||||
expect(formData).toEqual({
|
||||
name: 'developer',
|
||||
description: '',
|
||||
description: 'developer',
|
||||
type: 'builtin',
|
||||
cmd: undefined,
|
||||
endpoint: undefined,
|
||||
@@ -284,7 +285,7 @@ describe('Extension Utils', () => {
|
||||
it('should create builtin extension config', () => {
|
||||
const formData = {
|
||||
name: 'developer',
|
||||
description: '',
|
||||
description: 'developer',
|
||||
type: 'builtin' as const,
|
||||
cmd: '',
|
||||
endpoint: '',
|
||||
@@ -299,6 +300,7 @@ describe('Extension Utils', () => {
|
||||
expect(config).toEqual({
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'developer',
|
||||
timeout: 300,
|
||||
});
|
||||
});
|
||||
@@ -340,30 +342,6 @@ describe('Extension Utils', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractExtensionConfig', () => {
|
||||
it('should extract extension config from fixed entry', () => {
|
||||
const fixedEntry: FixedExtensionEntry = {
|
||||
type: 'stdio',
|
||||
name: 'test-extension',
|
||||
cmd: 'python',
|
||||
args: ['script.py'],
|
||||
enabled: true,
|
||||
timeout: 300,
|
||||
};
|
||||
|
||||
const config = extractExtensionConfig(fixedEntry);
|
||||
|
||||
expect(config).toEqual({
|
||||
type: 'stdio',
|
||||
name: 'test-extension',
|
||||
cmd: 'python',
|
||||
args: ['script.py'],
|
||||
enabled: true,
|
||||
timeout: 300,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('replaceWithShims', () => {
|
||||
beforeEach(() => {
|
||||
mockElectron.getBinaryPath.mockImplementation((binary: string) => {
|
||||
|
||||
@@ -96,12 +96,11 @@ export function extensionToFormData(extension: FixedExtensionEntry): ExtensionFo
|
||||
|
||||
return {
|
||||
name: extension.name || '',
|
||||
description:
|
||||
extension.type === 'stdio' || extension.type === 'sse' || extension.type === 'streamable_http'
|
||||
? extension.description || ''
|
||||
: '',
|
||||
description: extension.description || '',
|
||||
type:
|
||||
extension.type === 'frontend' || extension.type === 'inline_python'
|
||||
extension.type === 'frontend' ||
|
||||
extension.type === 'inline_python' ||
|
||||
extension.type === 'platform'
|
||||
? 'stdio'
|
||||
: extension.type,
|
||||
cmd: extension.type === 'stdio' ? combineCmdAndArgs(extension.cmd, extension.args) : undefined,
|
||||
@@ -166,6 +165,7 @@ export function createExtensionConfig(formData: ExtensionFormData): ExtensionCon
|
||||
return {
|
||||
type: formData.type,
|
||||
name: formData.name,
|
||||
description: formData.description,
|
||||
timeout: formData.timeout,
|
||||
};
|
||||
}
|
||||
@@ -186,17 +186,6 @@ export function combineCmdAndArgs(cmd: string, args: string[]): string {
|
||||
return [cmd, ...args].join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the ExtensionConfig from a FixedExtensionEntry object
|
||||
* @param fixedEntry - The FixedExtensionEntry object
|
||||
* @returns The ExtensionConfig portion of the object
|
||||
*/
|
||||
export function extractExtensionConfig(fixedEntry: FixedExtensionEntry): ExtensionConfig {
|
||||
// todo: enabled not used?
|
||||
const { ...extensionConfig } = fixedEntry;
|
||||
return extensionConfig;
|
||||
}
|
||||
|
||||
export async function replaceWithShims(cmd: string) {
|
||||
const binaryPathMap: Record<string, string> = {
|
||||
goosed: await window.electron.getBinaryPath('goosed'),
|
||||
|
||||
@@ -56,6 +56,7 @@ export default function PermissionRulesModal({ isOpen, onClose }: PermissionRule
|
||||
enabledExtensions.push({
|
||||
name: 'platform',
|
||||
type: 'builtin',
|
||||
description: 'platform',
|
||||
enabled: true,
|
||||
});
|
||||
// Sort extensions by name to maintain consistent order
|
||||
|
||||
@@ -50,9 +50,11 @@ export default function PermissionSettingsView({ onClose }: { onClose: () => voi
|
||||
const extensionsList = await getExtensions(true); // Force refresh
|
||||
// Filter out disabled extensions
|
||||
const enabledExtensions = extensionsList.filter((extension) => extension.enabled);
|
||||
// TODO(Douwe): this should really be a real extension:
|
||||
enabledExtensions.push({
|
||||
name: 'platform',
|
||||
type: 'builtin',
|
||||
description: 'platform',
|
||||
enabled: true,
|
||||
});
|
||||
// Sort extensions by name to maintain consistent order
|
||||
|
||||
@@ -20,6 +20,7 @@ describe('Recipe Validation', () => {
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
display_name: 'Developer',
|
||||
description: 'Developer',
|
||||
timeout: 300,
|
||||
bundled: true,
|
||||
},
|
||||
@@ -36,6 +37,7 @@ describe('Recipe Validation', () => {
|
||||
{
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'Developer',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -58,6 +60,7 @@ describe('Recipe Validation', () => {
|
||||
{
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'developer',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -75,6 +78,7 @@ describe('Recipe Validation', () => {
|
||||
{
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'developer',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -180,9 +184,9 @@ describe('Recipe Validation', () => {
|
||||
...validRecipe,
|
||||
extensions: [
|
||||
{
|
||||
// Only required fields for builtin extension
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'description',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -550,10 +554,12 @@ describe('Recipe Validation', () => {
|
||||
{
|
||||
type: 'builtin',
|
||||
name: 'developer',
|
||||
description: 'developer',
|
||||
},
|
||||
{
|
||||
type: 'builtin',
|
||||
name: 'computercontroller',
|
||||
description: 'computercontroller',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
syncBundledExtensions,
|
||||
addToAgentOnStartup,
|
||||
} from '../components/settings/extensions';
|
||||
import { extractExtensionConfig } from '../components/settings/extensions/utils';
|
||||
import type { ExtensionConfig, FixedExtensionEntry } from '../components/ConfigContext';
|
||||
import { addSubRecipesToAgent } from '../recipe/add_sub_recipe_on_agent';
|
||||
import {
|
||||
@@ -272,8 +271,7 @@ export const initializeSystem = async (
|
||||
|
||||
options?.setIsExtensionsLoading?.(true);
|
||||
|
||||
const extensionLoadingPromises = enabledExtensions.map(async (extensionEntry) => {
|
||||
const extensionConfig = extractExtensionConfig(extensionEntry);
|
||||
const extensionLoadingPromises = enabledExtensions.map(async (extensionConfig) => {
|
||||
const extensionName = extensionConfig.name;
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user