@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.
Files changed (98) hide show
  1. package/eslint.config.js +23 -0
  2. package/index.html +24 -0
  3. package/package.json +54 -26
  4. package/package.json.bak +89 -0
  5. package/package.json.pub +88 -0
  6. package/src/App.tsx +1173 -0
  7. package/src/components/AuthWidget.tsx +150 -0
  8. package/src/components/AutoField.tsx +206 -0
  9. package/src/components/Backdrop.tsx +93 -0
  10. package/src/components/ChatSidebar.tsx +394 -0
  11. package/src/components/DeleteConfirmDialog.tsx +40 -0
  12. package/src/components/LanguageSwitcher.tsx +186 -0
  13. package/src/components/Markdown.tsx +383 -0
  14. package/src/components/ModelInfoCard.tsx +112 -0
  15. package/src/components/ModelPickerDialog.tsx +470 -0
  16. package/src/components/OAuthLoginModal.tsx +374 -0
  17. package/src/components/OAuthProvidersCard.tsx +287 -0
  18. package/src/components/PlatformsCard.tsx +97 -0
  19. package/src/components/ScheduleBuilder.tsx +273 -0
  20. package/src/components/SidebarFooter.tsx +42 -0
  21. package/src/components/SidebarStatusStrip.tsx +72 -0
  22. package/src/components/SlashPopover.tsx +171 -0
  23. package/src/components/ThemeSwitcher.tsx +243 -0
  24. package/src/components/ToolCall.tsx +228 -0
  25. package/src/components/ToolsetConfigDrawer.tsx +448 -0
  26. package/src/contexts/PageHeaderProvider.tsx +139 -0
  27. package/src/contexts/SystemActions.tsx +120 -0
  28. package/src/contexts/page-header-context.ts +12 -0
  29. package/src/contexts/system-actions-context.ts +18 -0
  30. package/src/contexts/usePageHeader.ts +10 -0
  31. package/src/contexts/useSystemActions.ts +15 -0
  32. package/src/hooks/useModalBehavior.ts +44 -0
  33. package/src/hooks/useSidebarStatus.ts +27 -0
  34. package/src/i18n/af.ts +702 -0
  35. package/src/i18n/context.tsx +123 -0
  36. package/src/i18n/de.ts +701 -0
  37. package/src/i18n/en.ts +708 -0
  38. package/src/i18n/es.ts +701 -0
  39. package/src/i18n/fr.ts +701 -0
  40. package/src/i18n/ga.ts +702 -0
  41. package/src/i18n/hu.ts +702 -0
  42. package/src/i18n/index.ts +2 -0
  43. package/src/i18n/it.ts +701 -0
  44. package/src/i18n/ja.ts +702 -0
  45. package/src/i18n/ko.ts +702 -0
  46. package/src/i18n/pt.ts +702 -0
  47. package/src/i18n/ru.ts +702 -0
  48. package/src/i18n/tr.ts +702 -0
  49. package/src/i18n/types.ts +710 -0
  50. package/src/i18n/uk.ts +702 -0
  51. package/src/i18n/zh-hant.ts +702 -0
  52. package/src/i18n/zh.ts +698 -0
  53. package/src/index.css +274 -0
  54. package/src/lib/api.ts +1585 -0
  55. package/src/lib/dashboard-flags.ts +15 -0
  56. package/src/lib/format.ts +9 -0
  57. package/src/lib/fuzzy.ts +192 -0
  58. package/src/lib/gatewayClient.ts +253 -0
  59. package/src/lib/nested.ts +23 -0
  60. package/src/lib/resolve-page-title.ts +41 -0
  61. package/src/lib/schedule.ts +382 -0
  62. package/src/lib/slashExec.ts +163 -0
  63. package/src/lib/utils.ts +35 -0
  64. package/src/main.tsx +25 -0
  65. package/src/pages/AnalyticsPage.tsx +601 -0
  66. package/src/pages/ChannelsPage.tsx +772 -0
  67. package/src/pages/ChatPage.tsx +889 -0
  68. package/src/pages/ConfigPage.tsx +660 -0
  69. package/src/pages/CronPage.tsx +524 -0
  70. package/src/pages/DocsPage.tsx +69 -0
  71. package/src/pages/EnvPage.tsx +918 -0
  72. package/src/pages/LogsPage.tsx +246 -0
  73. package/src/pages/McpPage.tsx +757 -0
  74. package/src/pages/ModelsPage.tsx +994 -0
  75. package/src/pages/PairingPage.tsx +276 -0
  76. package/src/pages/PluginsPage.tsx +580 -0
  77. package/src/pages/ProfilesPage.tsx +559 -0
  78. package/src/pages/SessionsPage.tsx +936 -0
  79. package/src/pages/SkillsPage.tsx +557 -0
  80. package/src/pages/SystemPage.tsx +1259 -0
  81. package/src/pages/WebhooksPage.tsx +483 -0
  82. package/src/plugins/PluginPage.tsx +64 -0
  83. package/src/plugins/index.ts +6 -0
  84. package/src/plugins/registry.ts +151 -0
  85. package/src/plugins/sdk.d.ts +160 -0
  86. package/src/plugins/slots.ts +199 -0
  87. package/src/plugins/types.ts +37 -0
  88. package/src/plugins/usePlugins.ts +133 -0
  89. package/src/themes/context.tsx +443 -0
  90. package/src/themes/fonts.ts +160 -0
  91. package/src/themes/index.ts +3 -0
  92. package/src/themes/presets.ts +477 -0
  93. package/src/themes/types.ts +187 -0
  94. package/tsconfig.app.json +34 -0
  95. package/tsconfig.json +7 -0
  96. package/tsconfig.node.json +26 -0
  97. package/vite.config.ts +124 -0
  98. 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
+ }