Initial implementation of vault-os

Complete implementation across all 13 phases:

- vault-core: types, YAML frontmatter parsing, entity classification,
  filesystem ops, config, prompt composition, validation, search
- vault-watch: filesystem watcher with daemon write filtering, event
  classification
- vault-scheduler: cron engine, process executor, task runner with
  retry logic and concurrency limiting
- vault-api: Axum REST API (15 route modules), WebSocket with broadcast,
  AI assistant proxy, validation, templates
- Dashboard: React + TypeScript + Tailwind v4 with kanban, CodeMirror
  editor, dynamic view system, AI chat sidebar
- Nix flake with dev shell and NixOS module
- Graceful shutdown, inotify overflow recovery, tracing instrumentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Harald Hoyer 2026-03-03 01:21:17 +01:00
commit f820a72b04
123 changed files with 18288 additions and 0 deletions

View file

@ -0,0 +1,49 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { listNotifications, dismissNotification } from '../api/client';
const LEVEL_STYLES: Record<string, string> = {
info: 'bg-accent/10 border-accent/30 text-accent',
warning: 'bg-warning/10 border-warning/30 text-warning',
error: 'bg-danger/10 border-danger/30 text-danger',
success: 'bg-success/10 border-success/30 text-success',
};
export function NotificationBanner() {
const queryClient = useQueryClient();
const { data: notifications } = useQuery({
queryKey: ['notifications'],
queryFn: listNotifications,
refetchInterval: 30000,
});
const dismiss = useMutation({
mutationFn: dismissNotification,
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['notifications'] }),
});
if (!notifications?.length) return null;
return (
<div className="space-y-2 px-6 pt-4">
{notifications.map((n) => (
<div
key={n.id}
className={`flex items-start gap-3 rounded-md border px-3 py-2 text-sm ${
LEVEL_STYLES[n.level || 'info'] || LEVEL_STYLES.info
}`}
>
<div className="flex-1">
<div className="font-medium">{n.title}</div>
{n.message && <div className="mt-0.5 text-xs opacity-80">{n.message}</div>}
</div>
<button
onClick={() => dismiss.mutate(n.id)}
className="shrink-0 text-xs opacity-60 hover:opacity-100"
>
dismiss
</button>
</div>
))}
</div>
);
}