Skip to content

AmtocBots Manager — Full Implementation (Phases 2–9)#4

Open
amtocbot-droid wants to merge 14 commits intousers/krk/20260315_initial_setupfrom
users/krk/20260315_phase9_ollama
Open

AmtocBots Manager — Full Implementation (Phases 2–9)#4
amtocbot-droid wants to merge 14 commits intousers/krk/20260315_initial_setupfrom
users/krk/20260315_phase9_ollama

Conversation

@amtocbot-droid
Copy link
Owner

Summary

Complete implementation of the AmtocBots Manager PWA — all phases built on top of the Phase 1 infrastructure foundation.

Phase 2 — .NET 10 Backend Core

  • EF Core + Npgsql, 12-table schema with pgvector, AES-256 encryption on channel tokens
  • DockerService (Docker.DotNet), InstancesController with port allocation, BCrypt API tokens
  • MetricsPollingService → SignalR every 5s, ModelSwitchScheduler (NCrontab), QueueRetryWorker (Redis BRPOP)

Phase 3 — Angular 21 PWA Shell

  • Keycloak OIDC (PKCE, silent renew), SignalrService with typed on<T>() and auto-reconnect
  • ShellComponent, authGuard, roleGuard, authTokenInterceptor

Phase 4+5 — Instance Management UI + Channel Config

  • InstanceStore (signals + SignalR live stats), instance list/create/detail/logs
  • Telegram/Discord/Slack forms + WhatsApp QR poll flow (3s interval, blob URL)
  • OpenClawConfigBuilder writes JSON5 to named Docker volume on config save

Phase 6 — Model Intelligence & Token Management

  • Token usage dashboard: 7/30-day selector, model breakdown bars (blue→amber→red at 40/75%), per-instance table with cost
  • SwitchRulesComponent: threshold + cron auto-switch rules per instance

Phase 7 — Kanban Board

  • KanbanStore with SignalR CardCreated/CardMoved/CardUpdated/CardDeleted listeners, optimistic moves
  • CDK drag-drop board: priority colour dots, due-date overdue highlight, WIP limit badge, bot-created icon

Phase 8 — Chat System

  • ChatStore: cursor-paginated history, SignalR MessageReceived/UserTyping, JoinRoom/LeaveRoom
  • Dual-pane layout: room sidebar, @mention highlighting, reply-to preview, auto-scroll, typing indicator

Phase 9 — Ollama & Learnings

  • LearningsController: pgvector semantic search via Ollama embeddings + keyword fallback, CRUD
  • Ollama dashboard: health status, local model list, pull-model, semantic search, learnings CRUD

Test plan

  • docker compose up → all 8 services healthy, Caddy responds at manager.amtocbot.com
  • Login via Keycloak → shell renders, SignalR connects (WS inspector)
  • Create instance → Docker container visible in docker ps, live CPU% updates every 5s
  • Configure Telegram channel → config volume updated, OpenClaw picks it up on restart
  • Create Kanban card from UI, drag between columns → second browser tab sees move in real-time
  • Send chat message → appears instantly in all connected tabs
  • Add a switch rule (threshold 80%) → ModelSwitchScheduler evaluates it on next tick
  • GET /api/ollama/models returns local model list; semantic search returns relevant learnings

🤖 Generated with Claude Code

Toc Am and others added 14 commits March 15, 2026 20:06
Complete ASP.NET Core 10 Web API for AmtocBots Manager:

Entities & Data:
- AppDbContext with pgvector extension, AES-256 value converter for
  channel token encryption at rest, all EF Core entity configurations
- Entities: BotInstance, ChannelConfig, ModelSwitchRule, TokenUsageRecord,
  KanbanBoard/Column/Card, ChatRoom/Member/Message, BotLearning, AppUser

Services:
- DockerService (Docker.DotNet): create/start/stop/remove OpenClaw
  containers, stats polling, config volume writing via busybox helper
- OpenClawClient: typed HTTP client for /hooks/wake and /hooks/agent
- OpenClawConfigBuilder: generates JSON5 config from DB state
- TokenTracker: upserts daily token usage records per instance+model
- ModelSwitchingService: evaluates threshold rules, switches model,
  rewrites config volume, restarts container
