triagent 0.1.0-alpha13 → 0.1.0-alpha17
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 -3
- 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,156 @@
|
|
|
1
|
+
/* @jsxImportSource @opentui/solid */
|
|
2
|
+
import { DialogProvider, useDialog, useDialogKeyboard, type ConfirmContext } from "@opentui-ui/dialog/solid";
|
|
3
|
+
import { createTextAttributes } from "@opentui/core";
|
|
4
|
+
import type { JSX, ParentProps } from "solid-js";
|
|
5
|
+
|
|
6
|
+
const ATTR_BOLD = createTextAttributes({ bold: true });
|
|
7
|
+
const ATTR_DIM = createTextAttributes({ dim: true });
|
|
8
|
+
|
|
9
|
+
export type RiskLevel = "low" | "medium" | "high" | "critical";
|
|
10
|
+
|
|
11
|
+
export interface ApprovalDialogOptions {
|
|
12
|
+
command: string;
|
|
13
|
+
riskLevel: RiskLevel;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getRiskColor(risk: RiskLevel): string {
|
|
18
|
+
switch (risk) {
|
|
19
|
+
case "low": return "green";
|
|
20
|
+
case "medium": return "yellow";
|
|
21
|
+
case "high": return "red";
|
|
22
|
+
case "critical": return "magenta";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getRiskEmoji(risk: RiskLevel): string {
|
|
27
|
+
switch (risk) {
|
|
28
|
+
case "low": return "🟢";
|
|
29
|
+
case "medium": return "🟡";
|
|
30
|
+
case "high": return "🟠";
|
|
31
|
+
case "critical": return "🔴";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getRiskBorderColor(risk: RiskLevel): string {
|
|
36
|
+
switch (risk) {
|
|
37
|
+
case "low": return "#22c55e";
|
|
38
|
+
case "medium": return "#eab308";
|
|
39
|
+
case "high": return "#ef4444";
|
|
40
|
+
case "critical": return "#ec4899";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Content component for the approval confirmation dialog
|
|
46
|
+
*/
|
|
47
|
+
function ApprovalDialogContent(ctx: ConfirmContext & { options: ApprovalDialogOptions }) {
|
|
48
|
+
const { command, riskLevel, description } = ctx.options;
|
|
49
|
+
const riskColor = getRiskColor(riskLevel);
|
|
50
|
+
|
|
51
|
+
// Handle keyboard input
|
|
52
|
+
useDialogKeyboard((key) => {
|
|
53
|
+
if (key.name === "return" || key.name === "y") {
|
|
54
|
+
ctx.resolve(true);
|
|
55
|
+
} else if (key.name === "escape" || key.name === "n") {
|
|
56
|
+
ctx.resolve(false);
|
|
57
|
+
}
|
|
58
|
+
}, ctx.dialogId);
|
|
59
|
+
|
|
60
|
+
return () => (
|
|
61
|
+
<box flexDirection="column" gap={1}>
|
|
62
|
+
{/* Header */}
|
|
63
|
+
<box flexDirection="row" gap={1}>
|
|
64
|
+
<text fg={riskColor}>{getRiskEmoji(riskLevel)}</text>
|
|
65
|
+
<text fg="white" attributes={ATTR_BOLD}>
|
|
66
|
+
Write Operation Requires Approval
|
|
67
|
+
</text>
|
|
68
|
+
<text fg="gray" attributes={ATTR_DIM}>
|
|
69
|
+
({riskLevel} risk)
|
|
70
|
+
</text>
|
|
71
|
+
</box>
|
|
72
|
+
|
|
73
|
+
{/* Command */}
|
|
74
|
+
<box flexDirection="column">
|
|
75
|
+
<text fg="cyan" attributes={ATTR_BOLD}>Command:</text>
|
|
76
|
+
<text fg="yellow">{command}</text>
|
|
77
|
+
</box>
|
|
78
|
+
|
|
79
|
+
{/* Description if provided */}
|
|
80
|
+
{description && (
|
|
81
|
+
<box flexDirection="column">
|
|
82
|
+
<text fg="gray" attributes={ATTR_DIM}>{description}</text>
|
|
83
|
+
</box>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{/* Warning for high risk */}
|
|
87
|
+
{(riskLevel === "high" || riskLevel === "critical") && (
|
|
88
|
+
<box>
|
|
89
|
+
<text fg="red" attributes={ATTR_BOLD}>
|
|
90
|
+
⚠️ This is a {riskLevel}-risk operation. Review carefully.
|
|
91
|
+
</text>
|
|
92
|
+
</box>
|
|
93
|
+
)}
|
|
94
|
+
|
|
95
|
+
{/* Actions */}
|
|
96
|
+
<box flexDirection="row" gap={4} marginTop={1}>
|
|
97
|
+
<text fg="green" attributes={ATTR_BOLD}>[Y] Approve</text>
|
|
98
|
+
<text fg="red" attributes={ATTR_BOLD}>[N] Reject</text>
|
|
99
|
+
</box>
|
|
100
|
+
|
|
101
|
+
{/* Instructions */}
|
|
102
|
+
<text fg="gray" attributes={ATTR_DIM}>
|
|
103
|
+
Press Y to approve, N to reject, or Esc to cancel
|
|
104
|
+
</text>
|
|
105
|
+
</box>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Provider component that enables dialog functionality
|
|
111
|
+
*/
|
|
112
|
+
export function ApprovalDialogProvider(props: ParentProps): JSX.Element {
|
|
113
|
+
return (
|
|
114
|
+
<DialogProvider
|
|
115
|
+
size="medium"
|
|
116
|
+
dialogOptions={{
|
|
117
|
+
style: {
|
|
118
|
+
borderStyle: "single",
|
|
119
|
+
borderColor: "#ffffff",
|
|
120
|
+
backgroundColor: "#1a1a1a",
|
|
121
|
+
paddingLeft: 2,
|
|
122
|
+
paddingRight: 2,
|
|
123
|
+
paddingTop: 1,
|
|
124
|
+
paddingBottom: 1,
|
|
125
|
+
},
|
|
126
|
+
}}
|
|
127
|
+
backdropColor="#000000"
|
|
128
|
+
backdropOpacity={0.5}
|
|
129
|
+
>
|
|
130
|
+
{props.children}
|
|
131
|
+
</DialogProvider>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Hook to show approval confirmation dialogs
|
|
137
|
+
*/
|
|
138
|
+
export function useApprovalDialog() {
|
|
139
|
+
const dialog = useDialog();
|
|
140
|
+
|
|
141
|
+
const showApproval = async (options: ApprovalDialogOptions): Promise<boolean> => {
|
|
142
|
+
const result = await dialog.confirm({
|
|
143
|
+
content: (ctx) => ApprovalDialogContent({ ...ctx, options }),
|
|
144
|
+
style: {
|
|
145
|
+
borderColor: getRiskBorderColor(options.riskLevel),
|
|
146
|
+
borderStyle: "double",
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return { showApproval, ...dialog };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Re-export dialog hooks for other uses
|
|
156
|
+
export { useDialog, useDialogKeyboard };
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/* @jsxImportSource @opentui/solid */
|
|
2
|
+
import { Show, type JSX } from "solid-js";
|
|
3
|
+
import { createTextAttributes } from "@opentui/core";
|
|
4
|
+
|
|
5
|
+
const ATTR_DIM = createTextAttributes({ dim: true });
|
|
6
|
+
const ATTR_BOLD = createTextAttributes({ bold: true });
|
|
7
|
+
|
|
8
|
+
export interface ApprovalRequest {
|
|
9
|
+
id: string;
|
|
10
|
+
action: string;
|
|
11
|
+
target: string;
|
|
12
|
+
riskLevel: "low" | "medium" | "high" | "critical";
|
|
13
|
+
description: string;
|
|
14
|
+
approvalToken: string;
|
|
15
|
+
expiresAt: Date;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ApprovalModalProps {
|
|
19
|
+
request: ApprovalRequest | null;
|
|
20
|
+
onApprove: (token: string) => void;
|
|
21
|
+
onReject: () => void;
|
|
22
|
+
visible: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getRiskColor(risk: ApprovalRequest["riskLevel"]): string {
|
|
26
|
+
switch (risk) {
|
|
27
|
+
case "low":
|
|
28
|
+
return "green";
|
|
29
|
+
case "medium":
|
|
30
|
+
return "yellow";
|
|
31
|
+
case "high":
|
|
32
|
+
return "red";
|
|
33
|
+
case "critical":
|
|
34
|
+
return "magenta";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getRiskEmoji(risk: ApprovalRequest["riskLevel"]): string {
|
|
39
|
+
switch (risk) {
|
|
40
|
+
case "low":
|
|
41
|
+
return "🟢";
|
|
42
|
+
case "medium":
|
|
43
|
+
return "🟡";
|
|
44
|
+
case "high":
|
|
45
|
+
return "🟠";
|
|
46
|
+
case "critical":
|
|
47
|
+
return "🔴";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function ApprovalModal(props: ApprovalModalProps): JSX.Element {
|
|
52
|
+
const request = () => props.request;
|
|
53
|
+
const visible = () => props.visible && request() !== null;
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Show when={visible()}>
|
|
57
|
+
<box
|
|
58
|
+
position="absolute"
|
|
59
|
+
top={5}
|
|
60
|
+
left={10}
|
|
61
|
+
width={60}
|
|
62
|
+
borderStyle="double"
|
|
63
|
+
borderColor={getRiskColor(request()!.riskLevel)}
|
|
64
|
+
paddingLeft={2}
|
|
65
|
+
paddingRight={2}
|
|
66
|
+
paddingTop={1}
|
|
67
|
+
paddingBottom={1}
|
|
68
|
+
flexDirection="column"
|
|
69
|
+
>
|
|
70
|
+
{/* Header */}
|
|
71
|
+
<box flexDirection="row" justifyContent="center" marginBottom={1}>
|
|
72
|
+
<text fg={getRiskColor(request()!.riskLevel)} attributes={ATTR_BOLD}>
|
|
73
|
+
⚠️ APPROVAL REQUIRED ⚠️
|
|
74
|
+
</text>
|
|
75
|
+
</box>
|
|
76
|
+
|
|
77
|
+
{/* Risk Level */}
|
|
78
|
+
<box flexDirection="row" gap={1} marginBottom={1}>
|
|
79
|
+
<text fg="white">Risk Level:</text>
|
|
80
|
+
<text fg={getRiskColor(request()!.riskLevel)} attributes={ATTR_BOLD}>
|
|
81
|
+
{getRiskEmoji(request()!.riskLevel)} {request()!.riskLevel.toUpperCase()}
|
|
82
|
+
</text>
|
|
83
|
+
</box>
|
|
84
|
+
|
|
85
|
+
{/* Action */}
|
|
86
|
+
<box flexDirection="column" marginBottom={1}>
|
|
87
|
+
<text fg="cyan" attributes={ATTR_BOLD}>Action:</text>
|
|
88
|
+
<text fg="white">{request()!.action}</text>
|
|
89
|
+
</box>
|
|
90
|
+
|
|
91
|
+
{/* Target */}
|
|
92
|
+
<box flexDirection="column" marginBottom={1}>
|
|
93
|
+
<text fg="cyan" attributes={ATTR_BOLD}>Target:</text>
|
|
94
|
+
<text fg="white">{request()!.target}</text>
|
|
95
|
+
</box>
|
|
96
|
+
|
|
97
|
+
{/* Description */}
|
|
98
|
+
<box flexDirection="column" marginBottom={1}>
|
|
99
|
+
<text fg="cyan" attributes={ATTR_BOLD}>Description:</text>
|
|
100
|
+
<text fg="gray" wrapMode="word">{request()!.description}</text>
|
|
101
|
+
</box>
|
|
102
|
+
|
|
103
|
+
{/* Expiration */}
|
|
104
|
+
<box flexDirection="row" gap={1} marginBottom={1}>
|
|
105
|
+
<text fg="gray" attributes={ATTR_DIM}>
|
|
106
|
+
Expires: {formatTimeRemaining(request()!.expiresAt)}
|
|
107
|
+
</text>
|
|
108
|
+
</box>
|
|
109
|
+
|
|
110
|
+
{/* Divider */}
|
|
111
|
+
<text fg="gray" attributes={ATTR_DIM}>
|
|
112
|
+
─────────────────────────────────────────────
|
|
113
|
+
</text>
|
|
114
|
+
|
|
115
|
+
{/* Actions */}
|
|
116
|
+
<box flexDirection="row" justifyContent="center" gap={4} marginTop={1}>
|
|
117
|
+
<text fg="green" attributes={ATTR_BOLD}>
|
|
118
|
+
[Y] Approve
|
|
119
|
+
</text>
|
|
120
|
+
<text fg="red" attributes={ATTR_BOLD}>
|
|
121
|
+
[N] Reject
|
|
122
|
+
</text>
|
|
123
|
+
</box>
|
|
124
|
+
|
|
125
|
+
{/* Warning for high risk */}
|
|
126
|
+
<Show when={request()!.riskLevel === "high" || request()!.riskLevel === "critical"}>
|
|
127
|
+
<box marginTop={1}>
|
|
128
|
+
<text fg="red" attributes={ATTR_BOLD}>
|
|
129
|
+
⚠️ This is a {request()!.riskLevel}-risk action. Please review carefully.
|
|
130
|
+
</text>
|
|
131
|
+
</box>
|
|
132
|
+
</Show>
|
|
133
|
+
</box>
|
|
134
|
+
</Show>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function formatTimeRemaining(expiresAt: Date): string {
|
|
139
|
+
const remaining = expiresAt.getTime() - Date.now();
|
|
140
|
+
if (remaining <= 0) {
|
|
141
|
+
return "Expired";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const minutes = Math.floor(remaining / 60000);
|
|
145
|
+
const seconds = Math.floor((remaining % 60000) / 1000);
|
|
146
|
+
|
|
147
|
+
if (minutes > 0) {
|
|
148
|
+
return `${minutes}m ${seconds}s`;
|
|
149
|
+
}
|
|
150
|
+
return `${seconds}s`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Interactive approval prompt for use in message flow
|
|
154
|
+
// Similar to Claude Code's AskUserQuestion pattern
|
|
155
|
+
interface CommandApprovalProps {
|
|
156
|
+
approvalId: string;
|
|
157
|
+
command: string;
|
|
158
|
+
riskLevel: ApprovalRequest["riskLevel"];
|
|
159
|
+
selectedOption: number; // 0 = approve, 1 = reject, -1 = no selection
|
|
160
|
+
onSelect: (option: number) => void;
|
|
161
|
+
submitted: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function CommandApproval(props: CommandApprovalProps): JSX.Element {
|
|
165
|
+
const riskColor = getRiskColor(props.riskLevel);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<box
|
|
169
|
+
flexDirection="column"
|
|
170
|
+
borderStyle="single"
|
|
171
|
+
borderColor={riskColor}
|
|
172
|
+
paddingLeft={1}
|
|
173
|
+
paddingRight={1}
|
|
174
|
+
paddingTop={1}
|
|
175
|
+
paddingBottom={1}
|
|
176
|
+
marginTop={1}
|
|
177
|
+
marginBottom={1}
|
|
178
|
+
>
|
|
179
|
+
{/* Header */}
|
|
180
|
+
<box flexDirection="row" gap={1} marginBottom={1}>
|
|
181
|
+
<text fg={riskColor}>{getRiskEmoji(props.riskLevel)}</text>
|
|
182
|
+
<text fg="white" attributes={ATTR_BOLD}>
|
|
183
|
+
Write Operation Requires Approval
|
|
184
|
+
</text>
|
|
185
|
+
<text fg="gray" attributes={ATTR_DIM}>
|
|
186
|
+
({props.riskLevel} risk)
|
|
187
|
+
</text>
|
|
188
|
+
</box>
|
|
189
|
+
|
|
190
|
+
{/* Command */}
|
|
191
|
+
<box flexDirection="column" marginBottom={1}>
|
|
192
|
+
<text fg="cyan" attributes={ATTR_BOLD}>Command:</text>
|
|
193
|
+
<text fg="yellow">{props.command}</text>
|
|
194
|
+
</box>
|
|
195
|
+
|
|
196
|
+
{/* Options - Claude Code style */}
|
|
197
|
+
<box flexDirection="column" gap={1}>
|
|
198
|
+
<OptionButton
|
|
199
|
+
index={0}
|
|
200
|
+
label="Yes, execute this command"
|
|
201
|
+
selected={props.selectedOption === 0}
|
|
202
|
+
submitted={props.submitted}
|
|
203
|
+
color="green"
|
|
204
|
+
/>
|
|
205
|
+
<OptionButton
|
|
206
|
+
index={1}
|
|
207
|
+
label="No, cancel this operation"
|
|
208
|
+
selected={props.selectedOption === 1}
|
|
209
|
+
submitted={props.submitted}
|
|
210
|
+
color="red"
|
|
211
|
+
/>
|
|
212
|
+
</box>
|
|
213
|
+
|
|
214
|
+
{/* Instructions */}
|
|
215
|
+
<Show when={!props.submitted}>
|
|
216
|
+
<text fg="gray" attributes={ATTR_DIM} marginTop={1}>
|
|
217
|
+
Use ↑↓ to select, Enter to confirm
|
|
218
|
+
</text>
|
|
219
|
+
</Show>
|
|
220
|
+
</box>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface OptionButtonProps {
|
|
225
|
+
index: number;
|
|
226
|
+
label: string;
|
|
227
|
+
selected: boolean;
|
|
228
|
+
submitted: boolean;
|
|
229
|
+
color: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function OptionButton(props: OptionButtonProps): JSX.Element {
|
|
233
|
+
const isSelected = () => props.selected;
|
|
234
|
+
const isSubmitted = () => props.submitted;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<box flexDirection="row" gap={1}>
|
|
238
|
+
<Show
|
|
239
|
+
when={isSelected()}
|
|
240
|
+
fallback={<text fg="gray">○</text>}
|
|
241
|
+
>
|
|
242
|
+
<text fg={props.color}>●</text>
|
|
243
|
+
</Show>
|
|
244
|
+
<text
|
|
245
|
+
fg={isSelected() ? props.color : "white"}
|
|
246
|
+
attributes={isSelected() ? ATTR_BOLD : undefined}
|
|
247
|
+
>
|
|
248
|
+
{props.label}
|
|
249
|
+
</text>
|
|
250
|
+
<Show when={isSelected() && isSubmitted()}>
|
|
251
|
+
<text fg={props.color}> ✓</text>
|
|
252
|
+
</Show>
|
|
253
|
+
</box>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Compact inline version for showing result after approval
|
|
258
|
+
interface ApprovalResultProps {
|
|
259
|
+
command: string;
|
|
260
|
+
approved: boolean;
|
|
261
|
+
riskLevel: ApprovalRequest["riskLevel"];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function ApprovalResult(props: ApprovalResultProps): JSX.Element {
|
|
265
|
+
const riskColor = getRiskColor(props.riskLevel);
|
|
266
|
+
const statusColor = props.approved ? "green" : "red";
|
|
267
|
+
const statusText = props.approved ? "Approved" : "Rejected";
|
|
268
|
+
const statusIcon = props.approved ? "✓" : "✗";
|
|
269
|
+
|
|
270
|
+
return (
|
|
271
|
+
<box flexDirection="row" gap={1} marginTop={1} marginBottom={1}>
|
|
272
|
+
<text fg={riskColor}>{getRiskEmoji(props.riskLevel)}</text>
|
|
273
|
+
<text fg="gray" attributes={ATTR_DIM}>[{props.riskLevel}]</text>
|
|
274
|
+
<text fg={statusColor}>{statusIcon} {statusText}:</text>
|
|
275
|
+
<text fg="yellow">{props.command}</text>
|
|
276
|
+
</box>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// TUI Components - OpenTUI UI integration
|
|
2
|
+
|
|
3
|
+
// Styled span with proper typing for fg/bg/attributes
|
|
4
|
+
export { StyledSpan, type StyledSpanProps } from "./styled-span.js";
|
|
5
|
+
|
|
6
|
+
// Toast notifications
|
|
7
|
+
export {
|
|
8
|
+
ToastProvider,
|
|
9
|
+
toast,
|
|
10
|
+
toastSuccess,
|
|
11
|
+
toastError,
|
|
12
|
+
toastWarning,
|
|
13
|
+
toastInfo,
|
|
14
|
+
toastLoading,
|
|
15
|
+
toastDismiss,
|
|
16
|
+
toastPromise,
|
|
17
|
+
useToasts,
|
|
18
|
+
TOAST_DURATION,
|
|
19
|
+
} from "./toast.js";
|
|
20
|
+
|
|
21
|
+
// Approval dialogs
|
|
22
|
+
export {
|
|
23
|
+
ApprovalDialogProvider,
|
|
24
|
+
useApprovalDialog,
|
|
25
|
+
useDialog,
|
|
26
|
+
useDialogKeyboard,
|
|
27
|
+
type RiskLevel,
|
|
28
|
+
type ApprovalDialogOptions,
|
|
29
|
+
} from "./approval-dialog.js";
|
|
30
|
+
|
|
31
|
+
// Existing components
|
|
32
|
+
export { ApprovalModal, CommandApproval, ApprovalResult, type ApprovalRequest } from "./approval-modal.js";
|
|
33
|
+
export {
|
|
34
|
+
Timeline,
|
|
35
|
+
CompactTimeline,
|
|
36
|
+
investigationEventsToTimeline,
|
|
37
|
+
type TimelineEvent,
|
|
38
|
+
} from "./timeline.js";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* @jsxImportSource @opentui/solid */
|
|
2
|
+
/**
|
|
3
|
+
* Styled span component that properly types fg/bg/attributes props.
|
|
4
|
+
* This is a workaround for @opentui/solid's SpanProps not including TextNodeOptions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { JSX } from "solid-js";
|
|
8
|
+
import type { RGBA } from "@opentui/core";
|
|
9
|
+
|
|
10
|
+
export interface StyledSpanProps {
|
|
11
|
+
children?: JSX.Element | string | number | (JSX.Element | string | number)[];
|
|
12
|
+
fg?: string | RGBA;
|
|
13
|
+
bg?: string | RGBA;
|
|
14
|
+
attributes?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A span element with proper typing for fg, bg, and attributes props.
|
|
19
|
+
* Use this instead of <span> when you need color styling.
|
|
20
|
+
*/
|
|
21
|
+
export function StyledSpan(props: StyledSpanProps): JSX.Element {
|
|
22
|
+
// Cast to any to bypass type checking since the runtime supports these props
|
|
23
|
+
return <span {...(props as any)} />;
|
|
24
|
+
}
|