feat(ui): External-Agent-API + Besitzübertragung UI (UI-Backlog Module 15+16/16)
Check Cross-Plugin Imports / check (push) Has been cancelled

Modul 15 External-Agent (ai_assistant-Plugin, Manifest settings_page):
- SettingsExternalAgents.tsx: Agentenliste mit curl-Snippets (run/status/stream),
  Copy-Buttons, Bearer-Token-Hinweis, Rate-Limit-Doku, Token-Link
- api/externalAgent.ts (useAiAgents, buildCurlSnippets, curlCommand)
- Manifest: settings_pages +external-agents (order 61, permission ai:read)
- Komponenten-Map regeneriert (43)

Modul 16 Ownership-Transfer (Core):
- SettingsOwnership.tsx: Admin-Gate, From/To-User-Selects, 10 Entity-Type-Chips,
  ConfirmDialog, Ergebnis-Tabelle
- api/ownership.ts (useTransferOwnership, OWNERSHIP_ENTITY_TYPES)
- Route /settings/ownership + Nav-Eintrag

i18n de/en +28 Keys. Vitest 12/12, tsc 0, Build OK, ruff OK, Manifest-Import OK
This commit is contained in:
Agent Zero
2026-09-16 00:32:45 +02:00
parent f38dfdeea1
commit e8e07fa13a
12 changed files with 789 additions and 2 deletions
+74
View File
@@ -0,0 +1,74 @@
/**
* External Agent API integration client (UI-Backlog module 15/16).
*
* Backend: /api/v1/external/agent (ai_assistant plugin, external_api.py).
* - POST /{agent_id}/run → run agent, return full response (Bearer auth)
* - GET /{agent_id}/status → agent config + run stats (Bearer auth)
* - POST /{agent_id}/stream → SSE stream response (Bearer auth)
*
* These endpoints are Bearer-token-only (no session cookies) — they are
* meant for EXTERNAL systems, not the SPA itself. The UI is therefore an
* integration page: it lists the tenant's AI agents (via the existing
* /ai/agents endpoint) and renders copy-paste curl snippets per agent.
*/
import { useQuery } from '@tanstack/react-query';
import { fetchAgents, type AIAgent } from '@/api/ai';
/** TanStack wrapper around the existing fetchAgents (session auth). */
export function useAiAgents() {
return useQuery({
queryKey: ['ai-agents'],
queryFn: () => fetchAgents(),
});
}
export interface CurlSnippet {
method: 'POST' | 'GET';
endpoint: string;
body?: string;
description: string;
}
/** Base URL of this deployment (works for prod and local dev). */
export function externalBaseUrl(): string {
return `${window.location.origin}/api/v1/external/agent`;
}
/** Build the copy-paste curl examples for one agent. */
export function buildCurlSnippets(agentId: string): CurlSnippet[] {
const base = externalBaseUrl();
return [
{
method: 'POST',
endpoint: `${base}/${agentId}/run`,
body: JSON.stringify({ message: 'Hello, summarize my open tasks' }),
description: 'runAgent',
},
{
method: 'GET',
endpoint: `${base}/${agentId}/status`,
description: 'getStatus',
},
{
method: 'POST',
endpoint: `${base}/${agentId}/stream`,
body: JSON.stringify({ message: 'Hello, stream me an answer' }),
description: 'streamAgent (SSE)',
},
];
}
/** Render a single-line curl command for a snippet. */
export function curlCommand(snippet: CurlSnippet): string {
const parts: string[] = [];
parts.push(`curl -X ${snippet.method} '${snippet.endpoint}'`);
parts.push(`-H 'Authorization: Bearer <API_TOKEN>'`);
if (snippet.body) {
parts.push(`-H 'Content-Type: application/json'`);
parts.push(`-d '${snippet.body}'`);
}
return parts.join(' ');
}
export type { AIAgent };
+46
View File
@@ -0,0 +1,46 @@
/**
* Ownership transfer API client (UI-Backlog module 16/16).
*
* Backend: /api/v1/ownership (app/routes/owner_transfer.py, require_admin).
* - POST /transfer → bulk-transfer records from one user to another
*
* Entity types: contacts, addresses, attachments, bank_accounts,
* workflows, sequences, saved_filters, saved_views, webhooks,
* notifications (mirrored from owner_transfer_service.ENTITY_TABLES).
*/
import { useMutation } from '@tanstack/react-query';
import { apiPost } from '@/api/client';
export const OWNERSHIP_ENTITY_TYPES = [
'contacts',
'addresses',
'attachments',
'bank_accounts',
'workflows',
'sequences',
'saved_filters',
'saved_views',
'webhooks',
'notifications',
] as const;
export type OwnershipEntityType = (typeof OWNERSHIP_ENTITY_TYPES)[number];
export interface OwnershipTransferPayload {
from_user_id: string;
to_user_id: string;
entity_types?: string[] | null;
}
export interface OwnershipTransferResult {
message: string;
results: Record<string, number>;
}
export function useTransferOwnership() {
return useMutation({
mutationFn: (data: OwnershipTransferPayload) =>
apiPost<OwnershipTransferResult>('/ownership/transfer', data),
});
}