- RedisMessageQueueService: LPUSH/BRPOP main queue + sorted-set delay queue
- OllamaService: list models, pull, health, generate embeddings

Controllers & Endpoints:
- InstancesController: full CRUD + start/stop/restart/logs/config
- ModelsController: available models, token usage, switch rules CRUD
- KanbanController: boards/columns/cards + bot-webhook endpoint (API key auth)
- ChatController: rooms, paginated messages, bot-message endpoint
- WebhookController: receives token-usage and event callbacks from OpenClaw
- ChannelEndpoints: GET/PUT channel configs, WhatsApp QR proxy
- OllamaEndpoints: list/pull/status
- HealthEndpoints: /api/health

SignalR Hubs: InstanceHub, KanbanHub, ChatHub

Background Services:
- MetricsPollingService: Docker stats → InstanceHub every 5s
- ModelSwitchScheduler: NCrontab + threshold evaluation every 1m
- QueueRetryWorker: Redis queue retry with exponential backoff

Program.cs: full DI registration, Keycloak JWT auth (role claims from
realm_access.roles), CORS, SignalR, auto-migration on startup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Angular 21 standalone PWA foundation:

Config & Bootstrap:
- app.config.ts: provideRouter (input binding + view transitions),
  provideHttpClient (authTokenInterceptor), provideAuth (OIDC → Keycloak
  with PKCE + silent renew), provideServiceWorker
- app.routes.ts: lazy-loaded feature routes, authGuard on shell

Core:
- AuthService: signals-based isAuthenticated, userData, roles,
  isAdmin/isOperator computed signals; wraps angular-auth-oidc-client
- authGuard / roleGuard: functional guards (adminGuard, operatorGuard)
- authTokenInterceptor: attaches JWT Bearer to /api and /hubs requests
- AuthCallbackComponent: handles OIDC redirect
- SignalrService: hub connection factory with withAutomaticReconnect,
  typed on<T>() Observable, reconnecting signal for UI banner
- ShellComponent: mat-sidenav shell + reconnect banner
- SidebarComponent: nav items with routerLinkActive
- TopbarComponent: menu toggle + user dropdown + logout

Shared:
- StatusBadgeComponent: color-coded status badge (running/stopped/starting/error)
- ConfirmDialogComponent: reusable confirm dialog

Features:
- DashboardComponent: live instance list with stats via toSignal + HttpClient
- Route stubs for instances, models, kanban, chat (implementation in phases 4-8)

Infrastructure:
- multi-stage Dockerfile: node:22 build → nginx:1.27 serve
- nginx.conf: SPA routing, asset caching, gzip
- ngsw-config.json: app-shell prefetch, API freshness strategy
- proxy.conf.json: dev proxy /api and /hubs to :8080
- environments: dev (localhost) + production (amtocbot.com)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 4 - Instance Management UI:
- InstanceStore: signal-based state, SignalR StatusUpdate wired to live
  CPU/mem stats, start/stop/restart/delete actions with optimistic updates
- InstanceListComponent: card grid with live metric bars (5s refresh via
  SignalR), start/stop/restart buttons, delete confirm dialog, role guards
- InstanceDetailComponent: tabs for Config/Channels/Logs, live stats bar,
  computed liveStatus/liveStats from store signal, input() id binding
- InstanceFormComponent: reactive form with model selector (loads from
  /api/models/available including Ollama), resource limits, one-time token
  display after creation
- InstanceConfigEditorComponent: Monaco editor (vs-dark, JSON5) with
  dirty-state banner, reload and save actions
- InstanceLogViewerComponent: 10s auto-refresh while running, tail selector
  (50/200/500), scroll-to-bottom, effect() on isRunning input signal

Phase 5 - Channel Configuration UI:
- ChannelBase directive: shared saveConfig(), parsedConfig(), saving signal
- ChannelConfigComponent: tab container, loads channel configs on init
- TelegramConfigComponent: bot token (masked) + allowFrom chip list
- DiscordConfigComponent: bot token, guild ID, allowed channel IDs chip list
- SlackConfigComponent: app token (xapp-) + bot token (xoxb-) + channel IDs
- WhatsappConfigComponent: QR polling loop (3s interval), blob URL display,
  allowFrom phone numbers, paired/error/idle state machine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Token usage dashboard with 7/30-day range, model breakdown bars (blue→amber→red), per-instance table
