Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4561907
feat(ai): add AI provider selector and key management
olexii4 Apr 2, 2026
7e32091
fix(ai): adjust icon alignment and tooltip spacing
olexii4 Apr 4, 2026
4768518
fix(ai): dispatch request action after authorization check
olexii4 Apr 4, 2026
8b3ac5a
fix(ai): improve AI Provider Keys table for small screens
olexii4 Apr 4, 2026
7df6d5f
fix(ai): add hover effect to API Key tooltip icon
olexii4 Apr 4, 2026
b1f2842
fix(ai): polish UI alignment and move AI Providers tab to end
olexii4 Apr 5, 2026
a5300e4
feat(ai): move AI registry to ConfigMap and hide widgets when empty
olexii4 Apr 5, 2026
ee9cf21
test(ai): add unit tests for AI registry and provider components
olexii4 Apr 6, 2026
eae65d8
fix(ai): update injector image references from okurinny to oorel
olexii4 Apr 6, 2026
6a7bc49
fix(ai): update OverviewTab snapshots after AiToolFormGroup condition…
olexii4 Apr 6, 2026
961ee35
fix(ai): add aiConfig state to startWorkspace test mock store
olexii4 Apr 6, 2026
fb3de0b
fix(ai): make setupCommand non-fatal in postStart lifecycle hook
olexii4 Apr 6, 2026
1bc6953
feat(ai): auto-update outdated tools, guard key status, bulk delete, …
olexii4 Apr 8, 2026
8bf988c
refactor(ai): move cleanup into init containers and remove postStart …
olexii4 Apr 8, 2026
0bf6dd1
docs(ai): add security note for setupCommand in buildPostStartCommand…
olexii4 Apr 8, 2026
c809bc6
fix(ai): send API key as plain text and let backend handle encoding
olexii4 Apr 8, 2026
4a88959
fix(ai): use stringData instead of data for API key Secret
olexii4 Apr 8, 2026
f9c4fcb
fix(ai): move side effect out of setState updater in AiSelector
olexii4 Apr 8, 2026
6806930
fix(ai): restore cleanup postStart commands for stale binary removal
olexii4 Apr 8, 2026
fa8c40c
fix(ai): run cleanup postStart commands in background
olexii4 Apr 8, 2026
53010a7
fix(ai): remove orphaned cleanup commands on next workspace start
olexii4 Apr 8, 2026
e0a6dc3
fix(ai): address deep review issues across AI widget
olexii4 Apr 8, 2026
7d774bc
chore: replace AI agent name with generic identifier
olexii4 Apr 8, 2026
abf2719
fix(ai): clean up editor volume mounts and PATH when all tools removed
olexii4 Apr 9, 2026
c9f0202
fix(ai): track pending cleanup via annotation to survive cold start
olexii4 Apr 9, 2026
f534093
fix(ai): add type guard for JSON.parse result in AI registry API
olexii4 Apr 9, 2026
6d69682
test(ai): add unit tests to restore function coverage above 85%
olexii4 Apr 9, 2026
82a89ca
fix(ai): persist cleanup annotation during workspace start
olexii4 Apr 10, 2026
2b62037
test(ai): add coverage for AI tool sanitization in startWorkspace
olexii4 Apr 10, 2026
b1dc65c
fix: close ReconnectingWebSocket in test cleanup
olexii4 Apr 30, 2026
599828f
feat(ai): add Tech-Preview label to AI provider cards
olexii4 May 9, 2026
a9deec5
fix(ai): fix PF6 card a11y warnings and align badge styles
olexii4 May 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions packages/common/src/dto/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@ export interface IExternalDevfileRegistry {
url: string;
}

export interface AiToolDefinition {
/** Links this tool to an AiProviderDefinition.id, e.g. 'anthropic/claude' */
providerId: string;
/** Version tag, e.g. 'latest' */
tag: string;
name: string;
url: string;
/** Binary name available in PATH after injection, e.g. 'claude' */
binary: string;
/** init: single binary copied; bundle: full runtime dir copied */
pattern: 'init' | 'bundle';
/** Full injector image, e.g. 'quay.io/oorel/claude-code:next' */
injectorImage: string;
/** API key env var required by this tool, if any */
envVarName?: string;
/** One-time setup command run in the editor container at postStart */
setupCommand?: string;
}

export interface AiProviderDefinition {
id: string;
name: string;
publisher: string;
description?: string;
docsUrl?: string;
/** URL to the provider's SVG icon */
icon?: string;
/** Optional labels, e.g. ['Tech-Preview'] */
tags?: string[];
}

