triagent 0.1.0-alpha13 → 0.1.0-alpha18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +3 -4
  2. package/src/cli/config.ts +96 -0
  3. package/src/index.ts +201 -3
  4. package/src/integrations/elasticsearch/client.ts +210 -0
  5. package/src/integrations/grafana/client.ts +186 -0
  6. package/src/integrations/kubernetes/multi-cluster.ts +199 -0
  7. package/src/integrations/kubernetes/types.ts +24 -0
  8. package/src/integrations/loki/client.ts +219 -0
  9. package/src/integrations/prometheus/client.ts +163 -0
  10. package/src/integrations/slack/client.ts +265 -0
  11. package/src/integrations/teams/client.ts +199 -0
  12. package/src/mastra/agents/debugger.ts +152 -108
  13. package/src/mastra/tools/approval-store.ts +180 -0
  14. package/src/mastra/tools/cli.ts +94 -2
  15. package/src/mastra/tools/cost.ts +389 -0
  16. package/src/mastra/tools/logs.ts +210 -0
  17. package/src/mastra/tools/network.ts +253 -0
  18. package/src/mastra/tools/prometheus.ts +221 -0
  19. package/src/mastra/tools/remediation.ts +365 -0
  20. package/src/mastra/tools/runbook.ts +186 -0
  21. package/src/server/routes/history.ts +207 -0
  22. package/src/server/routes/notifications.ts +236 -0
  23. package/src/server/webhook.ts +36 -2
  24. package/src/storage/index.ts +3 -0
  25. package/src/storage/investigation-history.ts +277 -0
  26. package/src/storage/runbook-index.ts +330 -0
  27. package/src/storage/types.ts +72 -0
  28. package/src/tui/app.tsx +492 -76
  29. package/src/tui/components/approval-dialog.tsx +156 -0
  30. package/src/tui/components/approval-modal.tsx +278 -0
  31. package/src/tui/components/index.ts +38 -0
  32. package/src/tui/components/styled-span.tsx +24 -0
  33. package/src/tui/components/timeline.tsx +223 -0
  34. package/src/tui/components/toast.tsx +101 -0
@@ -0,0 +1,223 @@
1
+ /* @jsxImportSource @opentui/solid */
2
+ import { For, Show, type JSX } from "solid-js";
3
+ import { createTextAttributes } from "@opentui/core";
4
+ import type { InvestigationEvent, ToolCallRecord } from "../../storage/types.js";
5
+
6
+ const ATTR_DIM = createTextAttributes({ dim: true });
7
+ const ATTR_BOLD = createTextAttributes({ bold: true });
8
+
9
+ export interface TimelineEvent {
10
+ id: string;
11
+ timestamp: Date;
12
+ type: "tool_call" | "alert" | "k8s_event" | "log_entry" | "user_action";
13
+ title: string;
14
+ details?: string;
15
+ severity?: "critical" | "warning" | "info" | "success";
16
+ }
17
+
18
+ interface TimelineProps {
19
+ events: TimelineEvent[];
20
+ maxHeight?: number;
21
+ showTimestamps?: boolean;
22
+ }
23
+
24
+ function getSeverityColor(severity?: TimelineEvent["severity"]): string {
25
+ switch (severity) {
26
+ case "critical":
27
+ return "red";
28
+ case "warning":
29
+ return "yellow";
30
+ case "success":
31
+ return "green";
32
+ case "info":
33
+ default:
34
+ return "blue";
35
+ }
36
+ }
37
+
38
+ function getEventIcon(type: TimelineEvent["type"]): string {
39
+ switch (type) {
40
+ case "tool_call":
41
+ return "⚙";
42
+ case "alert":
43
+ return "🔔";
44
+ case "k8s_event":
45
+ return "☸";
46
+ case "log_entry":
47
+ return "📝";
48
+ case "user_action":
49
+ return "👤";
50
+ default:
51
+ return "•";
52
+ }
53
+ }
54
+
55
+ function formatTime(date: Date): string {
56
+ return date.toLocaleTimeString("en-US", {
57
+ hour: "2-digit",
58
+ minute: "2-digit",
59
+ second: "2-digit",
60
+ hour12: false,
61
+ });
62
+ }
63
+
64
+ function formatRelativeTime(date: Date): string {
65
+ const now = new Date();
66
+ const diffMs = now.getTime() - date.getTime();
67
+ const diffSec = Math.floor(diffMs / 1000);
68
+ const diffMin = Math.floor(diffSec / 60);
69
+ const diffHour = Math.floor(diffMin / 60);
70
+
71
+ if (diffSec < 60) {
72
+ return `${diffSec}s ago`;
73
+ } else if (diffMin < 60) {
74
+ return `${diffMin}m ago`;
75
+ } else if (diffHour < 24) {
76
+ return `${diffHour}h ago`;
77
+ } else {
78
+ return date.toLocaleDateString();
79
+ }
80
+ }
81
+
82
+ export function Timeline(props: TimelineProps): JSX.Element {
83
+ const showTimestamps = props.showTimestamps ?? true;
84
+
85
+ return (
86
+ <box flexDirection="column" gap={0}>
87
+ <Show
88
+ when={props.events.length > 0}
89
+ fallback={
90
+ <text fg="gray" attributes={ATTR_DIM}>
91
+ No events to display
92
+ </text>
93
+ }
94
+ >
95
+ <For each={props.events}>
96
+ {(event, index) => {
97
+ const color = getSeverityColor(event.severity);
98
+ const icon = getEventIcon(event.type);
99
+ const isLast = index() === props.events.length - 1;
100
+
101
+ return (
102
+ <box flexDirection="row" gap={1}>
103
+ {/* Timeline line */}
104
+ <box flexDirection="column" width={3} alignItems="center">
105
+ <text fg={color}>{icon}</text>
106
+ <Show when={!isLast}>
107
+ <text fg="gray" attributes={ATTR_DIM}>│</text>
108
+ </Show>
109
+ </box>
110
+
111
+ {/* Event content */}
112
+ <box flexDirection="column" flexGrow={1}>
113
+ <box flexDirection="row" gap={1}>
114
+ <text fg={color} attributes={ATTR_BOLD}>
115
+ {event.title}
116
+ </text>
117
+ <Show when={showTimestamps}>
118
+ <text fg="gray" attributes={ATTR_DIM}>
119
+ ({formatRelativeTime(event.timestamp)})
120
+ </text>
121
+ </Show>
122
+ </box>
123
+ <Show when={event.details}>
124
+ <text fg="gray" attributes={ATTR_DIM}>
125
+ {event.details}
126
+ </text>
127
+ </Show>
128
+ </box>
129
+ </box>
130
+ );
131
+ }}
132
+ </For>
133
+ </Show>
134
+ </box>
135
+ );
136
+ }
137
+
138
+ // Helper to convert investigation events to timeline events
139
+ export function investigationEventsToTimeline(
140
+ events: InvestigationEvent[],
141
+ toolCalls: ToolCallRecord[]
142
+ ): TimelineEvent[] {
143
+ const timelineEvents: TimelineEvent[] = [];
144
+
145
+ // Add tool calls
146
+ for (const tc of toolCalls) {
147
+ timelineEvents.push({
148
+ id: tc.id,
149
+ timestamp: tc.timestamp,
150
+ type: "tool_call",
151
+ title: `Tool: ${tc.toolName}`,
152
+ details: tc.args?.command ? `$ ${tc.args.command}` : undefined,
153
+ severity: tc.error ? "warning" : "success",
154
+ });
155
+ }
156
+
157
+ // Add investigation events
158
+ for (const event of events) {
159
+ let severity: TimelineEvent["severity"] = "info";
160
+ if (event.type === "alert") {
161
+ severity = "warning";
162
+ } else if (event.type === "k8s_event") {
163
+ const data = event.data as { type?: string };
164
+ severity = data.type === "Warning" ? "warning" : "info";
165
+ }
166
+
167
+ timelineEvents.push({
168
+ id: event.id,
169
+ timestamp: event.timestamp,
170
+ type: event.type,
171
+ title: event.source,
172
+ details: JSON.stringify(event.data).slice(0, 100),
173
+ severity,
174
+ });
175
+ }
176
+
177
+ // Sort by timestamp descending (most recent first)
178
+ timelineEvents.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
179
+
180
+ return timelineEvents;
181
+ }
182
+
183
+ interface CompactTimelineProps {
184
+ events: TimelineEvent[];
185
+ maxEvents?: number;
186
+ }
187
+
188
+ export function CompactTimeline(props: CompactTimelineProps): JSX.Element {
189
+ const maxEvents = props.maxEvents ?? 5;
190
+ const displayEvents = () => props.events.slice(0, maxEvents);
191
+ const hasMore = () => props.events.length > maxEvents;
192
+
193
+ return (
194
+ <box flexDirection="column">
195
+ <text fg="cyan" attributes={ATTR_BOLD}>
196
+ Recent Events
197
+ </text>
198
+ <box flexDirection="column" marginTop={1}>
199
+ <For each={displayEvents()}>
200
+ {(event) => {
201
+ const color = getSeverityColor(event.severity);
202
+ const icon = getEventIcon(event.type);
203
+
204
+ return (
205
+ <box flexDirection="row" gap={1}>
206
+ <text fg={color}>{icon}</text>
207
+ <text fg="white">{event.title}</text>
208
+ <text fg="gray" attributes={ATTR_DIM}>
209
+ {formatRelativeTime(event.timestamp)}
210
+ </text>
211
+ </box>
212
+ );
213
+ }}
214
+ </For>
215
+ <Show when={hasMore()}>
216
+ <text fg="gray" attributes={ATTR_DIM}>
217
+ ... and {props.events.length - maxEvents} more events
218
+ </text>
219
+ </Show>
220
+ </box>
221
+ </box>
222
+ );
223
+ }
@@ -0,0 +1,101 @@
1
+ /* @jsxImportSource @opentui/solid */
2
+ import { Toaster, toast, useToasts } from "@opentui-ui/toast/solid";
3
+ import { TOAST_DURATION } from "@opentui-ui/toast";
4
+ import type { JSX } from "solid-js";
5
+
6
+ // Re-export toast function and duration constants for app-wide use
7
+ export { toast, TOAST_DURATION, useToasts };
8
+
9
+ export interface ToastProviderProps {
10
+ children: JSX.Element;
11
+ }
12
+
13
+ /**
14
+ * Toast provider component that wraps the application
15
+ * Provides toast notifications positioned at the bottom-right
16
+ */
17
+ export function ToastProvider(props: ToastProviderProps): JSX.Element {
18
+ return (
19
+ <>
20
+ {props.children}
21
+ <Toaster
22
+ position="bottom-right"
23
+ gap={1}
24
+ maxWidth={50}
25
+ closeButton={true}
26
+ toastOptions={{
27
+ duration: TOAST_DURATION.DEFAULT,
28
+ style: {
29
+ borderStyle: "single",
30
+ paddingLeft: 1,
31
+ paddingRight: 1,
32
+ paddingTop: 0,
33
+ paddingBottom: 0,
34
+ },
35
+ }}
36
+ />
37
+ </>
38
+ );
39
+ }
40
+
41
+ // Helper functions for common toast patterns
42
+
43
+ /**
44
+ * Show a success toast notification
45
+ */
46
+ export function toastSuccess(message: string, description?: string) {
47
+ return toast.success(message, { description, duration: TOAST_DURATION.SHORT });
48
+ }
49
+
50
+ /**
51
+ * Show an error toast notification
52
+ */
53
+ export function toastError(message: string, description?: string) {
54
+ return toast.error(message, { description, duration: TOAST_DURATION.LONG });
55
+ }
56
+
57
+ /**
58
+ * Show a warning toast notification
59
+ */
60
+ export function toastWarning(message: string, description?: string) {
61
+ return toast.warning(message, { description, duration: TOAST_DURATION.LONG });
62
+ }
63
+
64
+ /**
65
+ * Show an info toast notification
66
+ */
67
+ export function toastInfo(message: string, description?: string) {
68
+ return toast.info(message, { description, duration: TOAST_DURATION.DEFAULT });
69
+ }
70
+
71
+ /**
72
+ * Show a loading toast that can be updated when operation completes
73
+ */
74
+ export function toastLoading(message: string) {
75
+ return toast.loading(message, { duration: TOAST_DURATION.PERSISTENT });
76
+ }
77
+
78
+ /**
79
+ * Dismiss a specific toast or all toasts
80
+ */
81
+ export function toastDismiss(id?: string | number) {
82
+ if (id !== undefined) {
83
+ toast.dismiss(id);
84
+ } else {
85
+ toast.dismiss();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Show toast with promise handling - loading, success, error states
91
+ */
92
+ export function toastPromise<T>(
93
+ promise: Promise<T>,
94
+ messages: {
95
+ loading?: string;
96
+ success?: string | ((data: T) => string);
97
+ error?: string | ((err: unknown) => string);
98
+ }
99
+ ) {
100
+ return toast.promise(promise, messages);
101
+ }