- SwitchRulesComponent: threshold + cron auto-switch rules per instance, enable/disable toggle, priority
- models.routes.ts: add :instanceId/rules sub-route
- ModelsController: persist IsActive on rule update
- ModelDtos: add IsActive to CreateSwitchRuleRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- KanbanStore: signal-based board state, SignalR CardCreated/CardMoved/CardUpdated/CardDeleted listeners, JoinBoard/LeaveBoard hub calls, optimistic moveCard
- BoardListComponent: board grid with create dialog
- KanbanBoardComponent: CDK drag-drop across columns, priority color dots, due-date overdue highlight, bot-created badge, WIP limit warnings, add-card dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ChatStore: signal-based state, SignalR MessageReceived/UserTyping listeners, cursor-paginated message history, JoinRoom/LeaveRoom hub calls, sendTyping with debounce
- ChatShellComponent: dual-pane layout (room sidebar + message area), @mention highlight, reply-to preview, auto-scroll with load-older-messages on scroll-to-top, typing indicator, create-room dialog
- AuthService: add userId() and username() computed signals from OIDC userData

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LearningsController: GET /learnings (list with instanceId/tag filters), GET /learnings/search (vector similarity via Ollama embeddings, keyword fallback), POST /learnings (embed + store), DELETE /learnings/{id}
- OllamaDashboardComponent: health status dot, model list, pull-model form, semantic search, learnings CRUD with source/tag filters
- app.routes.ts: add /ollama lazy route
- sidebar: add Ollama nav item

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…etail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…JAR from Maven Central via alpine stage

- quay.io/keycloak/keycloak:26 tag doesn't exist; use 26.2
- p2-inc/keycloak-magic-link moved to Maven Central (io.phasetwo.keycloak), version 0.57
- KC image has no curl/wget; use alpine:3.20 as downloader stage
- Remove non-existent 'magic-link' feature flag (extension registers itself)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend (Angular):
- styles.scss: migrate to Angular Material M3 theme API (mat.define-theme)
- tsconfig.json: add baseUrl for path alias resolution
- package.json: bump TypeScript ~5.6→~5.9 (Angular 21 requirement); add package-lock.json
- Dockerfile: add --legacy-peer-deps to npm ci
- auth.service.ts: fix toSignal generic, remove invalid getAccessToken$(); use signal for getToken()
- signalr.service.ts: inject AuthService instead of OidcSecurityService for token factory
- instance.store.ts: replace catch(e:any) and bare catch{} with valid TS5.9 syntax
- channel configs: fix inject import from @angular/core (not @angular/forms)
- components: add missing pipe imports (DecimalPipe, CurrencyPipe, DatePipe, SlicePipe)
- CpuClampPipe: move class definition before @component so it can be listed in imports[]
- channel-config: replace Array.with() with slice-based immutable update

Backend (.NET):
- DockerService: cast decimal to double before * 1e9; remove ?? on non-nullable ulong
- LearningsController: use named ct: param for GenerateEmbeddingAsync
- RedisMessageQueueService: explicit (string) cast to resolve Deserialize overload ambiguity
- AmtocBots.Api.csproj: Hjson 3.1.* → 3.0.* (3.1 doesn't exist on NuGet)
- docker-compose.yml: include password in Redis connection string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch cloudflared from cert.pem/config-file approach to token-based
  auth (tunnel run --token ${CLOUDFLARE_TUNNEL_TOKEN}) — no volumes needed
- Fix webAuthnPolicySignatureAlgorithms and
  webAuthnPolicyPasswordlessSignatureAlgorithms from Array to String in
  realm-export.json — KC 26 deserializes these as java.lang.String

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passing --token ${VAR} on the command line breaks when VAR is empty —
cloudflared sees '-token' with no value and exits with a parse error.
Using the TUNNEL_TOKEN environment variable avoids this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Prefixing hostnames with http:// prevents Caddy from attempting ACME
certificate issuance, which was failing because ACME challenges go
through the Cloudflare proxy and can't reach Caddy directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant