botschat 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.
Files changed (88) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +213 -0
  3. package/migrations/0001_initial.sql +88 -0
  4. package/migrations/0002_rename_projects_to_channels.sql +53 -0
  5. package/migrations/0003_messages.sql +14 -0
  6. package/migrations/0004_jobs.sql +15 -0
  7. package/migrations/0005_deleted_cron_jobs.sql +6 -0
  8. package/migrations/0006_tasks_add_model.sql +2 -0
  9. package/migrations/0007_sessions.sql +25 -0
  10. package/migrations/0008_remove_openclaw_fields.sql +8 -0
  11. package/package.json +53 -0
  12. package/packages/api/package.json +17 -0
  13. package/packages/api/src/do/connection-do.ts +929 -0
  14. package/packages/api/src/env.ts +8 -0
  15. package/packages/api/src/index.ts +297 -0
  16. package/packages/api/src/routes/agents.ts +68 -0
  17. package/packages/api/src/routes/auth.ts +105 -0
  18. package/packages/api/src/routes/channels.ts +185 -0
  19. package/packages/api/src/routes/jobs.ts +65 -0
  20. package/packages/api/src/routes/models.ts +22 -0
  21. package/packages/api/src/routes/pairing.ts +76 -0
  22. package/packages/api/src/routes/projects.ts +177 -0
  23. package/packages/api/src/routes/sessions.ts +171 -0
  24. package/packages/api/src/routes/tasks.ts +375 -0
  25. package/packages/api/src/routes/upload.ts +52 -0
  26. package/packages/api/src/utils/auth.ts +101 -0
  27. package/packages/api/src/utils/id.ts +19 -0
  28. package/packages/api/tsconfig.json +18 -0
  29. package/packages/plugin/dist/index.d.ts +19 -0
  30. package/packages/plugin/dist/index.d.ts.map +1 -0
  31. package/packages/plugin/dist/index.js +17 -0
  32. package/packages/plugin/dist/index.js.map +1 -0
  33. package/packages/plugin/dist/src/accounts.d.ts +12 -0
  34. package/packages/plugin/dist/src/accounts.d.ts.map +1 -0
  35. package/packages/plugin/dist/src/accounts.js +103 -0
  36. package/packages/plugin/dist/src/accounts.js.map +1 -0
  37. package/packages/plugin/dist/src/channel.d.ts +206 -0
  38. package/packages/plugin/dist/src/channel.d.ts.map +1 -0
  39. package/packages/plugin/dist/src/channel.js +1248 -0
  40. package/packages/plugin/dist/src/channel.js.map +1 -0
  41. package/packages/plugin/dist/src/runtime.d.ts +3 -0
  42. package/packages/plugin/dist/src/runtime.d.ts.map +1 -0
  43. package/packages/plugin/dist/src/runtime.js +18 -0
  44. package/packages/plugin/dist/src/runtime.js.map +1 -0
  45. package/packages/plugin/dist/src/types.d.ts +179 -0
  46. package/packages/plugin/dist/src/types.d.ts.map +1 -0
  47. package/packages/plugin/dist/src/types.js +6 -0
  48. package/packages/plugin/dist/src/types.js.map +1 -0
  49. package/packages/plugin/dist/src/ws-client.d.ts +51 -0
  50. package/packages/plugin/dist/src/ws-client.d.ts.map +1 -0
  51. package/packages/plugin/dist/src/ws-client.js +170 -0
  52. package/packages/plugin/dist/src/ws-client.js.map +1 -0
  53. package/packages/plugin/openclaw.plugin.json +11 -0
  54. package/packages/plugin/package.json +39 -0
  55. package/packages/plugin/tsconfig.json +20 -0
  56. package/packages/web/dist/assets/index-C-wI8eHy.css +1 -0
  57. package/packages/web/dist/assets/index-CbPEKHLG.js +93 -0
  58. package/packages/web/dist/index.html +17 -0
  59. package/packages/web/index.html +16 -0
  60. package/packages/web/package.json +29 -0
  61. package/packages/web/postcss.config.js +6 -0
  62. package/packages/web/src/App.tsx +827 -0
  63. package/packages/web/src/api.ts +242 -0
  64. package/packages/web/src/components/ChatWindow.tsx +864 -0
  65. package/packages/web/src/components/CronDetail.tsx +943 -0
  66. package/packages/web/src/components/CronSidebar.tsx +123 -0
  67. package/packages/web/src/components/DebugLogPanel.tsx +258 -0
  68. package/packages/web/src/components/IconRail.tsx +163 -0
  69. package/packages/web/src/components/JobList.tsx +120 -0
  70. package/packages/web/src/components/LoginPage.tsx +178 -0
  71. package/packages/web/src/components/MessageContent.tsx +1082 -0
  72. package/packages/web/src/components/ModelSelect.tsx +87 -0
  73. package/packages/web/src/components/ScheduleEditor.tsx +403 -0
  74. package/packages/web/src/components/SessionTabs.tsx +246 -0
  75. package/packages/web/src/components/Sidebar.tsx +331 -0
  76. package/packages/web/src/components/TaskBar.tsx +413 -0
  77. package/packages/web/src/components/ThreadPanel.tsx +212 -0
  78. package/packages/web/src/debug-log.ts +58 -0
  79. package/packages/web/src/index.css +170 -0
  80. package/packages/web/src/main.tsx +10 -0
  81. package/packages/web/src/store.ts +492 -0
  82. package/packages/web/src/ws.ts +99 -0
  83. package/packages/web/tailwind.config.js +65 -0
  84. package/packages/web/tsconfig.json +18 -0
  85. package/packages/web/vite.config.ts +20 -0
  86. package/scripts/dev.sh +122 -0
  87. package/tsconfig.json +18 -0
  88. package/wrangler.toml +40 -0
