@aidex/admin-angular
v1.2.0
Published
Aidex Admin Angular adapter — an AdminService wrapping @aidex/admin's AdminController in an Angular Signal. No business logic, no UI components; Angular is the entire concern this package owns.
Readme
@aidex/admin-angular
Installation
pnpm add @aidex/admin-angular @aidex/admin @aidex/admin-theme @aidex/connections @aidex/ai-control @aidex/observability @angular/corenpm install @aidex/admin-angular @aidex/admin @aidex/admin-theme @aidex/connections @aidex/ai-control @aidex/observability @angular/coreAn AdminService wrapping @aidex/admin's AdminController in an Angular
Signal. No business logic lives here — every read and command still goes
through the same AdminController your application already constructed;
this package only translates its existing subscribe()/getSnapshot()
into an Angular-native reactive primitive.
Usage
import { Component, DestroyRef, inject } from '@angular/core';
import { AdminController } from '@aidex/admin';
import { AdminService } from '@aidex/admin-angular';
// 1. Create/provide an AdminController exactly as in @aidex/admin's own
// docs — the same connectionManager/aiControl/observability instances
// your app already wired into Aidex.
const admin = new AdminController({ connectionManager, aiControl, observability });
@Component({
selector: 'app-admin-panel',
template: `
<p>Health: {{ adminService.snapshot().health }}</p>
<button (click)="adminService.setAIEnabled(!adminService.snapshot().aiControl.enabled)">
Toggle AI
</button>
`,
})
export class AdminPanelComponent {
// 2. Inject/construct the service with the controller and the current
// DestroyRef — cleanup (unsubscribe) happens automatically when this
// component is destroyed.
protected readonly adminService = new AdminService(admin, inject(DestroyRef));
// 3. Read the reactive snapshot directly in the template via the signal
// call above, or in code via `this.adminService.snapshot()`.
disableConnection(id: string) {
// 4. Invoke a command — delegates straight to AdminController.
this.adminService.disableConnection(id);
}
}Rendering the AI Control Center
AdminService is state-only — it has no visual components, and won't grow
any. The AI Control Center's actual dashboard (Overview, Executions,
Routing, Guardrails, Connections, Providers, AI Control, Observability)
already exists as a framework-neutral Custom Element,
<aidex-control-center>, in @aidex/admin-elements. Angular hosts it
directly rather than receiving a second, duplicate implementation:
pnpm add @aidex/admin-elementsimport { Component, CUSTOM_ELEMENTS_SCHEMA, DestroyRef, ElementRef, inject, viewChild } from '@angular/core';
import { AdminController } from '@aidex/admin';
import { AdminService } from '@aidex/admin-angular';
import '@aidex/admin-elements'; // registers <aidex-control-center> and friends
const admin = new AdminController({ connectionManager, aiControl, observability });
@Component({
selector: 'app-control-center',
// CUSTOM_ELEMENTS_SCHEMA tells Angular's template compiler that
// <aidex-control-center> is a real, non-Angular element — no Angular
// wrapper component is needed for this.
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<aidex-control-center #cc></aidex-control-center>`,
})
export class ControlCenterComponent {
// Same admin instance AdminService already wraps — one controller, one
// subscription per consumer, no second store.
protected readonly adminService = new AdminService(admin, inject(DestroyRef));
private readonly cc = viewChild.required<ElementRef<HTMLElement & { controller?: AdminController }>>('cc');
ngAfterViewInit(): void {
// `.controller` is a plain DOM property (a live object, not a string),
// the same assignment `@aidex/admin-react`/`@aidex/admin-vue` use.
this.cc().nativeElement.controller = admin;
}
}AdminService remains available alongside the hosted element for any
template binding that wants direct, Angular-native signal access to
AdminSnapshot (e.g. a custom header showing adminService.snapshot().health)
without going through the Control Center's own UI.
Design notes
- Not a singleton, not
providedIn: 'root'.AdminServiceis constructed explicitly with theAdminControlleryour app already owns — the same instance drivingAidex— mirroring@aidex/admin-react'suseAdmin(controller). snapshotis a read-onlySignal<AdminSnapshot>, kept in sync viaAdminController.subscribe(). Cleanup is automatic: the constructor registers the returned unsubscribe function withdestroyRef.onDestroy().- No duplicated state.
AdminControllerremains the single source of truth; this service caches nothing beyond the current signal value. - New
AdminSnapshotfields need no adapter change.snapshot()already carries the wholeAdminSnapshot—providers/executionsare available with zero changes here. Every command method also forwards the same optional trailingactorargumentAdminControlleraccepts (e.g.adminService.setAIEnabled(false, 'user-42')), for audit-event attribution —AdminServicehas no auth model of its own.
