@nastechai/agent 0.16.0 → 0.17.0
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/eslint.config.js +23 -0
- package/index.html +24 -0
- package/package.json +54 -26
- package/package.json.bak +89 -0
- package/package.json.pub +88 -0
- package/src/App.tsx +1173 -0
- package/src/components/AuthWidget.tsx +150 -0
- package/src/components/AutoField.tsx +206 -0
- package/src/components/Backdrop.tsx +93 -0
- package/src/components/ChatSidebar.tsx +394 -0
- package/src/components/DeleteConfirmDialog.tsx +40 -0
- package/src/components/LanguageSwitcher.tsx +186 -0
- package/src/components/Markdown.tsx +383 -0
- package/src/components/ModelInfoCard.tsx +112 -0
- package/src/components/ModelPickerDialog.tsx +470 -0
- package/src/components/OAuthLoginModal.tsx +374 -0
- package/src/components/OAuthProvidersCard.tsx +287 -0
- package/src/components/PlatformsCard.tsx +97 -0
- package/src/components/ScheduleBuilder.tsx +273 -0
- package/src/components/SidebarFooter.tsx +42 -0
- package/src/components/SidebarStatusStrip.tsx +72 -0
- package/src/components/SlashPopover.tsx +171 -0
- package/src/components/ThemeSwitcher.tsx +243 -0
- package/src/components/ToolCall.tsx +228 -0
- package/src/components/ToolsetConfigDrawer.tsx +448 -0
- package/src/contexts/PageHeaderProvider.tsx +139 -0
- package/src/contexts/SystemActions.tsx +120 -0
- package/src/contexts/page-header-context.ts +12 -0
- package/src/contexts/system-actions-context.ts +18 -0
- package/src/contexts/usePageHeader.ts +10 -0
- package/src/contexts/useSystemActions.ts +15 -0
- package/src/hooks/useModalBehavior.ts +44 -0
- package/src/hooks/useSidebarStatus.ts +27 -0
- package/src/i18n/af.ts +702 -0
- package/src/i18n/context.tsx +123 -0
- package/src/i18n/de.ts +701 -0
- package/src/i18n/en.ts +708 -0
- package/src/i18n/es.ts +701 -0
- package/src/i18n/fr.ts +701 -0
- package/src/i18n/ga.ts +702 -0
- package/src/i18n/hu.ts +702 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/it.ts +701 -0
- package/src/i18n/ja.ts +702 -0
- package/src/i18n/ko.ts +702 -0
- package/src/i18n/pt.ts +702 -0
- package/src/i18n/ru.ts +702 -0
- package/src/i18n/tr.ts +702 -0
- package/src/i18n/types.ts +710 -0
- package/src/i18n/uk.ts +702 -0
- package/src/i18n/zh-hant.ts +702 -0
- package/src/i18n/zh.ts +698 -0
- package/src/index.css +274 -0
- package/src/lib/api.ts +1585 -0
- package/src/lib/dashboard-flags.ts +15 -0
- package/src/lib/format.ts +9 -0
- package/src/lib/fuzzy.ts +192 -0
- package/src/lib/gatewayClient.ts +253 -0
- package/src/lib/nested.ts +23 -0
- package/src/lib/resolve-page-title.ts +41 -0
- package/src/lib/schedule.ts +382 -0
- package/src/lib/slashExec.ts +163 -0
- package/src/lib/utils.ts +35 -0
- package/src/main.tsx +25 -0
- package/src/pages/AnalyticsPage.tsx +601 -0
- package/src/pages/ChannelsPage.tsx +772 -0
- package/src/pages/ChatPage.tsx +889 -0
- package/src/pages/ConfigPage.tsx +660 -0
- package/src/pages/CronPage.tsx +524 -0
- package/src/pages/DocsPage.tsx +69 -0
- package/src/pages/EnvPage.tsx +918 -0
- package/src/pages/LogsPage.tsx +246 -0
- package/src/pages/McpPage.tsx +757 -0
- package/src/pages/ModelsPage.tsx +994 -0
- package/src/pages/PairingPage.tsx +276 -0
- package/src/pages/PluginsPage.tsx +580 -0
- package/src/pages/ProfilesPage.tsx +559 -0
- package/src/pages/SessionsPage.tsx +936 -0
- package/src/pages/SkillsPage.tsx +557 -0
- package/src/pages/SystemPage.tsx +1259 -0
- package/src/pages/WebhooksPage.tsx +483 -0
- package/src/plugins/PluginPage.tsx +64 -0
- package/src/plugins/index.ts +6 -0
- package/src/plugins/registry.ts +151 -0
- package/src/plugins/sdk.d.ts +160 -0
- package/src/plugins/slots.ts +199 -0
- package/src/plugins/types.ts +37 -0
- package/src/plugins/usePlugins.ts +133 -0
- package/src/themes/context.tsx +443 -0
- package/src/themes/fonts.ts +160 -0
- package/src/themes/index.ts +3 -0
- package/src/themes/presets.ts +477 -0
- package/src/themes/types.ts +187 -0
- package/tsconfig.app.json +34 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +124 -0
- package/vite.config.ts.timestamp-1780999102396-af6b77b30ebd8.mjs +105 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEffect,
|
|
3
|
+
useLayoutEffect,
|
|
4
|
+
useState,
|
|
5
|
+
useCallback,
|
|
6
|
+
useRef,
|
|
7
|
+
} from "react";
|
|
8
|
+
import { FileText, RefreshCw } from "lucide-react";
|
|
9
|
+
import { api } from "@/lib/api";
|
|
10
|
+
import { Badge } from "@nastechai/ui/ui/components/badge";
|
|
11
|
+
import { Button } from "@nastechai/ui/ui/components/button";
|
|
12
|
+
import { FilterGroup, Segmented } from "@nastechai/ui/ui/components/segmented";
|
|
13
|
+
import { Spinner } from "@nastechai/ui/ui/components/spinner";
|
|
14
|
+
import { Switch } from "@nastechai/ui/ui/components/switch";
|
|
15
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@nastechai/ui/ui/components/card";
|
|
16
|
+
import { Label } from "@nastechai/ui/ui/components/label";
|
|
17
|
+
import { useI18n } from "@/i18n";
|
|
18
|
+
import { usePageHeader } from "@/contexts/usePageHeader";
|
|
19
|
+
import { PluginSlot } from "@/plugins";
|
|
20
|
+
|
|
21
|
+
const FILES = ["agent", "errors", "gateway"] as const;
|
|
22
|
+
const LEVELS = ["ALL", "DEBUG", "INFO", "WARNING", "ERROR"] as const;
|
|
23
|
+
const COMPONENTS = ["all", "gateway", "agent", "tools", "cli", "cron"] as const;
|
|
24
|
+
const LINE_COUNTS = [50, 100, 200, 500] as const;
|
|
25
|
+
|
|
26
|
+
function classifyLine(line: string): "error" | "warning" | "info" | "debug" {
|
|
27
|
+
const upper = line.toUpperCase();
|
|
28
|
+
if (
|
|
29
|
+
upper.includes("ERROR") ||
|
|
30
|
+
upper.includes("CRITICAL") ||
|
|
31
|
+
upper.includes("FATAL")
|
|
32
|
+
)
|
|
33
|
+
return "error";
|
|
34
|
+
if (upper.includes("WARNING") || upper.includes("WARN")) return "warning";
|
|
35
|
+
if (upper.includes("DEBUG")) return "debug";
|
|
36
|
+
return "info";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const LINE_COLORS: Record<string, string> = {
|
|
40
|
+
error: "text-destructive",
|
|
41
|
+
warning: "text-warning",
|
|
42
|
+
info: "text-foreground",
|
|
43
|
+
debug: "text-text-tertiary",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const formatFilterLabel = (value: string) => value.toUpperCase();
|
|
47
|
+
|
|
48
|
+
const toSegmentOptions = <T extends string>(values: readonly T[]) =>
|
|
49
|
+
values.map((v) => ({ value: v, label: formatFilterLabel(v) }));
|
|
50
|
+
|
|
51
|
+
const filterGroupClass =
|
|
52
|
+
"flex min-w-0 w-full flex-col items-start gap-1.5 sm:w-auto sm:max-w-full sm:flex-row sm:items-center";
|
|
53
|
+
|
|
54
|
+
const segmentedClass =
|
|
55
|
+
"w-fit max-w-full flex-wrap justify-start self-start";
|
|
56
|
+
|
|
57
|
+
export default function LogsPage() {
|
|
58
|
+
const [file, setFile] = useState<(typeof FILES)[number]>("agent");
|
|
59
|
+
const [level, setLevel] = useState<(typeof LEVELS)[number]>("ALL");
|
|
60
|
+
const [component, setComponent] =
|
|
61
|
+
useState<(typeof COMPONENTS)[number]>("all");
|
|
62
|
+
const [lineCount, setLineCount] = useState<(typeof LINE_COUNTS)[number]>(100);
|
|
63
|
+
const [autoRefresh, setAutoRefresh] = useState(false);
|
|
64
|
+
const [lines, setLines] = useState<string[]>([]);
|
|
65
|
+
const [loading, setLoading] = useState(false);
|
|
66
|
+
const [error, setError] = useState<string | null>(null);
|
|
67
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
68
|
+
const { t } = useI18n();
|
|
69
|
+
const { setAfterTitle, setEnd } = usePageHeader();
|
|
70
|
+
|
|
71
|
+
const fetchLogs = useCallback(() => {
|
|
72
|
+
setLoading(true);
|
|
73
|
+
setError(null);
|
|
74
|
+
api
|
|
75
|
+
.getLogs({ file, lines: lineCount, level, component })
|
|
76
|
+
.then((resp) => {
|
|
77
|
+
setLines(resp.lines);
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
if (scrollRef.current) {
|
|
80
|
+
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
81
|
+
}
|
|
82
|
+
}, 50);
|
|
83
|
+
})
|
|
84
|
+
.catch((err) => setError(String(err)))
|
|
85
|
+
.finally(() => setLoading(false));
|
|
86
|
+
}, [file, lineCount, level, component]);
|
|
87
|
+
|
|
88
|
+
useLayoutEffect(() => {
|
|
89
|
+
setAfterTitle(
|
|
90
|
+
<span className="flex items-center gap-1.5">
|
|
91
|
+
<Badge tone="secondary" className="text-xs">
|
|
92
|
+
{formatFilterLabel(file)} · {formatFilterLabel(level)} ·{" "}
|
|
93
|
+
{formatFilterLabel(component)}
|
|
94
|
+
</Badge>
|
|
95
|
+
<Button
|
|
96
|
+
type="button"
|
|
97
|
+
ghost
|
|
98
|
+
size="icon"
|
|
99
|
+
className="text-muted-foreground hover:text-foreground"
|
|
100
|
+
onClick={fetchLogs}
|
|
101
|
+
disabled={loading}
|
|
102
|
+
aria-label={t.common.refresh}
|
|
103
|
+
>
|
|
104
|
+
{loading ? <Spinner /> : <RefreshCw />}
|
|
105
|
+
</Button>
|
|
106
|
+
</span>,
|
|
107
|
+
);
|
|
108
|
+
setEnd(
|
|
109
|
+
<div className="flex w-full min-w-0 flex-wrap items-center justify-start gap-2 sm:justify-end sm:gap-3">
|
|
110
|
+
<div className="flex items-center gap-2">
|
|
111
|
+
<Label htmlFor="logs-auto-refresh" className="text-xs cursor-pointer">
|
|
112
|
+
{t.logs.autoRefresh}
|
|
113
|
+
</Label>
|
|
114
|
+
<Switch
|
|
115
|
+
checked={autoRefresh}
|
|
116
|
+
onCheckedChange={setAutoRefresh}
|
|
117
|
+
id="logs-auto-refresh"
|
|
118
|
+
/>
|
|
119
|
+
{autoRefresh && (
|
|
120
|
+
<Badge tone="success" className="text-xs">
|
|
121
|
+
<span className="mr-1 inline-block h-1.5 w-1.5 animate-pulse rounded-full bg-current" />
|
|
122
|
+
{t.common.live}
|
|
123
|
+
</Badge>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
</div>,
|
|
127
|
+
);
|
|
128
|
+
return () => {
|
|
129
|
+
setAfterTitle(null);
|
|
130
|
+
setEnd(null);
|
|
131
|
+
};
|
|
132
|
+
}, [
|
|
133
|
+
autoRefresh,
|
|
134
|
+
component,
|
|
135
|
+
file,
|
|
136
|
+
level,
|
|
137
|
+
loading,
|
|
138
|
+
setAfterTitle,
|
|
139
|
+
setEnd,
|
|
140
|
+
t.common.live,
|
|
141
|
+
t.common.refresh,
|
|
142
|
+
t.logs.autoRefresh,
|
|
143
|
+
fetchLogs,
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
fetchLogs();
|
|
148
|
+
}, [fetchLogs]);
|
|
149
|
+
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
if (!autoRefresh) return;
|
|
152
|
+
const interval = setInterval(fetchLogs, 5000);
|
|
153
|
+
return () => clearInterval(interval);
|
|
154
|
+
}, [autoRefresh, fetchLogs]);
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div className="flex min-w-0 max-w-full flex-col gap-4">
|
|
158
|
+
<PluginSlot name="logs:top" />
|
|
159
|
+
<div
|
|
160
|
+
role="toolbar"
|
|
161
|
+
aria-label={t.logs.title}
|
|
162
|
+
className="flex min-w-0 max-w-full flex-col items-start gap-3 sm:flex-row sm:flex-wrap sm:items-start sm:gap-x-6 sm:gap-y-3"
|
|
163
|
+
>
|
|
164
|
+
<FilterGroup label={t.logs.file} className={filterGroupClass}>
|
|
165
|
+
<Segmented
|
|
166
|
+
className={segmentedClass}
|
|
167
|
+
value={file}
|
|
168
|
+
onChange={setFile}
|
|
169
|
+
options={toSegmentOptions(FILES)}
|
|
170
|
+
/>
|
|
171
|
+
</FilterGroup>
|
|
172
|
+
|
|
173
|
+
<FilterGroup label={t.logs.level} className={filterGroupClass}>
|
|
174
|
+
<Segmented
|
|
175
|
+
className={segmentedClass}
|
|
176
|
+
value={level}
|
|
177
|
+
onChange={setLevel}
|
|
178
|
+
options={toSegmentOptions(LEVELS)}
|
|
179
|
+
/>
|
|
180
|
+
</FilterGroup>
|
|
181
|
+
|
|
182
|
+
<FilterGroup label={t.logs.component} className={filterGroupClass}>
|
|
183
|
+
<Segmented
|
|
184
|
+
className={segmentedClass}
|
|
185
|
+
value={component}
|
|
186
|
+
onChange={setComponent}
|
|
187
|
+
options={toSegmentOptions(COMPONENTS)}
|
|
188
|
+
/>
|
|
189
|
+
</FilterGroup>
|
|
190
|
+
|
|
191
|
+
<FilterGroup label={t.logs.lines} className={filterGroupClass}>
|
|
192
|
+
<Segmented
|
|
193
|
+
className={segmentedClass}
|
|
194
|
+
value={String(lineCount)}
|
|
195
|
+
onChange={(v) =>
|
|
196
|
+
setLineCount(Number(v) as (typeof LINE_COUNTS)[number])
|
|
197
|
+
}
|
|
198
|
+
options={LINE_COUNTS.map((n) => ({
|
|
199
|
+
value: String(n),
|
|
200
|
+
label: String(n),
|
|
201
|
+
}))}
|
|
202
|
+
/>
|
|
203
|
+
</FilterGroup>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<Card className="min-w-0 max-w-full overflow-hidden">
|
|
207
|
+
<CardHeader className="py-3 px-4">
|
|
208
|
+
<CardTitle className="text-sm flex items-center gap-2">
|
|
209
|
+
<FileText className="h-4 w-4" />
|
|
210
|
+
{file}.log
|
|
211
|
+
</CardTitle>
|
|
212
|
+
</CardHeader>
|
|
213
|
+
<CardContent className="p-0">
|
|
214
|
+
{error && (
|
|
215
|
+
<div className="bg-destructive/10 border-b border-destructive/20 p-3">
|
|
216
|
+
<p className="text-sm text-destructive">{error}</p>
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
<div
|
|
221
|
+
ref={scrollRef}
|
|
222
|
+
className="max-w-full min-h-[400px] max-h-[calc(100vh-220px)] overflow-auto p-4 font-mono-ui text-xs leading-5 break-words"
|
|
223
|
+
>
|
|
224
|
+
{lines.length === 0 && !loading && (
|
|
225
|
+
<p className="text-muted-foreground text-center py-8">
|
|
226
|
+
{t.logs.noLogLines}
|
|
227
|
+
</p>
|
|
228
|
+
)}
|
|
229
|
+
{lines.map((line, i) => {
|
|
230
|
+
const cls = classifyLine(line);
|
|
231
|
+
return (
|
|
232
|
+
<div
|
|
233
|
+
key={i}
|
|
234
|
+
className={`${LINE_COLORS[cls]} hover:bg-secondary/20 px-1 -mx-1`}
|
|
235
|
+
>
|
|
236
|
+
{line}
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
})}
|
|
240
|
+
</div>
|
|
241
|
+
</CardContent>
|
|
242
|
+
</Card>
|
|
243
|
+
<PluginSlot name="logs:bottom" />
|
|
244
|
+
</div>
|
|
245
|
+
);
|
|
246
|
+
}
|