@@ -0,0 +1,242 @@
1
+ /** Lightweight API client for the BotsChat Workers API. */
2
+
3
+ import { dlog } from "./debug-log";
4
+
5
+ const API_BASE = "/api";
6
+
7
+ let _token: string | null = localStorage.getItem("botschat_token");
8
+
9
+ export function setToken(token: string | null) {
10
+ _token = token;
11
+ if (token) localStorage.setItem("botschat_token", token);
12
+ else localStorage.removeItem("botschat_token");
13
+ }
14
+
15
+ export function getToken(): string | null {
16
+ return _token;
17
+ }
18
+
19
+ async function request<T>(
20
+ method: string,
21
+ path: string,
22
+ body?: unknown,
23
+ ): Promise<T> {
24
+ const tag = `${method} ${path}`;
25
+ dlog.api("API", `→ ${tag}`, body);
26
+
27
+ const headers: Record<string, string> = {
28
+ "Content-Type": "application/json",
29
+ };
30
+ if (_token) headers["Authorization"] = `Bearer ${_token}`;
31
+
32
+ const t0 = performance.now();
33
+ let res: Response;
34
+ try {
35
+ res = await fetch(`${API_BASE}${path}`, {
36
+ method,
37
+ headers,
38
+ body: body ? JSON.stringify(body) : undefined,
39
+ cache: "no-store",
40
+ });
41
+ } catch (err) {
42
+ const ms = Math.round(performance.now() - t0);
43
+ dlog.error("API", `✗ ${tag} — network error (${ms}ms)`, String(err));
44
+ throw err;
45
+ }
46
+
47
+ const ms = Math.round(performance.now() - t0);
48
+
49
+ if (!res.ok) {
50
+ const err = await res.json().catch(() => ({ error: res.statusText }));
51
+ const message = (err as { error?: string }).error ?? `HTTP ${res.status}`;
52
+ dlog.error("API", `✗ ${tag} — ${res.status} (${ms}ms): ${message}`);
53
+ throw new Error(message);
54
+ }
55
+
56
+ const data = await res.json() as T;
57
+ dlog.api("API", `✓ ${tag} — ${res.status} (${ms}ms)`, data);
58
+ return data;
59
+ }
60
+
61
+ // ---- Auth ----
62
+ export type AuthResponse = { id: string; email: string; token: string; displayName?: string };
63
+
64
+ export type UserSettings = { defaultModel?: string };
65
+
66
+ export const authApi = {
67
+ register: (email: string, password: string, displayName?: string) =>
68
+ request<AuthResponse>("POST", "/auth/register", { email, password, displayName }),
69
+ login: (email: string, password: string) =>
70
+ request<AuthResponse>("POST", "/auth/login", { email, password }),
71
+ me: () => request<{ id: string; email: string; displayName: string | null; settings: UserSettings }>("GET", "/me"),
72
+ };
73
+
74
+ // ---- User settings ----
75
+ export const meApi = {
76
+ updateSettings: (data: { defaultModel?: string }) =>
77
+ request<{ ok: boolean; settings: UserSettings }>("PATCH", "/me", data),
78
+ };
79
+
80
+ // ---- Models ----
81
+ export type ModelInfo = {
82
+ id: string;
83
+ name: string;
84
+ provider: string;
85
+ };
86
+
87
+ export const modelsApi = {
88
+ list: () => request<{ models: ModelInfo[] }>("GET", "/models"),
89
+ };
90
+
91
+ // ---- Agents (OpenClaw-aligned: first level = Agent, then Session) ----
92
+ export type Agent = {
93
+ id: string;
94
+ name: string;
95
+ sessionKey: string;
96
+ isDefault: boolean;
97
+ channelId: string | null;
98
+ };
99
+
100
+ export const agentsApi = {
101
+ list: () => request<{ agents: Agent[] }>("GET", "/agents"),
102
+ };
103
+
104
+ // ---- Channels ----
105
+ export type Channel = {
106
+ id: string;
107
+ name: string;
108
+ description: string;
109
+ openclawAgentId: string;
110
+ systemPrompt: string;
111
+ createdAt: number;
112
+ updatedAt: number;
113
+ };
114
+
115
+ export const channelsApi = {
116
+ list: () => request<{ channels: Channel[] }>("GET", "/channels"),
117
+ get: (id: string) => request<Channel>("GET", `/channels/${id}`),
118
+ create: (data: { name: string; description?: string; systemPrompt?: string; openclawAgentId?: string }) =>
119
+ request<Channel>("POST", "/channels", data),
120
+ update: (id: string, data: Partial<Pick<Channel, "name" | "description" | "systemPrompt">>) =>
121
+ request<{ ok: boolean }>("PATCH", `/channels/${id}`, data),
122
+ delete: (id: string) => request<{ ok: boolean }>("DELETE", `/channels/${id}`),
123
+ };
124
+
125
+ // ---- Sessions ----
126
+ export type Session = {
127
+ id: string;
128
+ name: string;
129
+ sessionKey: string;
130
+ createdAt: number;
131
+ updatedAt: number;
132
+ };
133
+
134
+ export const sessionsApi = {
135
+ list: (channelId: string) =>
136
+ request<{ sessions: Session[] }>("GET", `/channels/${channelId}/sessions`),
137
+ create: (channelId: string, name?: string) =>
138
+ request<Session>("POST", `/channels/${channelId}/sessions`, { name }),
139
+ rename: (channelId: string, sessionId: string, name: string) =>
140
+ request<{ ok: boolean }>("PATCH", `/channels/${channelId}/sessions/${sessionId}`, { name }),
141
+ delete: (channelId: string, sessionId: string) =>
142
+ request<{ ok: boolean }>("DELETE", `/channels/${channelId}/sessions/${sessionId}`),
143
+ };
144
+
145
+ // ---- Tasks ----
146
+ export type Task = {
147
+ id: string;
148
+ name: string;
149
+ kind: "background" | "adhoc";
150
+ openclawCronJobId: string | null;
151
+ schedule: string | null;
152
+ instructions: string | null;
153
+ model: string | null;
154
+ sessionKey: string | null;
155
+ enabled: boolean;
156
+ createdAt: number;
157
+ updatedAt: number;
158
+ };
159
+
160
+ export type TaskWithChannel = Task & { channelId: string };
161
+
162
+ export type TaskScanEntry = {
163
+ cronJobId: string;
164
+ schedule: string;
165
+ instructions: string;
166
+ model: string;
167
+ enabled: boolean;
168
+ };
169
+
170
+ export const tasksApi = {
171
+ list: (channelId: string) =>
172
+ request<{ tasks: Task[] }>("GET", `/channels/${channelId}/tasks`),
173
+ listAll: (kind: "background" | "adhoc" = "background") =>
174
+ request<{ tasks: TaskWithChannel[] }>("GET", `/tasks?kind=${kind}`),
175
+ /** Fetch OpenClaw-owned fields (schedule/instructions/model) cached in the DO. */
176
+ scanData: () =>
177
+ request<{ tasks: TaskScanEntry[] }>("GET", "/task-scan"),
178
+ create: (channelId: string, data: { name: string; kind: "background" | "adhoc"; schedule?: string; instructions?: string }) =>
179
+ request<Task>("POST", `/channels/${channelId}/tasks`, data),
180
+ update: (channelId: string, taskId: string, data: Partial<Pick<Task, "name" | "schedule" | "instructions" | "model" | "enabled">>) =>
181
+ request<{ ok: boolean }>("PATCH", `/channels/${channelId}/tasks/${taskId}`, data),
182
+ delete: (channelId: string, taskId: string) =>
183
+ request<{ ok: boolean }>("DELETE", `/channels/${channelId}/tasks/${taskId}`),
184
+ run: (channelId: string, taskId: string) =>
185
+ request<{ ok: boolean; message: string }>("POST", `/channels/${channelId}/tasks/${taskId}/run`),
186
+ };
187
+
188
+ // ---- Jobs (background task execution history) ----
189
+ export type Job = {
190
+ id: string;
191
+ number: number;
192
+ sessionKey: string;
193
+ status: "running" | "ok" | "error" | "skipped";
194
+ startedAt: number;
195
+ finishedAt: number | null;
196
+ durationMs: number | null;
197
+ summary: string;
198
+ time: string;
199
+ };
200
+
201
+ export const jobsApi = {
202
+ list: (channelId: string, taskId: string) =>
203
+ request<{ jobs: Job[] }>("GET", `/channels/${channelId}/tasks/${taskId}/jobs`),
204
+ listByTask: (taskId: string) =>
205
+ request<{ jobs: Job[] }>("GET", `/tasks/${taskId}/jobs`),
206
+ };
207
+
208
+ // ---- Messages ----
209
+ export type MessageRecord = {
210
+ id: string;
211
+ sender: "user" | "agent";
212
+ text: string;
213
+ timestamp: number;
214
+ mediaUrl?: string;
215
+ a2ui?: string;
216
+ threadId?: string;
217
+ };
218
+
219
+ export const messagesApi = {
220
+ list: (userId: string, sessionKey: string, threadId?: string) =>
221
+ request<{ messages: MessageRecord[]; replyCounts?: Record<string, number> }>(
222
+ "GET",
223
+ `/messages/${userId}?sessionKey=${encodeURIComponent(sessionKey)}${threadId ? `&threadId=${encodeURIComponent(threadId)}` : ""}`,
224
+ ),
225
+ };
226
+
227
+ // ---- Pairing Tokens ----
228
+ export type PairingToken = {
229
+ id: string;
230
+ token: string;
231
+ tokenPreview: string;
232
+ label: string | null;
233
+ lastConnectedAt: number | null;
234
+ createdAt: number;
235
+ };
236
+
237
+ export const pairingApi = {
238
+ list: () => request<{ tokens: PairingToken[] }>("GET", "/pairing-tokens"),
239
+ create: (label?: string) =>
240
+ request<{ id: string; token: string; label: string | null }>("POST", "/pairing-tokens", { label }),
241
+ delete: (id: string) => request<{ ok: boolean }>("DELETE", `/pairing-tokens/${id}`),
242
+ };