@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,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
|
+
}
|