Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## [Coming up]

### Added

- **Admin agent file editor**: The admin console now includes `/admin/agents`
for editing each registered agent's allowlisted workspace bootstrap markdown
files, with saved revision history and restore controls.

### Changed

- **Interactive TUI approval picker**: Local TUI approval requests open a
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ hybridclaw tui
Open locally:

- Chat UI: `http://127.0.0.1:9090/chat`
- Admin UI: `http://127.0.0.1:9090/admin` for channels, scheduler, audit, and config
- Admin UI: `http://127.0.0.1:9090/admin` for channels, agent files,
scheduler, audit, and config
- Agents UI: `http://127.0.0.1:9090/agents`
- OpenAI-compatible API: `http://127.0.0.1:9090/v1/models` and `http://127.0.0.1:9090/v1/chat/completions`

Expand All @@ -92,7 +93,8 @@ operator and maintainer manual lives at
Once the gateway is running, open HybridClaw locally:

- Web Chat: `http://127.0.0.1:9090/chat`
- Admin Console: `http://127.0.0.1:9090/admin` for channels, scheduler, audit, and config
- Admin Console: `http://127.0.0.1:9090/admin` for channels, agent files,
scheduler, audit, and config
- Agent Dashboard: `http://127.0.0.1:9090/agents`
- or connect Slack, WhatsApp, Telegram, Discord, Microsoft Teams, Email

Expand Down Expand Up @@ -130,6 +132,7 @@ Once the gateway is running, open HybridClaw locally:
## Built for real workflows

- channels
- versioned agent workspace prompt files
- browser sessions
- office docs
- skills / plugins / MCP
Expand Down
73 changes: 73 additions & 0 deletions console/src/api/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type {
AdminAdaptiveSkillAmendmentsResponse,
AdminAdaptiveSkillHealthResponse,
AdminAgent,
AdminAgentMarkdownFileResponse,
AdminAgentMarkdownRevisionResponse,
AdminAgentsResponse,
AdminAuditResponse,
AdminChannelConfig,
AdminChannelsResponse,
Expand Down Expand Up @@ -183,6 +187,75 @@ export function fetchAgentsOverview(token: string): Promise<AgentsOverview> {
return requestJson<AgentsOverviewResponse>('/api/agents', { token });
}

export async function fetchAdminAgents(token: string): Promise<AdminAgent[]> {
const payload = await requestJson<AdminAgentsResponse>('/api/admin/agents', {
token,
});
return payload.agents;
}

export function fetchAdminAgentMarkdownFile(
token: string,
params: {
agentId: string;
fileName: string;
},
): Promise<AdminAgentMarkdownFileResponse> {
return requestJson<AdminAgentMarkdownFileResponse>(
`/api/admin/agents/${encodeURIComponent(params.agentId)}/files/${encodeURIComponent(params.fileName)}`,
{ token },
);
}

export function saveAdminAgentMarkdownFile(
token: string,
params: {
agentId: string;
fileName: string;
content: string;
},
): Promise<AdminAgentMarkdownFileResponse> {
return requestJson<AdminAgentMarkdownFileResponse>(
`/api/admin/agents/${encodeURIComponent(params.agentId)}/files/${encodeURIComponent(params.fileName)}`,
{
token,
method: 'PUT',
body: { content: params.content },
},
);
}

export function fetchAdminAgentMarkdownRevision(
token: string,
params: {
agentId: string;
fileName: string;
revisionId: string;
},
): Promise<AdminAgentMarkdownRevisionResponse> {
return requestJson<AdminAgentMarkdownRevisionResponse>(
`/api/admin/agents/${encodeURIComponent(params.agentId)}/files/${encodeURIComponent(params.fileName)}/revisions/${encodeURIComponent(params.revisionId)}`,
{ token },
);
}

export function restoreAdminAgentMarkdownRevision(
token: string,
params: {
agentId: string;
fileName: string;
revisionId: string;
},
): Promise<AdminAgentMarkdownFileResponse> {
return requestJson<AdminAgentMarkdownFileResponse>(
`/api/admin/agents/${encodeURIComponent(params.agentId)}/files/${encodeURIComponent(params.fileName)}/revisions/${encodeURIComponent(params.revisionId)}/restore`,
{
token,
method: 'POST',
},
);
}

export function fetchJobsContext(
token: string,
): Promise<AdminJobsContextResponse> {
Expand Down
48 changes: 48 additions & 0 deletions console/src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,54 @@ export interface AdminSchedulerResponse {
jobs: AdminSchedulerJob[];
}

export interface AdminAgentMarkdownFile {
name: string;
path: string;
exists: boolean;
updatedAt: string | null;
sizeBytes: number | null;
}

export interface AdminAgentMarkdownRevision {
id: string;
createdAt: string;
sizeBytes: number;
sha256: string;
source: 'save' | 'restore';
}

export interface AdminAgent {
id: string;
name: string | null;
model: string | null;
skills: string[] | null;
chatbotId: string | null;
enableRag: boolean | null;
workspace: string | null;
workspacePath: string;
markdownFiles: AdminAgentMarkdownFile[];
}

export interface AdminAgentsResponse {
agents: AdminAgent[];
}

export interface AdminAgentMarkdownFileResponse {
agent: AdminAgent;
file: AdminAgentMarkdownFile & {
content: string;
revisions: AdminAgentMarkdownRevision[];
};
}

export interface AdminAgentMarkdownRevisionResponse {
agent: AdminAgent;
fileName: string;
revision: AdminAgentMarkdownRevision & {
content: string;
};
}

export interface AgentCard {
id: string;
name: string | null;
Expand Down
2 changes: 2 additions & 0 deletions console/src/components/sidebar/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ComponentType } from 'react';
import {
Agents,
Audit,
Channels,
Cog,
Expand Down Expand Up @@ -54,6 +55,7 @@ export const SIDEBAR_NAV_GROUPS: ReadonlyArray<SidebarNavGroup> = [
{
label: 'Configuration',
items: [
{ to: '/agents', label: 'Agent Files', icon: Agents },
{ to: '/skills', label: 'Skills', icon: Skills },
{ to: '/plugins', label: 'Plugins', icon: Plugins },
{ to: '/tools', label: 'Tools', icon: Tools },
Expand Down
8 changes: 8 additions & 0 deletions console/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
} from '@tanstack/react-router';
import { lazy, Suspense } from 'react';
import { AppShell } from './components/app-shell';
import { AgentFilesPage } from './routes/agents';
import { AuditPage } from './routes/audit';
import { ChannelsPage } from './routes/channels';
import { ConfigPage } from './routes/config';
Expand Down Expand Up @@ -52,6 +53,12 @@ const dashboardRoute = createRoute({
component: DashboardPage,
});

const agentFilesRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/agents',
component: AgentFilesPage,
});

const terminalRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/terminal',
Expand Down Expand Up @@ -138,6 +145,7 @@ const toolsRoute = createRoute({

const routeTree = rootRoute.addChildren([
dashboardRoute,
agentFilesRoute,
terminalRoute,
gatewayRoute,
sessionsRoute,
Expand Down
Loading
Loading