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.
- package/package.json +3 -4
- package/src/cli/config.ts +96 -0
- package/src/index.ts +201 -3
- package/src/integrations/elasticsearch/client.ts +210 -0
- package/src/integrations/grafana/client.ts +186 -0
- package/src/integrations/kubernetes/multi-cluster.ts +199 -0
- package/src/integrations/kubernetes/types.ts +24 -0
- package/src/integrations/loki/client.ts +219 -0
- package/src/integrations/prometheus/client.ts +163 -0
- package/src/integrations/slack/client.ts +265 -0
- package/src/integrations/teams/client.ts +199 -0
- package/src/mastra/agents/debugger.ts +152 -108
- package/src/mastra/tools/approval-store.ts +180 -0
- package/src/mastra/tools/cli.ts +94 -2
- package/src/mastra/tools/cost.ts +389 -0
- package/src/mastra/tools/logs.ts +210 -0
- package/src/mastra/tools/network.ts +253 -0
- package/src/mastra/tools/prometheus.ts +221 -0
- package/src/mastra/tools/remediation.ts +365 -0
- package/src/mastra/tools/runbook.ts +186 -0
- package/src/server/routes/history.ts +207 -0
- package/src/server/routes/notifications.ts +236 -0
- package/src/server/webhook.ts +36 -2
- package/src/storage/index.ts +3 -0
- package/src/storage/investigation-history.ts +277 -0
- package/src/storage/runbook-index.ts +330 -0
- package/src/storage/types.ts +72 -0
- package/src/tui/app.tsx +492 -76
- package/src/tui/components/approval-dialog.tsx +156 -0
- package/src/tui/components/approval-modal.tsx +278 -0
- package/src/tui/components/index.ts +38 -0
- package/src/tui/components/styled-span.tsx +24 -0
- package/src/tui/components/timeline.tsx +223 -0
- 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
|
+
}
|