botschat 0.1.8 → 0.1.9
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/package.json +1 -1
- package/packages/api/src/do/connection-do.ts +4 -3
- package/packages/api/src/routes/upload.ts +2 -1
- package/packages/api/src/utils/uuid.ts +17 -0
- package/packages/plugin/dist/src/channel.js +1 -1
- package/packages/plugin/dist/src/channel.js.map +1 -1
- package/packages/plugin/dist/src/e2e-crypto.d.ts +47 -0
- package/packages/plugin/dist/src/e2e-crypto.d.ts.map +1 -0
- package/packages/plugin/dist/src/e2e-crypto.js +210 -0
- package/packages/plugin/dist/src/e2e-crypto.js.map +1 -0
- package/packages/plugin/dist/src/ws-client.js +1 -1
- package/packages/plugin/dist/src/ws-client.js.map +1 -1
- package/packages/plugin/package.json +2 -3
- package/packages/web/dist/assets/{index-C-FpELeN.js → index-BcHAQzqW.js} +132 -132
- package/packages/web/dist/index.html +1 -1
- package/packages/web/src/App.tsx +5 -4
- package/packages/web/src/components/ChatWindow.tsx +5 -5
- package/packages/web/src/components/LoginPage.tsx +3 -0
- package/packages/web/src/components/MobileLayout.tsx +29 -2
- package/packages/web/src/components/Sidebar.tsx +14 -1
- package/packages/web/src/components/ThreadPanel.tsx +2 -1
- package/packages/web/src/utils/uuid.ts +21 -0
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&family=Noto+Sans+SC:wght@400;700&display=swap" rel="stylesheet" />
|
|
29
29
|
|
|
30
30
|
<title>BotsChat</title>
|
|
31
|
-
<script type="module" crossorigin src="/assets/index-
|
|
31
|
+
<script type="module" crossorigin src="/assets/index-BcHAQzqW.js"></script>
|
|
32
32
|
<link rel="stylesheet" crossorigin href="/assets/index-B1sFqYiM.css">
|
|
33
33
|
</head>
|
|
34
34
|
<body>
|
package/packages/web/src/App.tsx
CHANGED
|
@@ -30,6 +30,7 @@ import { MobileLayout } from "./components/MobileLayout";
|
|
|
30
30
|
import { dlog } from "./debug-log";
|
|
31
31
|
import { E2eService } from "./e2e";
|
|
32
32
|
import { gtagPageView } from "./analytics";
|
|
33
|
+
import { randomUUID } from "./utils/uuid";
|
|
33
34
|
|
|
34
35
|
export default function App() {
|
|
35
36
|
const [state, dispatch] = useReducer(appReducer, initialState, (init): AppState => {
|
|
@@ -506,7 +507,7 @@ export default function App() {
|
|
|
506
507
|
// from the server when the user navigates to that session.
|
|
507
508
|
if (!isCurrentSession(sessionKey)) break;
|
|
508
509
|
const chatMsg: ChatMessage = {
|
|
509
|
-
id:
|
|
510
|
+
id: randomUUID(),
|
|
510
511
|
sender: "agent",
|
|
511
512
|
text: msg.text as string,
|
|
512
513
|
timestamp: Date.now(),
|
|
@@ -523,7 +524,7 @@ export default function App() {
|
|
|
523
524
|
case "agent.media": {
|
|
524
525
|
if (!isCurrentSession(sessionKey)) break;
|
|
525
526
|
const mediaMsg: ChatMessage = {
|
|
526
|
-
id:
|
|
527
|
+
id: randomUUID(),
|
|
527
528
|
sender: "agent",
|
|
528
529
|
text: (msg.caption as string) ?? "",
|
|
529
530
|
mediaUrl: msg.mediaUrl as string,
|
|
@@ -541,7 +542,7 @@ export default function App() {
|
|
|
541
542
|
case "agent.a2ui": {
|
|
542
543
|
if (!isCurrentSession(sessionKey)) break;
|
|
543
544
|
const a2uiMsg: ChatMessage = {
|
|
544
|
-
id:
|
|
545
|
+
id: randomUUID(),
|
|
545
546
|
sender: "agent",
|
|
546
547
|
text: "",
|
|
547
548
|
a2ui: msg.jsonl as string,
|
|
@@ -700,7 +701,7 @@ export default function App() {
|
|
|
700
701
|
const token = getToken();
|
|
701
702
|
if (!token) return;
|
|
702
703
|
|
|
703
|
-
const sessionId =
|
|
704
|
+
const sessionId = randomUUID();
|
|
704
705
|
dlog.info("WS", `Connecting WebSocket (session=${sessionId.slice(0, 8)}...)`);
|
|
705
706
|
const client = new BotsChatWSClient({
|
|
706
707
|
userId: state.user.id,
|
|
@@ -5,6 +5,7 @@ import { MessageContent } from "./MessageContent";
|
|
|
5
5
|
import { ModelSelect } from "./ModelSelect";
|
|
6
6
|
import { SessionTabs } from "./SessionTabs";
|
|
7
7
|
import { dlog } from "../debug-log";
|
|
8
|
+
import { randomUUID } from "../utils/uuid";
|
|
8
9
|
|
|
9
10
|
type ChatWindowProps = {
|
|
10
11
|
sendMessage: (msg: WSMessage) => void;
|
|
@@ -229,7 +230,7 @@ export function ChatWindow({ sendMessage }: ChatWindowProps) {
|
|
|
229
230
|
setSkillVersion((v) => v + 1);
|
|
230
231
|
|
|
231
232
|
const msg: ChatMessage = {
|
|
232
|
-
id:
|
|
233
|
+
id: randomUUID(),
|
|
233
234
|
sender: "user",
|
|
234
235
|
text: `/model ${modelId}`,
|
|
235
236
|
timestamp: Date.now(),
|
|
@@ -381,7 +382,7 @@ export function ChatWindow({ sendMessage }: ChatWindowProps) {
|
|
|
381
382
|
}
|
|
382
383
|
|
|
383
384
|
const msg: ChatMessage = {
|
|
384
|
-
id:
|
|
385
|
+
id: randomUUID(),
|
|
385
386
|
sender: "user",
|
|
386
387
|
text: trimmed,
|
|
387
388
|
timestamp: Date.now(),
|
|
@@ -416,7 +417,7 @@ export function ChatWindow({ sendMessage }: ChatWindowProps) {
|
|
|
416
417
|
if (!sessionKey) return;
|
|
417
418
|
dlog.info("A2UI", `Action triggered: ${action}`);
|
|
418
419
|
const msg: ChatMessage = {
|
|
419
|
-
id:
|
|
420
|
+
id: randomUUID(),
|
|
420
421
|
sender: "user",
|
|
421
422
|
text: action,
|
|
422
423
|
timestamp: Date.now(),
|
|
@@ -448,7 +449,7 @@ export function ChatWindow({ sendMessage }: ChatWindowProps) {
|
|
|
448
449
|
// Send the chosen label as a user message (show the readable label, not the
|
|
449
450
|
// technical value, so the chat history reads naturally)
|
|
450
451
|
const msg: ChatMessage = {
|
|
451
|
-
id:
|
|
452
|
+
id: randomUUID(),
|
|
452
453
|
sender: "user",
|
|
453
454
|
text: label,
|
|
454
455
|
timestamp: Date.now(),
|
|
@@ -861,4 +862,3 @@ function ActionButton({
|
|
|
861
862
|
</button>
|
|
862
863
|
);
|
|
863
864
|
}
|
|
864
|
-
|
|
@@ -77,6 +77,9 @@ export function LoginPage() {
|
|
|
77
77
|
}
|
|
78
78
|
dlog.info("Auth", `${isRegister ? "Register" : "Login"} success — user ${res.id} (${res.email})`);
|
|
79
79
|
handleAuthSuccess(res);
|
|
80
|
+
if (isRegister) {
|
|
81
|
+
localStorage.setItem("botschat_onboarding_dismissed", "1");
|
|
82
|
+
}
|
|
80
83
|
} catch (err) {
|
|
81
84
|
const message = err instanceof Error ? err.message : "Something went wrong";
|
|
82
85
|
dlog.error("Auth", `${isRegister ? "Register" : "Login"} failed: ${message}`);
|
|
@@ -10,6 +10,7 @@ import { CronSidebar } from "./CronSidebar";
|
|
|
10
10
|
import { CronDetail } from "./CronDetail";
|
|
11
11
|
import { ModelSelect } from "./ModelSelect";
|
|
12
12
|
import { ConnectionSettings } from "./ConnectionSettings";
|
|
13
|
+
import { E2ESettings } from "./E2ESettings";
|
|
13
14
|
import { dlog } from "../debug-log";
|
|
14
15
|
|
|
15
16
|
type MobileScreen =
|
|
@@ -258,6 +259,17 @@ export function MobileLayout({
|
|
|
258
259
|
)}
|
|
259
260
|
{theme === "dark" ? "Light Mode" : "Dark Mode"}
|
|
260
261
|
</button>
|
|
262
|
+
<button
|
|
263
|
+
className="w-full text-left px-4 py-2.5 text-body flex items-center gap-2.5"
|
|
264
|
+
style={{ color: "var(--text-primary)" }}
|
|
265
|
+
onClick={() => { onOpenSettings(); setShowUserMenu(false); }}
|
|
266
|
+
>
|
|
267
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
268
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
|
269
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
270
|
+
</svg>
|
|
271
|
+
Settings
|
|
272
|
+
</button>
|
|
261
273
|
<div style={{ borderTop: "1px solid var(--border)" }} />
|
|
262
274
|
<button
|
|
263
275
|
className="w-full text-left px-4 py-2.5 text-body flex items-center gap-2.5"
|
|
@@ -277,7 +289,7 @@ export function MobileLayout({
|
|
|
277
289
|
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
278
290
|
{screen === "channel-list" && (
|
|
279
291
|
<div className="flex-1 min-h-0 overflow-y-auto" style={{ background: "var(--bg-secondary)" }}>
|
|
280
|
-
<Sidebar />
|
|
292
|
+
<Sidebar onOpenSettings={onOpenSettings} />
|
|
281
293
|
</div>
|
|
282
294
|
)}
|
|
283
295
|
|
|
@@ -360,7 +372,7 @@ function MobileSettingsModal({
|
|
|
360
372
|
onClose: () => void;
|
|
361
373
|
handleDefaultModelChange: (modelId: string) => Promise<void>;
|
|
362
374
|
}) {
|
|
363
|
-
const [tab, setTab] = useState<"general" | "connection">("general");
|
|
375
|
+
const [tab, setTab] = useState<"general" | "connection" | "security">("general");
|
|
364
376
|
|
|
365
377
|
return (
|
|
366
378
|
<div
|
|
@@ -405,6 +417,17 @@ function MobileSettingsModal({
|
|
|
405
417
|
>
|
|
406
418
|
Connection
|
|
407
419
|
</button>
|
|
420
|
+
<button
|
|
421
|
+
className="pb-2 text-caption font-bold transition-colors"
|
|
422
|
+
style={{
|
|
423
|
+
color: tab === "security" ? "var(--text-primary)" : "var(--text-muted)",
|
|
424
|
+
borderBottom: tab === "security" ? "2px solid var(--bg-active)" : "2px solid transparent",
|
|
425
|
+
marginBottom: "-1px",
|
|
426
|
+
}}
|
|
427
|
+
onClick={() => setTab("security")}
|
|
428
|
+
>
|
|
429
|
+
Security
|
|
430
|
+
</button>
|
|
408
431
|
</div>
|
|
409
432
|
|
|
410
433
|
{/* Tab content — scrollable */}
|
|
@@ -440,6 +463,10 @@ function MobileSettingsModal({
|
|
|
440
463
|
{tab === "connection" && (
|
|
441
464
|
<ConnectionSettings />
|
|
442
465
|
)}
|
|
466
|
+
|
|
467
|
+
{tab === "security" && (
|
|
468
|
+
<E2ESettings />
|
|
469
|
+
)}
|
|
443
470
|
</div>
|
|
444
471
|
|
|
445
472
|
<button
|
|
@@ -3,7 +3,7 @@ import { useAppState, useAppDispatch } from "../store";
|
|
|
3
3
|
import { agentsApi, channelsApi } from "../api";
|
|
4
4
|
import { dlog } from "../debug-log";
|
|
5
5
|
|
|
6
|
-
export function Sidebar() {
|
|
6
|
+
export function Sidebar({ onOpenSettings }: { onOpenSettings?: () => void } = {}) {
|
|
7
7
|
const state = useAppState();
|
|
8
8
|
const dispatch = useAppDispatch();
|
|
9
9
|
const [showCreate, setShowCreate] = useState(false);
|
|
@@ -88,6 +88,19 @@ export function Sidebar() {
|
|
|
88
88
|
<span className="text-[--text-sidebar-active] font-bold text-h2 truncate flex-1">
|
|
89
89
|
BotsChat
|
|
90
90
|
</span>
|
|
91
|
+
{onOpenSettings && (
|
|
92
|
+
<button
|
|
93
|
+
onClick={onOpenSettings}
|
|
94
|
+
className="p-1 rounded transition-colors hover:bg-[--sidebar-hover]"
|
|
95
|
+
style={{ color: "var(--text-sidebar)" }}
|
|
96
|
+
title="Settings"
|
|
97
|
+
>
|
|
98
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
|
99
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
|
100
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
101
|
+
</svg>
|
|
102
|
+
</button>
|
|
103
|
+
)}
|
|
91
104
|
<svg className="w-3 h-3 text-[--text-sidebar]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
|
92
105
|
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
|
93
106
|
</svg>
|
|
@@ -4,6 +4,7 @@ import { messagesApi } from "../api";
|
|
|
4
4
|
import type { WSMessage } from "../ws";
|
|
5
5
|
import { MessageContent } from "./MessageContent";
|
|
6
6
|
import { dlog } from "../debug-log";
|
|
7
|
+
import { randomUUID } from "../utils/uuid";
|
|
7
8
|
|
|
8
9
|
type ThreadPanelProps = {
|
|
9
10
|
sendMessage: (msg: WSMessage) => void;
|
|
@@ -47,7 +48,7 @@ export function ThreadPanel({ sendMessage }: ThreadPanelProps) {
|
|
|
47
48
|
dlog.info("Thread", `Send reply: ${trimmed.length > 120 ? trimmed.slice(0, 120) + "…" : trimmed}`, { threadId: state.activeThreadId });
|
|
48
49
|
|
|
49
50
|
const msg: ChatMessage = {
|
|
50
|
-
id:
|
|
51
|
+
id: randomUUID(),
|
|
51
52
|
sender: "user",
|
|
52
53
|
text: trimmed,
|
|
53
54
|
timestamp: Date.now(),
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a UUID v4. Uses crypto.randomUUID() when available (HTTPS),
|
|
3
|
+
* otherwise falls back to crypto.getRandomValues() for HTTP/insecure contexts
|
|
4
|
+
* where randomUUID is not defined.
|
|
5
|
+
*/
|
|
6
|
+
export function randomUUID(): string {
|
|
7
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
8
|
+
return crypto.randomUUID();
|
|
9
|
+
}
|
|
10
|
+
// Fallback for insecure context (e.g. http://0.0.0.0:8787): UUID v4 via getRandomValues
|
|
11
|
+
const bytes = new Uint8Array(16);
|
|
12
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
13
|
+
crypto.getRandomValues(bytes);
|
|
14
|
+
} else {
|
|
15
|
+
for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);
|
|
16
|
+
}
|
|
17
|
+
bytes[6] = (bytes[6]! & 0x0f) | 0x40;
|
|
18
|
+
bytes[8] = (bytes[8]! & 0x3f) | 0x80;
|
|
19
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
20
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
21
|
+
}
|