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:
commit
f820a72b04
123 changed files with 18288 additions and 0 deletions
80
dashboard/src/api/ws.ts
Normal file
80
dashboard/src/api/ws.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import type { WsEvent } from './types';
|
||||
|
||||
type Listener = (event: WsEvent) => void;
|
||||
|
||||
export class VaultWebSocket {
|
||||
private ws: WebSocket | null = null;
|
||||
private listeners: Map<string, Set<Listener>> = new Map();
|
||||
private globalListeners: Set<Listener> = new Set();
|
||||
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private url: string;
|
||||
|
||||
constructor(url?: string) {
|
||||
const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
this.url = url || `${proto}//${window.location.host}/ws`;
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (this.ws?.readyState === WebSocket.OPEN) return;
|
||||
|
||||
this.ws = new WebSocket(this.url);
|
||||
|
||||
this.ws.onmessage = (msg) => {
|
||||
try {
|
||||
const event: WsEvent = JSON.parse(msg.data);
|
||||
this.globalListeners.forEach((fn) => fn(event));
|
||||
this.listeners.get(event.type)?.forEach((fn) => fn(event));
|
||||
} catch {
|
||||
// ignore malformed messages
|
||||
}
|
||||
};
|
||||
|
||||
this.ws.onclose = () => {
|
||||
this.scheduleReconnect();
|
||||
};
|
||||
|
||||
this.ws.onerror = () => {
|
||||
this.ws?.close();
|
||||
};
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (this.reconnectTimer) {
|
||||
clearTimeout(this.reconnectTimer);
|
||||
this.reconnectTimer = null;
|
||||
}
|
||||
this.ws?.close();
|
||||
this.ws = null;
|
||||
}
|
||||
|
||||
/** Listen to a specific event type */
|
||||
on(type: string, fn: Listener): () => void {
|
||||
if (!this.listeners.has(type)) {
|
||||
this.listeners.set(type, new Set());
|
||||
}
|
||||
this.listeners.get(type)!.add(fn);
|
||||
return () => this.listeners.get(type)?.delete(fn);
|
||||
}
|
||||
|
||||
/** Listen to all events */
|
||||
onAny(fn: Listener): () => void {
|
||||
this.globalListeners.add(fn);
|
||||
return () => this.globalListeners.delete(fn);
|
||||
}
|
||||
|
||||
send(action: Record<string, unknown>) {
|
||||
if (this.ws?.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(JSON.stringify(action));
|
||||
}
|
||||
}
|
||||
|
||||
private scheduleReconnect() {
|
||||
if (this.reconnectTimer) return;
|
||||
this.reconnectTimer = setTimeout(() => {
|
||||
this.reconnectTimer = null;
|
||||
this.connect();
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
export const vaultWs = new VaultWebSocket();
|
||||
Loading…
Add table
Add a link
Reference in a new issue