@rickydata/react 0.1.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/dist/index.js ADDED
@@ -0,0 +1,917 @@
1
+ // src/providers/RickyDataProvider.tsx
2
+ import { createContext, useContext, useMemo } from "react";
3
+ import { AgentClient } from "rickydata/agent";
4
+ import { jsx } from "react/jsx-runtime";
5
+ var RickyDataContext = createContext(null);
6
+ function RickyDataProvider({ config, client, children }) {
7
+ const agentClient = useMemo(() => {
8
+ if (client) return client;
9
+ if (!config) throw new Error("RickyDataProvider requires either `config` or `client`");
10
+ return new AgentClient({
11
+ tokenGetter: config.getAuthToken,
12
+ gatewayUrl: config.gatewayUrl
13
+ });
14
+ }, [client, config?.getAuthToken, config?.gatewayUrl]);
15
+ return /* @__PURE__ */ jsx(RickyDataContext.Provider, { value: agentClient, children });
16
+ }
17
+ function useRickyData() {
18
+ const ctx = useContext(RickyDataContext);
19
+ if (!ctx) {
20
+ throw new Error("useRickyData must be used within a <RickyDataProvider>");
21
+ }
22
+ return ctx;
23
+ }
24
+
25
+ // src/hooks/agents.ts
26
+ import { useQuery } from "@tanstack/react-query";
27
+ var agentKeys = {
28
+ all: ["agents"],
29
+ lists: () => [...agentKeys.all, "list"],
30
+ details: () => [...agentKeys.all, "detail"],
31
+ detail: (id) => [...agentKeys.details(), id]
32
+ };
33
+ function useAgents() {
34
+ const client = useRickyData();
35
+ return useQuery({
36
+ queryKey: agentKeys.lists(),
37
+ queryFn: () => client.listAgents(),
38
+ staleTime: 5 * 60 * 1e3
39
+ // 5 min
40
+ });
41
+ }
42
+ function useAgent(agentId) {
43
+ const client = useRickyData();
44
+ return useQuery({
45
+ queryKey: agentKeys.detail(agentId),
46
+ queryFn: () => client.getAgent(agentId),
47
+ enabled: !!agentId
48
+ });
49
+ }
50
+
51
+ // src/hooks/apikey.ts
52
+ import { useQuery as useQuery2, useMutation, useQueryClient } from "@tanstack/react-query";
53
+ var apiKeyKeys = {
54
+ all: ["apikey"],
55
+ status: () => [...apiKeyKeys.all, "status"],
56
+ openai: () => [...apiKeyKeys.all, "openai"]
57
+ };
58
+ function useApiKeyStatus() {
59
+ const client = useRickyData();
60
+ return useQuery2({
61
+ queryKey: apiKeyKeys.status(),
62
+ queryFn: () => client.getApiKeyStatus(),
63
+ staleTime: 3e4
64
+ });
65
+ }
66
+ function useSetApiKey() {
67
+ const client = useRickyData();
68
+ const queryClient = useQueryClient();
69
+ return useMutation({
70
+ mutationFn: (apiKey) => client.setApiKey(apiKey),
71
+ onSuccess: () => {
72
+ queryClient.invalidateQueries({ queryKey: apiKeyKeys.status() });
73
+ }
74
+ });
75
+ }
76
+ function useDeleteApiKey() {
77
+ const client = useRickyData();
78
+ const queryClient = useQueryClient();
79
+ return useMutation({
80
+ mutationFn: () => client.deleteApiKey(),
81
+ onSuccess: () => {
82
+ queryClient.invalidateQueries({ queryKey: apiKeyKeys.status() });
83
+ }
84
+ });
85
+ }
86
+ function useOpenAIApiKeyStatus() {
87
+ const client = useRickyData();
88
+ return useQuery2({
89
+ queryKey: apiKeyKeys.openai(),
90
+ queryFn: () => client.getOpenAIApiKeyStatus(),
91
+ staleTime: 3e4
92
+ });
93
+ }
94
+ function useSetOpenAIApiKey() {
95
+ const client = useRickyData();
96
+ const queryClient = useQueryClient();
97
+ return useMutation({
98
+ mutationFn: (apiKey) => client.storeOpenAIApiKey(apiKey),
99
+ onSuccess: () => {
100
+ queryClient.invalidateQueries({ queryKey: apiKeyKeys.openai() });
101
+ }
102
+ });
103
+ }
104
+
105
+ // src/hooks/balance.ts
106
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
107
+ var balanceKeys = {
108
+ all: ["wallet-balance"],
109
+ balance: () => [...balanceKeys.all, "balance"],
110
+ transactions: (limit, offset) => [...balanceKeys.all, "transactions", { limit, offset }]
111
+ };
112
+ function useWalletBalance(opts) {
113
+ const client = useRickyData();
114
+ const query = useQuery3({
115
+ queryKey: balanceKeys.balance(),
116
+ queryFn: () => client.getWalletBalance(),
117
+ staleTime: opts?.staleTime ?? 6e4,
118
+ enabled: opts?.enabled !== false
119
+ });
120
+ const balance = query.data?.availableBalance ?? "0";
121
+ const num = parseFloat(balance);
122
+ const balanceDisplay = isNaN(num) ? "$0.00" : num >= 0.01 ? `$${num.toFixed(2)}` : num > 0 ? `$${num.toFixed(4)}` : "$0.00";
123
+ return {
124
+ ...query,
125
+ balance,
126
+ balanceDisplay,
127
+ depositAddress: query.data?.unifiedDepositAddress,
128
+ agentSpends: query.data?.agentSpends,
129
+ refresh: () => query.refetch()
130
+ };
131
+ }
132
+ function useWalletTransactions(limit, offset) {
133
+ const client = useRickyData();
134
+ return useQuery3({
135
+ queryKey: balanceKeys.transactions(limit, offset),
136
+ queryFn: () => client.getWalletTransactions(limit, offset),
137
+ staleTime: 3e4
138
+ });
139
+ }
140
+
141
+ // src/hooks/sessions.ts
142
+ import { useQuery as useQuery4, useMutation as useMutation2, useQueryClient as useQueryClient2 } from "@tanstack/react-query";
143
+ var sessionKeys = {
144
+ all: ["sessions"],
145
+ lists: (agentId) => [...sessionKeys.all, "list", agentId],
146
+ details: () => [...sessionKeys.all, "detail"],
147
+ detail: (agentId, sessionId) => [...sessionKeys.details(), agentId, sessionId]
148
+ };
149
+ function useSessions(agentId) {
150
+ const client = useRickyData();
151
+ return useQuery4({
152
+ queryKey: sessionKeys.lists(agentId),
153
+ queryFn: () => client.listSessions(agentId),
154
+ enabled: !!agentId,
155
+ staleTime: 3e4
156
+ });
157
+ }
158
+ function useSession(agentId, sessionId) {
159
+ const client = useRickyData();
160
+ return useQuery4({
161
+ queryKey: sessionKeys.detail(agentId, sessionId),
162
+ queryFn: () => client.getSession(agentId, sessionId),
163
+ enabled: !!agentId && !!sessionId
164
+ });
165
+ }
166
+ function useDeleteSession() {
167
+ const client = useRickyData();
168
+ const queryClient = useQueryClient2();
169
+ return useMutation2({
170
+ mutationFn: ({ agentId, sessionId }) => client.deleteSession(agentId, sessionId),
171
+ onSuccess: (_data, { agentId }) => {
172
+ queryClient.invalidateQueries({ queryKey: sessionKeys.lists(agentId) });
173
+ }
174
+ });
175
+ }
176
+
177
+ // src/hooks/wallet-settings.ts
178
+ import { useQuery as useQuery5, useMutation as useMutation3, useQueryClient as useQueryClient3 } from "@tanstack/react-query";
179
+ var walletSettingsKeys = {
180
+ all: ["wallet-settings"],
181
+ settings: () => [...walletSettingsKeys.all, "current"]
182
+ };
183
+ function useWalletSettings() {
184
+ const client = useRickyData();
185
+ const queryClient = useQueryClient3();
186
+ const query = useQuery5({
187
+ queryKey: walletSettingsKeys.settings(),
188
+ queryFn: () => client.getWalletSettings(),
189
+ staleTime: 6e4
190
+ });
191
+ const mutation = useMutation3({
192
+ mutationFn: (settings) => client.updateWalletSettings(settings),
193
+ onSuccess: (data) => {
194
+ queryClient.setQueryData(walletSettingsKeys.settings(), data);
195
+ }
196
+ });
197
+ return {
198
+ ...query,
199
+ settings: query.data,
200
+ updateSettings: mutation.mutateAsync,
201
+ isUpdating: mutation.isPending
202
+ };
203
+ }
204
+
205
+ // src/hooks/secrets.ts
206
+ import { useState, useEffect, useCallback } from "react";
207
+ function useSecrets({ agentId, mcpServers = [] }) {
208
+ const client = useRickyData();
209
+ const [sections, setSections] = useState([]);
210
+ const [loading, setLoading] = useState(true);
211
+ const fetchRequirements = useCallback(async () => {
212
+ setLoading(true);
213
+ const result = [];
214
+ try {
215
+ const { configured } = await client.getApiKeyStatus();
216
+ if (!configured) {
217
+ result.push({
218
+ id: "anthropic",
219
+ label: "Anthropic API Key",
220
+ keys: ["ANTHROPIC_API_KEY"],
221
+ configuredKeys: [],
222
+ save: async (secrets) => {
223
+ const key = secrets["ANTHROPIC_API_KEY"];
224
+ if (key) await client.setApiKey(key);
225
+ }
226
+ });
227
+ }
228
+ } catch {
229
+ }
230
+ try {
231
+ const status = await client.getAgentSecretStatus(agentId);
232
+ if (status.missingRequired.length > 0) {
233
+ result.push({
234
+ id: `agent-${agentId}`,
235
+ label: "Agent Secrets",
236
+ keys: status.missingRequired,
237
+ configuredKeys: status.configuredSecrets,
238
+ save: async (secrets) => {
239
+ await client.storeAgentSecrets(agentId, secrets);
240
+ }
241
+ });
242
+ }
243
+ } catch {
244
+ }
245
+ if (mcpServers.length > 0) {
246
+ try {
247
+ const reqs = await client.getMcpRequirements(agentId);
248
+ for (const server of reqs.servers) {
249
+ const missing = server.missing || server.required.filter((k) => !server.configured?.includes(k));
250
+ if (missing.length > 0) {
251
+ result.push({
252
+ id: `mcp-${server.serverId}`,
253
+ label: `MCP: ${server.name || server.serverId}`,
254
+ keys: missing,
255
+ configuredKeys: server.configured || [],
256
+ save: async (secrets) => {
257
+ await client.storeMcpSecrets(server.serverId, secrets);
258
+ }
259
+ });
260
+ }
261
+ }
262
+ } catch {
263
+ }
264
+ }
265
+ setSections(result);
266
+ setLoading(false);
267
+ }, [client, agentId, mcpServers.join(",")]);
268
+ useEffect(() => {
269
+ fetchRequirements();
270
+ }, [fetchRequirements]);
271
+ return {
272
+ sections,
273
+ loading,
274
+ allConfigured: !loading && sections.length === 0,
275
+ refresh: fetchRequirements
276
+ };
277
+ }
278
+
279
+ // src/hooks/chat.ts
280
+ import { useState as useState2, useCallback as useCallback2, useRef, useEffect as useEffect2 } from "react";
281
+ import { streamSSEEvents } from "rickydata/agent";
282
+ function useAgentChat({
283
+ agentId,
284
+ model = "haiku",
285
+ resumeSessionId
286
+ }) {
287
+ const client = useRickyData();
288
+ const [messages, setMessages] = useState2([]);
289
+ const [messagesLoading, setMessagesLoading] = useState2(!!resumeSessionId);
290
+ const [sending, setSending] = useState2(false);
291
+ const [sessionId, setSessionId] = useState2(resumeSessionId || null);
292
+ const [streamingPhase, setStreamingPhase] = useState2("idle");
293
+ const [activeTools, setActiveTools] = useState2([]);
294
+ const [apiKeyConfigured, setApiKeyConfigured] = useState2(null);
295
+ const modelRef = useRef(model);
296
+ modelRef.current = model;
297
+ useEffect2(() => {
298
+ let cancelled = false;
299
+ client.getApiKeyStatus().then(({ configured }) => {
300
+ if (!cancelled) setApiKeyConfigured(configured);
301
+ }).catch(() => {
302
+ if (!cancelled) setApiKeyConfigured(false);
303
+ });
304
+ return () => {
305
+ cancelled = true;
306
+ };
307
+ }, [client]);
308
+ useEffect2(() => {
309
+ if (!resumeSessionId) return;
310
+ let cancelled = false;
311
+ client.getSession(agentId, resumeSessionId).then((session) => {
312
+ if (cancelled) return;
313
+ const msgs = session.messages.map((m, i) => ({
314
+ id: `restored-${i}`,
315
+ role: m.role === "user" ? "user" : "agent",
316
+ content: m.content,
317
+ timestamp: m.timestamp || session.createdAt
318
+ }));
319
+ setMessages(msgs);
320
+ setMessagesLoading(false);
321
+ }).catch(() => {
322
+ if (!cancelled) setMessagesLoading(false);
323
+ });
324
+ return () => {
325
+ cancelled = true;
326
+ };
327
+ }, [client, agentId, resumeSessionId]);
328
+ const sendMessage = useCallback2(async (text) => {
329
+ if (!text.trim() || sending) return;
330
+ if (apiKeyConfigured === false) {
331
+ setMessages((prev) => [...prev, {
332
+ id: `msg-${Date.now()}-error`,
333
+ role: "agent",
334
+ content: "Anthropic API key required. Configure ANTHROPIC_API_KEY before chatting.",
335
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
336
+ }]);
337
+ return;
338
+ }
339
+ const userMessage = {
340
+ id: `msg-${Date.now()}`,
341
+ role: "user",
342
+ content: text.trim(),
343
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
344
+ };
345
+ setMessages((prev) => [...prev, userMessage]);
346
+ setSending(true);
347
+ try {
348
+ let sid = sessionId;
349
+ if (!sid) {
350
+ const session = await client.createSession(agentId, modelRef.current);
351
+ sid = session.id;
352
+ setSessionId(sid);
353
+ }
354
+ const response = await client.chatRaw(agentId, sid, text.trim(), modelRef.current);
355
+ let textAccum = "";
356
+ const toolExecutions = [];
357
+ let messageCost = "";
358
+ let toolIdCounter = 0;
359
+ const streamingMsgId = `agent-streaming-${Date.now()}`;
360
+ setActiveTools([]);
361
+ setStreamingPhase("idle");
362
+ await streamSSEEvents(response, (event) => {
363
+ switch (event.type) {
364
+ case "text":
365
+ setStreamingPhase("streaming");
366
+ setActiveTools([]);
367
+ textAccum += event.data;
368
+ setMessages((prev) => {
369
+ const existing = prev.find((m) => m.id === streamingMsgId);
370
+ if (!existing) {
371
+ return [...prev, {
372
+ id: streamingMsgId,
373
+ role: "agent",
374
+ content: textAccum,
375
+ toolExecutions: toolExecutions.length > 0 ? [...toolExecutions] : void 0,
376
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
377
+ }];
378
+ }
379
+ return prev.map(
380
+ (m) => m.id === streamingMsgId ? { ...m, content: textAccum, toolExecutions: toolExecutions.length > 0 ? [...toolExecutions] : void 0 } : m
381
+ );
382
+ });
383
+ break;
384
+ case "tool_call": {
385
+ const data = event.data;
386
+ const displayName = data.displayName || data.name.split("__").pop() || data.name;
387
+ toolExecutions.push({
388
+ id: data.id || `tool-${++toolIdCounter}`,
389
+ name: data.name,
390
+ displayName,
391
+ args: data.args
392
+ });
393
+ setActiveTools((prev) => [...prev, displayName]);
394
+ setStreamingPhase("tools");
395
+ break;
396
+ }
397
+ case "tool_result": {
398
+ const resultData = event.data;
399
+ const resultContent = resultData.result ?? resultData.content;
400
+ let matched = false;
401
+ if (resultData.id) {
402
+ const idx = toolExecutions.findIndex((t) => t.id === resultData.id);
403
+ if (idx !== -1) {
404
+ toolExecutions[idx].result = { content: resultContent, isError: resultData.isError };
405
+ matched = true;
406
+ }
407
+ }
408
+ if (!matched) {
409
+ for (let i = toolExecutions.length - 1; i >= 0; i--) {
410
+ if (toolExecutions[i].name === resultData.name && !toolExecutions[i].result) {
411
+ toolExecutions[i].result = { content: resultContent, isError: resultData.isError };
412
+ break;
413
+ }
414
+ }
415
+ }
416
+ break;
417
+ }
418
+ case "done":
419
+ if (event.data.cost) messageCost = event.data.cost;
420
+ break;
421
+ case "error":
422
+ textAccum += `
423
+
424
+ Error: ${event.data.message}`;
425
+ break;
426
+ }
427
+ });
428
+ setMessages((prev) => {
429
+ const withoutStreaming = prev.filter((m) => m.id !== streamingMsgId);
430
+ return [...withoutStreaming, {
431
+ id: `msg-${Date.now()}-agent`,
432
+ role: "agent",
433
+ content: textAccum || "(No response)",
434
+ toolExecutions: toolExecutions.length > 0 ? [...toolExecutions] : void 0,
435
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
436
+ costUSD: messageCost || void 0
437
+ }];
438
+ });
439
+ } catch (err) {
440
+ const errStatus = typeof err === "object" && err !== null && "status" in err ? err.status : 0;
441
+ const errMessage = err instanceof Error ? err.message : String(err);
442
+ if (errStatus === 402) {
443
+ setMessages((prev) => [...prev, {
444
+ id: `msg-${Date.now()}-error`,
445
+ role: "agent",
446
+ content: "Insufficient balance. Please deposit funds to continue.",
447
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
448
+ }]);
449
+ } else if (errStatus === 404 || /session not found|not found/i.test(errMessage)) {
450
+ setSessionId(null);
451
+ setMessages((prev) => [...prev, {
452
+ id: `msg-${Date.now()}-error`,
453
+ role: "agent",
454
+ content: "Session expired. Starting a new conversation...",
455
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
456
+ }]);
457
+ } else if (errStatus === 401 || /unauthorized|authentication|invalid.*token|expired.*token/i.test(errMessage)) {
458
+ setSessionId(null);
459
+ setMessages((prev) => [...prev, {
460
+ id: `msg-${Date.now()}-error`,
461
+ role: "agent",
462
+ content: "Re-authenticating... please send again.",
463
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
464
+ }]);
465
+ } else if (errStatus === 400 && /anthropic api key|required/i.test(errMessage)) {
466
+ setApiKeyConfigured(false);
467
+ setMessages((prev) => [...prev, {
468
+ id: `msg-${Date.now()}-error`,
469
+ role: "agent",
470
+ content: errMessage,
471
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
472
+ }]);
473
+ } else {
474
+ setMessages((prev) => [...prev, {
475
+ id: `msg-${Date.now()}-error`,
476
+ role: "agent",
477
+ content: `Error: ${errMessage}`,
478
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
479
+ }]);
480
+ }
481
+ } finally {
482
+ setSending(false);
483
+ setStreamingPhase("idle");
484
+ setActiveTools([]);
485
+ }
486
+ }, [client, agentId, sending, sessionId, apiKeyConfigured]);
487
+ const refreshApiKeyStatus = useCallback2(() => {
488
+ client.getApiKeyStatus().then(({ configured }) => setApiKeyConfigured(configured)).catch(() => {
489
+ });
490
+ }, [client]);
491
+ const prevApiKeyRef = useRef(apiKeyConfigured);
492
+ useEffect2(() => {
493
+ if (prevApiKeyRef.current === false && apiKeyConfigured === true) {
494
+ setMessages((prev) => [...prev, {
495
+ id: `msg-${Date.now()}-system`,
496
+ role: "agent",
497
+ content: "API key configured successfully! You can start chatting now.",
498
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
499
+ }]);
500
+ }
501
+ prevApiKeyRef.current = apiKeyConfigured;
502
+ }, [apiKeyConfigured]);
503
+ const clearChat = useCallback2(() => {
504
+ setMessages([]);
505
+ setSessionId(null);
506
+ setStreamingPhase("idle");
507
+ setActiveTools([]);
508
+ }, []);
509
+ return {
510
+ messages,
511
+ messagesLoading,
512
+ sending,
513
+ sessionId,
514
+ streamingPhase,
515
+ activeTools,
516
+ apiKeyConfigured,
517
+ sendMessage,
518
+ clearChat,
519
+ refreshApiKeyStatus
520
+ };
521
+ }
522
+
523
+ // src/components/SecretForm.tsx
524
+ import { useState as useState3 } from "react";
525
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
526
+ var container = {
527
+ display: "flex",
528
+ flexDirection: "column",
529
+ gap: "12px"
530
+ };
531
+ var labelStyle = {
532
+ display: "block",
533
+ fontSize: "12px",
534
+ fontWeight: 500,
535
+ fontFamily: "monospace",
536
+ color: "#9ca3af",
537
+ marginBottom: "4px"
538
+ };
539
+ var configuredBadge = {
540
+ marginLeft: "6px",
541
+ color: "#10b981",
542
+ fontFamily: "sans-serif"
543
+ };
544
+ var inputWrapper = {
545
+ position: "relative"
546
+ };
547
+ var inputStyle = {
548
+ width: "100%",
549
+ padding: "8px 36px 8px 12px",
550
+ fontSize: "14px",
551
+ fontFamily: "monospace",
552
+ borderRadius: "8px",
553
+ border: "1px solid rgba(255,255,255,0.1)",
554
+ backgroundColor: "#1f2937",
555
+ color: "#fff",
556
+ outline: "none",
557
+ boxSizing: "border-box"
558
+ };
559
+ var toggleBtn = {
560
+ position: "absolute",
561
+ right: "8px",
562
+ top: "50%",
563
+ transform: "translateY(-50%)",
564
+ padding: "4px",
565
+ background: "none",
566
+ border: "none",
567
+ color: "#9ca3af",
568
+ cursor: "pointer",
569
+ fontSize: "14px"
570
+ };
571
+ var feedbackBase = {
572
+ display: "flex",
573
+ alignItems: "center",
574
+ gap: "8px",
575
+ padding: "8px",
576
+ borderRadius: "8px",
577
+ fontSize: "12px"
578
+ };
579
+ var feedbackSuccess = {
580
+ ...feedbackBase,
581
+ backgroundColor: "rgba(16,185,129,0.1)",
582
+ border: "1px solid rgba(16,185,129,0.2)",
583
+ color: "#34d399"
584
+ };
585
+ var feedbackError = {
586
+ ...feedbackBase,
587
+ backgroundColor: "rgba(239,68,68,0.1)",
588
+ border: "1px solid rgba(239,68,68,0.2)",
589
+ color: "#f87171"
590
+ };
591
+ var btnRow = {
592
+ display: "flex",
593
+ alignItems: "center",
594
+ justifyContent: "space-between"
595
+ };
596
+ var saveBtn = {
597
+ display: "inline-flex",
598
+ alignItems: "center",
599
+ gap: "6px",
600
+ padding: "6px 12px",
601
+ fontSize: "12px",
602
+ fontWeight: 500,
603
+ borderRadius: "6px",
604
+ border: "none",
605
+ backgroundColor: "#6366f1",
606
+ color: "#fff",
607
+ cursor: "pointer",
608
+ transition: "opacity 0.15s"
609
+ };
610
+ var cancelBtn = {
611
+ padding: "6px 12px",
612
+ fontSize: "12px",
613
+ fontWeight: 500,
614
+ borderRadius: "6px",
615
+ border: "none",
616
+ background: "none",
617
+ color: "#9ca3af",
618
+ cursor: "pointer"
619
+ };
620
+ var deleteBtn = {
621
+ display: "inline-flex",
622
+ alignItems: "center",
623
+ gap: "4px",
624
+ padding: "6px 10px",
625
+ fontSize: "12px",
626
+ fontWeight: 500,
627
+ borderRadius: "6px",
628
+ border: "none",
629
+ background: "none",
630
+ color: "#f87171",
631
+ cursor: "pointer"
632
+ };
633
+ function SecretForm({ secretKeys, configuredKeys, onSave, onDelete, onClose, className }) {
634
+ const [values, setValues] = useState3(() => {
635
+ const init = {};
636
+ for (const key of secretKeys) init[key] = "";
637
+ return init;
638
+ });
639
+ const [showValues, setShowValues] = useState3({});
640
+ const [saving, setSaving] = useState3(false);
641
+ const [deleting, setDeleting] = useState3(false);
642
+ const [feedback, setFeedback] = useState3(null);
643
+ const hasAnyValue = Object.values(values).some((v) => v.trim().length > 0);
644
+ const handleSave = async () => {
645
+ const secrets = {};
646
+ for (const [key, val] of Object.entries(values)) {
647
+ if (val.trim()) secrets[key] = val.trim();
648
+ }
649
+ if (Object.keys(secrets).length === 0) return;
650
+ setSaving(true);
651
+ setFeedback(null);
652
+ try {
653
+ await onSave(secrets);
654
+ setFeedback({ type: "success", message: "Secrets saved successfully." });
655
+ setValues((prev) => {
656
+ const next = { ...prev };
657
+ for (const k of Object.keys(next)) next[k] = "";
658
+ return next;
659
+ });
660
+ if (onClose) setTimeout(onClose, 800);
661
+ } catch (err) {
662
+ setFeedback({ type: "error", message: err instanceof Error ? err.message : "Failed to save." });
663
+ } finally {
664
+ setSaving(false);
665
+ }
666
+ };
667
+ const handleDelete = async () => {
668
+ if (!onDelete) return;
669
+ setDeleting(true);
670
+ setFeedback(null);
671
+ try {
672
+ await onDelete();
673
+ setFeedback({ type: "success", message: "Secrets removed." });
674
+ if (onClose) setTimeout(onClose, 800);
675
+ } catch (err) {
676
+ setFeedback({ type: "error", message: err instanceof Error ? err.message : "Failed to remove." });
677
+ } finally {
678
+ setDeleting(false);
679
+ }
680
+ };
681
+ return /* @__PURE__ */ jsxs("div", { style: container, className, children: [
682
+ secretKeys.map((key) => {
683
+ const isVisible = showValues[key] ?? false;
684
+ const isConfigured = configuredKeys.includes(key);
685
+ return /* @__PURE__ */ jsxs("div", { children: [
686
+ /* @__PURE__ */ jsxs("label", { style: labelStyle, children: [
687
+ key,
688
+ isConfigured && /* @__PURE__ */ jsx2("span", { style: configuredBadge, children: "(configured)" })
689
+ ] }),
690
+ /* @__PURE__ */ jsxs("div", { style: inputWrapper, children: [
691
+ /* @__PURE__ */ jsx2(
692
+ "input",
693
+ {
694
+ type: isVisible ? "text" : "password",
695
+ value: values[key],
696
+ onChange: (e) => setValues((prev) => ({ ...prev, [key]: e.target.value })),
697
+ placeholder: isConfigured ? "Enter new value to update" : "Enter value",
698
+ style: inputStyle
699
+ }
700
+ ),
701
+ /* @__PURE__ */ jsx2(
702
+ "button",
703
+ {
704
+ type: "button",
705
+ onClick: () => setShowValues((prev) => ({ ...prev, [key]: !isVisible })),
706
+ style: toggleBtn,
707
+ "aria-label": isVisible ? "Hide" : "Show",
708
+ children: isVisible ? "\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F" : "\u{1F441}\uFE0F"
709
+ }
710
+ )
711
+ ] })
712
+ ] }, key);
713
+ }),
714
+ feedback && /* @__PURE__ */ jsxs("div", { style: feedback.type === "success" ? feedbackSuccess : feedbackError, children: [
715
+ feedback.type === "success" ? "\u2713" : "\u26A0",
716
+ " ",
717
+ feedback.message
718
+ ] }),
719
+ /* @__PURE__ */ jsxs("div", { style: btnRow, children: [
720
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
721
+ /* @__PURE__ */ jsx2(
722
+ "button",
723
+ {
724
+ onClick: handleSave,
725
+ disabled: saving || !hasAnyValue,
726
+ style: { ...saveBtn, opacity: saving || !hasAnyValue ? 0.4 : 1 },
727
+ children: saving ? "Saving..." : "Save Keys"
728
+ }
729
+ ),
730
+ onClose && /* @__PURE__ */ jsx2("button", { onClick: onClose, style: cancelBtn, children: "Cancel" })
731
+ ] }),
732
+ onDelete && configuredKeys.length > 0 && /* @__PURE__ */ jsx2("button", { onClick: handleDelete, disabled: deleting, style: deleteBtn, children: deleting ? "..." : "\u{1F5D1} Remove" })
733
+ ] })
734
+ ] });
735
+ }
736
+
737
+ // src/components/SecretOrchestrator.tsx
738
+ import { useState as useState4, useEffect as useEffect3 } from "react";
739
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
740
+ var wrapper = {
741
+ display: "flex",
742
+ flexDirection: "column",
743
+ gap: "8px"
744
+ };
745
+ var loadingBox = {
746
+ display: "flex",
747
+ alignItems: "center",
748
+ gap: "8px",
749
+ padding: "12px",
750
+ borderRadius: "12px",
751
+ border: "1px solid rgba(245,158,11,0.2)",
752
+ backgroundColor: "rgba(31,41,55,0.8)",
753
+ fontSize: "12px",
754
+ color: "#fcd34d"
755
+ };
756
+ var sectionBox = {
757
+ borderRadius: "12px",
758
+ border: "1px solid rgba(245,158,11,0.2)",
759
+ backgroundColor: "rgba(31,41,55,0.8)",
760
+ overflow: "hidden"
761
+ };
762
+ var headerBtn = {
763
+ width: "100%",
764
+ display: "flex",
765
+ alignItems: "center",
766
+ gap: "8px",
767
+ padding: "12px 16px",
768
+ textAlign: "left",
769
+ background: "none",
770
+ border: "none",
771
+ cursor: "pointer",
772
+ color: "inherit"
773
+ };
774
+ var headerLabel = {
775
+ flex: 1,
776
+ fontSize: "12px",
777
+ fontWeight: 500,
778
+ color: "#fcd34d"
779
+ };
780
+ var bodyBox = {
781
+ padding: "0 16px 16px"
782
+ };
783
+ function SecretOrchestrator({
784
+ agentId,
785
+ mcpServers,
786
+ onAllConfigured,
787
+ className,
788
+ compact
789
+ }) {
790
+ const { sections, loading, allConfigured, refresh } = useSecrets({ agentId, mcpServers });
791
+ const [expandedId, setExpandedId] = useState4(null);
792
+ useEffect3(() => {
793
+ if (sections.length > 0 && expandedId === null) {
794
+ setExpandedId(sections[0].id);
795
+ }
796
+ }, [sections, expandedId]);
797
+ useEffect3(() => {
798
+ if (allConfigured) onAllConfigured?.();
799
+ }, [allConfigured, onAllConfigured]);
800
+ if (loading) {
801
+ return /* @__PURE__ */ jsxs2("div", { style: loadingBox, className, children: [
802
+ /* @__PURE__ */ jsx3("span", { style: { animation: "spin 1s linear infinite" }, children: "\u21BB" }),
803
+ "Checking configuration requirements..."
804
+ ] });
805
+ }
806
+ if (sections.length === 0) return null;
807
+ return /* @__PURE__ */ jsx3("div", { style: wrapper, className, children: sections.map((section) => {
808
+ const isExpanded = expandedId === section.id;
809
+ return /* @__PURE__ */ jsxs2("div", { style: sectionBox, children: [
810
+ /* @__PURE__ */ jsxs2(
811
+ "button",
812
+ {
813
+ onClick: () => setExpandedId(isExpanded ? null : section.id),
814
+ style: headerBtn,
815
+ children: [
816
+ /* @__PURE__ */ jsx3("span", { style: { color: "#f59e0b" }, children: "\u26A0" }),
817
+ /* @__PURE__ */ jsxs2("span", { style: headerLabel, children: [
818
+ section.label,
819
+ " required"
820
+ ] }),
821
+ /* @__PURE__ */ jsx3("span", { style: { color: "#6b7280", fontSize: "12px" }, children: isExpanded ? "\u25BC" : "\u25B6" })
822
+ ]
823
+ }
824
+ ),
825
+ isExpanded && /* @__PURE__ */ jsx3("div", { style: bodyBox, children: /* @__PURE__ */ jsx3(
826
+ SecretForm,
827
+ {
828
+ secretKeys: section.keys,
829
+ configuredKeys: section.configuredKeys,
830
+ onSave: async (secrets) => {
831
+ await section.save(secrets);
832
+ refresh();
833
+ },
834
+ onClose: compact ? void 0 : () => setExpandedId(null)
835
+ }
836
+ ) })
837
+ ] }, section.id);
838
+ }) });
839
+ }
840
+
841
+ // src/components/WalletChip.tsx
842
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
843
+ var chipStyle = {
844
+ display: "inline-flex",
845
+ alignItems: "center",
846
+ gap: "6px",
847
+ padding: "4px 10px",
848
+ borderRadius: "9999px",
849
+ backgroundColor: "rgba(255,255,255,0.08)",
850
+ border: "1px solid rgba(255,255,255,0.1)",
851
+ fontSize: "12px",
852
+ fontFamily: "monospace",
853
+ color: "#d1d5db",
854
+ cursor: "default",
855
+ transition: "background-color 0.15s"
856
+ };
857
+ var chipClickable = {
858
+ ...chipStyle,
859
+ cursor: "pointer"
860
+ };
861
+ var balanceBadge = {
862
+ padding: "1px 6px",
863
+ borderRadius: "9999px",
864
+ backgroundColor: "rgba(99,102,241,0.2)",
865
+ color: "#a5b4fc",
866
+ fontSize: "11px",
867
+ fontFamily: "sans-serif",
868
+ fontWeight: 500
869
+ };
870
+ function truncateAddress(address) {
871
+ if (address.length <= 10) return address;
872
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
873
+ }
874
+ function WalletChip({ address, balanceDisplay, displayName, onPress, className }) {
875
+ const Tag = onPress ? "button" : "div";
876
+ const style = onPress ? chipClickable : chipStyle;
877
+ return /* @__PURE__ */ jsxs3(
878
+ Tag,
879
+ {
880
+ onClick: onPress,
881
+ style: { ...style, ...onPress ? { border: "1px solid rgba(255,255,255,0.1)", background: "rgba(255,255,255,0.08)" } : {} },
882
+ className,
883
+ ...Tag === "button" ? { type: "button" } : {},
884
+ children: [
885
+ /* @__PURE__ */ jsx4("span", { children: displayName || truncateAddress(address) }),
886
+ balanceDisplay && /* @__PURE__ */ jsx4("span", { style: balanceBadge, children: balanceDisplay })
887
+ ]
888
+ }
889
+ );
890
+ }
891
+ export {
892
+ RickyDataProvider,
893
+ SecretForm,
894
+ SecretOrchestrator,
895
+ WalletChip,
896
+ agentKeys,
897
+ apiKeyKeys,
898
+ balanceKeys,
899
+ sessionKeys,
900
+ useAgent,
901
+ useAgentChat,
902
+ useAgents,
903
+ useApiKeyStatus,
904
+ useDeleteApiKey,
905
+ useDeleteSession,
906
+ useOpenAIApiKeyStatus,
907
+ useRickyData,
908
+ useSecrets,
909
+ useSession,
910
+ useSessions,
911
+ useSetApiKey,
912
+ useSetOpenAIApiKey,
913
+ useWalletBalance,
914
+ useWalletSettings,
915
+ useWalletTransactions,
916
+ walletSettingsKeys
917
+ };