@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,276 @@
1
+ import { useCallback, useEffect, useLayoutEffect, useState } from "react";
2
+ import { Check, ShieldCheck, Trash2, Users, X } from "lucide-react";
3
+ import { Badge } from "@nastechai/ui/ui/components/badge";
4
+ import { Button } from "@nastechai/ui/ui/components/button";
5
+ import { Spinner } from "@nastechai/ui/ui/components/spinner";
6
+ import { H2 } from "@nastechai/ui/ui/components/typography/h2";
7
+ import { api } from "@/lib/api";
8
+ import type { PairingResponse, PairingUser } from "@/lib/api";
9
+ import { DeleteConfirmDialog } from "@/components/DeleteConfirmDialog";
10
+ import { useToast } from "@nastechai/ui/hooks/use-toast";
11
+ import { useConfirmDelete } from "@nastechai/ui/hooks/use-confirm-delete";
12
+ import { Toast } from "@nastechai/ui/ui/components/toast";
13
+ import { Card, CardContent } from "@nastechai/ui/ui/components/card";
14
+ import { usePageHeader } from "@/contexts/usePageHeader";
15
+
16
+ function getUserKey(user: PairingUser): string {
17
+ return `${user.platform}:${user.user_id}`;
18
+ }
19
+
20
+ function splitUserKey(key: string): { platform: string; user_id: string } {
21
+ const idx = key.indexOf(":");
22
+ if (idx === -1) return { platform: "", user_id: key };
23
+ return { platform: key.slice(0, idx), user_id: key.slice(idx + 1) };
24
+ }
25
+
26
+ function getUserLabel(user: PairingUser): string {
27
+ return user.user_name || user.user_id;
28
+ }
29
+
30
+ export default function PairingPage() {
31
+ const [pending, setPending] = useState<PairingUser[]>([]);
32
+ const [approved, setApproved] = useState<PairingUser[]>([]);
33
+ const [loading, setLoading] = useState(true);
34
+ const [approving, setApproving] = useState<string | null>(null);
35
+ const [clearing, setClearing] = useState(false);
36
+ const { toast, showToast } = useToast();
37
+ const { setEnd } = usePageHeader();
38
+
39
+ const loadPairing = useCallback(() => {
40
+ api
41
+ .getPairing()
42
+ .then((res: PairingResponse) => {
43
+ setPending(res.pending);
44
+ setApproved(res.approved);
45
+ })
46
+ .catch(() => showToast("Failed to load pairing requests", "error"))
47
+ .finally(() => setLoading(false));
48
+ }, [showToast]);
49
+
50
+ useEffect(() => {
51
+ loadPairing();
52
+ }, [loadPairing]);
53
+
54
+ const handleApprove = async (user: PairingUser) => {
55
+ if (!user.code) {
56
+ showToast("Missing pairing code", "error");
57
+ return;
58
+ }
59
+ const key = getUserKey(user);
60
+ setApproving(key);
61
+ try {
62
+ await api.approvePairing(user.platform, user.code);
63
+ showToast(`Approved: "${getUserLabel(user)}"`, "success");
64
+ loadPairing();
65
+ } catch (e) {
66
+ showToast(`Error: ${e}`, "error");
67
+ } finally {
68
+ setApproving(null);
69
+ }
70
+ };
71
+
72
+ const handleClearPending = async () => {
73
+ if (!window.confirm("Clear all pending pairing requests?")) return;
74
+ setClearing(true);
75
+ try {
76
+ const res = await api.clearPendingPairing();
77
+ showToast(`Cleared ${res.cleared} pending request(s)`, "success");
78
+ loadPairing();
79
+ } catch (e) {
80
+ showToast(`Error: ${e}`, "error");
81
+ } finally {
82
+ setClearing(false);
83
+ }
84
+ };
85
+
86
+ const userRevoke = useConfirmDelete({
87
+ onDelete: useCallback(
88
+ async (key: string) => {
89
+ const { platform, user_id } = splitUserKey(key);
90
+ const user = approved.find((u) => getUserKey(u) === key);
91
+ try {
92
+ await api.revokePairing(platform, user_id);
93
+ showToast(
94
+ `Revoked: "${user ? getUserLabel(user) : user_id}"`,
95
+ "success",
96
+ );
97
+ loadPairing();
98
+ } catch (e) {
99
+ showToast(`Error: ${e}`, "error");
100
+ throw e;
101
+ }
102
+ },
103
+ [approved, loadPairing, showToast],
104
+ ),
105
+ });
106
+
107
+ // Put "Clear pending" button in page header
108
+ useLayoutEffect(() => {
109
+ setEnd(
110
+ <Button
111
+ className="uppercase"
112
+ size="sm"
113
+ onClick={handleClearPending}
114
+ disabled={clearing}
115
+ prefix={clearing ? <Spinner /> : <Trash2 className="h-4 w-4" />}
116
+ >
117
+ Clear pending
118
+ </Button>,
119
+ );
120
+ return () => {
121
+ setEnd(null);
122
+ };
123
+ // eslint-disable-next-line react-hooks/exhaustive-deps
124
+ }, [setEnd, clearing]);
125
+
126
+ if (loading) {
127
+ return (
128
+ <div className="flex items-center justify-center py-24">
129
+ <Spinner className="text-2xl text-primary" />
130
+ </div>
131
+ );
132
+ }
133
+
134
+ const pendingRevokeUser = userRevoke.pendingId
135
+ ? approved.find((u) => getUserKey(u) === userRevoke.pendingId)
136
+ : null;
137
+
138
+ return (
139
+ <div className="flex flex-col gap-6">
140
+ <Toast toast={toast} />
141
+
142
+ <DeleteConfirmDialog
143
+ open={userRevoke.isOpen}
144
+ onCancel={userRevoke.cancel}
145
+ onConfirm={userRevoke.confirm}
146
+ title="Revoke access"
147
+ description={
148
+ pendingRevokeUser
149
+ ? `"${getUserLabel(pendingRevokeUser)}" will lose access. This cannot be undone.`
150
+ : "This user will lose access. This cannot be undone."
151
+ }
152
+ confirmLabel="Revoke"
153
+ loading={userRevoke.isDeleting}
154
+ />
155
+
156
+ {/* Pending requests */}
157
+ <div className="flex flex-col gap-3">
158
+ <H2
159
+ variant="sm"
160
+ className="flex items-center gap-2 text-muted-foreground"
161
+ >
162
+ <Users className="h-4 w-4" />
163
+ Pending requests ({pending.length})
164
+ </H2>
165
+
166
+ {pending.length === 0 && (
167
+ <Card>
168
+ <CardContent className="py-8 text-center text-sm text-muted-foreground">
169
+ No pending pairing requests
170
+ </CardContent>
171
+ </Card>
172
+ )}
173
+
174
+ {pending.map((user) => {
175
+ const key = getUserKey(user);
176
+ return (
177
+ <Card key={key}>
178
+ <CardContent className="flex items-start gap-4 py-4">
179
+ <div className="flex-1 min-w-0">
180
+ <div className="flex items-center gap-2 mb-1">
181
+ <Badge tone="outline">{user.platform}</Badge>
182
+ {user.code && (
183
+ <span className="font-mono text-sm">{user.code}</span>
184
+ )}
185
+ </div>
186
+ <div className="flex items-center gap-4 text-xs text-muted-foreground">
187
+ <span className="truncate">{user.user_id}</span>
188
+ {user.user_name && (
189
+ <span className="truncate">{user.user_name}</span>
190
+ )}
191
+ {typeof user.age_minutes === "number" && (
192
+ <span>{user.age_minutes}m ago</span>
193
+ )}
194
+ </div>
195
+ </div>
196
+
197
+ <div className="flex items-center gap-1 shrink-0">
198
+ <Button
199
+ size="sm"
200
+ className="uppercase"
201
+ onClick={() => handleApprove(user)}
202
+ disabled={approving === key || !user.code}
203
+ prefix={
204
+ approving === key ? (
205
+ <Spinner />
206
+ ) : (
207
+ <Check className="h-4 w-4" />
208
+ )
209
+ }
210
+ >
211
+ Approve
212
+ </Button>
213
+ </div>
214
+ </CardContent>
215
+ </Card>
216
+ );
217
+ })}
218
+ </div>
219
+
220
+ {/* Approved users */}
221
+ <div className="flex flex-col gap-3">
222
+ <H2
223
+ variant="sm"
224
+ className="flex items-center gap-2 text-muted-foreground"
225
+ >
226
+ <ShieldCheck className="h-4 w-4" />
227
+ Approved users ({approved.length})
228
+ </H2>
229
+
230
+ {approved.length === 0 && (
231
+ <Card>
232
+ <CardContent className="py-8 text-center text-sm text-muted-foreground">
233
+ No approved users
234
+ </CardContent>
235
+ </Card>
236
+ )}
237
+
238
+ {approved.map((user) => {
239
+ const key = getUserKey(user);
240
+ return (
241
+ <Card key={key}>
242
+ <CardContent className="flex items-start gap-4 py-4">
243
+ <div className="flex-1 min-w-0">
244
+ <div className="flex items-center gap-2 mb-1">
245
+ <Badge tone="outline">{user.platform}</Badge>
246
+ <span className="font-medium text-sm truncate">
247
+ {user.user_id}
248
+ </span>
249
+ </div>
250
+ {user.user_name && (
251
+ <div className="text-xs text-muted-foreground truncate">
252
+ {user.user_name}
253
+ </div>
254
+ )}
255
+ </div>
256
+
257
+ <div className="flex items-center gap-1 shrink-0">
258
+ <Button
259
+ ghost
260
+ size="icon"
261
+ title="Revoke"
262
+ aria-label="Revoke"
263
+ className="text-destructive"
264
+ onClick={() => userRevoke.requestDelete(key)}
265
+ >
266
+ <X />
267
+ </Button>
268
+ </div>
269
+ </CardContent>
270
+ </Card>
271
+ );
272
+ })}
273
+ </div>
274
+ </div>
275
+ );
276
+ }