export interface IServerConfig {
containerBuild: {
containerBuildConfiguration?: {
Expand Down Expand Up @@ -153,6 +184,17 @@ export interface IServerConfig {
allowedSourceUrls: string[];
}

/**
* AI tool registry read from a cluster ConfigMap.
* Contains providers (who makes the tool), tools (how to inject it),
* and the default selection for new workspaces.
*/
export interface IAiRegistry {
providers: AiProviderDefinition[];
tools: AiToolDefinition[];
defaultAiProviders: string[];
}

export interface IAdvancedAuthorization {
allowUsers?: string[];
allowGroups?: string[];
Expand Down
6 changes: 6 additions & 0 deletions packages/dashboard-backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { registerCors } from '@/plugins/cors';
import { registerStaticServer } from '@/plugins/staticServer';
import { registerSwagger } from '@/plugins/swagger';
import { registerWebSocket } from '@/plugins/webSocket';
import { registerAiConfigRoutes } from '@/routes/api/aiConfig';
import { registerAiRegistryRoute } from '@/routes/api/aiRegistry';
import { registerAirGapSampleRoute } from '@/routes/api/airGapSample';
import { registerBackupRoutes } from '@/routes/api/backup';
import { registerClusterConfigRoute } from '@/routes/api/clusterConfig';
Expand Down Expand Up @@ -143,5 +145,9 @@ export default async function buildApp(server: FastifyInstance): Promise<unknown
registerAirGapSampleRoute(server),

registerBackupRoutes(server),

registerAiConfigRoutes(server),

registerAiRegistryRoute(isLocalRun(), server),
]);
}
2 changes: 1 addition & 1 deletion packages/dashboard-backend/src/constants/k8s.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Red Hat, Inc. - initial API and implementation
*/

// Generated by Claude Opus 4.6
// Generated by AI Assistant

// DevWorkspaceOperatorConfig constants
export const DEVWORKSPACE_OPERATOR_CONFIG_GROUP = 'controller.devfile.io';
Expand Down
37 changes: 37 additions & 0 deletions packages/dashboard-backend/src/constants/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,43 @@ export const sshKeyParamsSchema: JSONSchema7 = {
required: ['namespace', 'name'],
};

export const aiProviderKeyBodySchema: JSONSchema7 = {
type: 'object',
properties: {
toolId: {
type: 'string',
pattern: '^[a-zA-Z0-9_./-]+$',
maxLength: 63,
},
envVarName: {
type: 'string',
pattern: '^[A-Z][A-Z0-9_]*$',
maxLength: 128,
},
apiKey: {
type: 'string',
minLength: 1,
maxLength: 8192,
},
},
required: ['toolId', 'envVarName', 'apiKey'],
};

export const aiProviderKeyParamsSchema: JSONSchema7 = {
type: 'object',
properties: {
namespace: {
type: 'string',
},
toolId: {
type: 'string',
pattern: '^[a-zA-Z0-9_./-]+$',
maxLength: 63,
},
},
required: ['namespace', 'toolId'],
};

// namespaced schemas

export const namespacedSchema: JSONSchema7 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
*/

import {
IAiProviderKeyApi,
IAiRegistryApi,
IAirGapSampleApi,
IDevWorkspaceApi,
IDevWorkspaceClusterApi,
Expand Down Expand Up @@ -85,6 +87,14 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
get editorsApi(): IEditorsApi {
throw new Error('Method not implemented.');
}

get aiProviderKeyApi(): IAiProviderKeyApi {
throw new Error('Method not implemented.');
}

get aiRegistryApi(): IAiRegistryApi {
throw new Error('Method not implemented.');
}
}

export class DevWorkspaceSingletonClient implements IDevWorkspaceSingletonClient {
Expand Down
12 changes: 12 additions & 0 deletions packages/dashboard-backend/src/devworkspaceClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

import * as k8s from '@kubernetes/client-node';

import { AiProviderKeyApiService } from '@/devworkspaceClient/services/aiProviderKeyApi';
import { AiRegistryApiService } from '@/devworkspaceClient/services/aiRegistryApi';
import { AirGapSampleApiService } from '@/devworkspaceClient/services/airGapSampleApi';
import { DevWorkspaceApiService } from '@/devworkspaceClient/services/devWorkspaceApi';
import { DevWorkspaceClusterApiService } from '@/devworkspaceClient/services/devWorkspaceClusterApiService';
Expand All @@ -31,6 +33,8 @@ import { SshKeysService } from '@/devworkspaceClient/services/sshKeysApi';
import { UserProfileApiService } from '@/devworkspaceClient/services/userProfileApi';
import { WorkspacePreferencesApiService } from '@/devworkspaceClient/services/workspacePreferencesApi';
import {
IAiProviderKeyApi,
IAiRegistryApi,
IAirGapSampleApi,
IDevWorkspaceApi,
IDevWorkspaceClient,
Expand Down Expand Up @@ -132,4 +136,12 @@ export class DevWorkspaceClient implements IDevWorkspaceClient {
get workspacePreferencesApi(): IWorkspacePreferencesApi {
return new WorkspacePreferencesApiService(this.kubeConfig);
}

get aiProviderKeyApi(): IAiProviderKeyApi {
return new AiProviderKeyApiService(this.kubeConfig);
}

get aiRegistryApi(): IAiRegistryApi {
return new AiRegistryApiService(this.kubeConfig);
}
}
Loading
Loading