@chrysb/alphaclaw 0.3.3 → 0.3.4

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 (31) hide show
  1. package/bin/alphaclaw.js +18 -0
  2. package/lib/plugin/usage-tracker/index.js +308 -0
  3. package/lib/plugin/usage-tracker/openclaw.plugin.json +8 -0
  4. package/lib/public/css/explorer.css +51 -1
  5. package/lib/public/css/shell.css +3 -1
  6. package/lib/public/css/theme.css +35 -0
  7. package/lib/public/js/app.js +73 -24
  8. package/lib/public/js/components/file-tree.js +231 -28
  9. package/lib/public/js/components/file-viewer.js +193 -20
  10. package/lib/public/js/components/segmented-control.js +33 -0
  11. package/lib/public/js/components/sidebar.js +14 -32
  12. package/lib/public/js/components/telegram-workspace/index.js +353 -0
  13. package/lib/public/js/components/telegram-workspace/manage.js +397 -0
  14. package/lib/public/js/components/telegram-workspace/onboarding.js +616 -0
  15. package/lib/public/js/components/usage-tab.js +528 -0
  16. package/lib/public/js/components/watchdog-tab.js +1 -1
  17. package/lib/public/js/lib/api.js +25 -1
  18. package/lib/public/js/lib/telegram-api.js +78 -0
  19. package/lib/public/js/lib/ui-settings.js +38 -0
  20. package/lib/public/setup.html +34 -30
  21. package/lib/server/alphaclaw-version.js +3 -3
  22. package/lib/server/constants.js +1 -0
  23. package/lib/server/onboarding/openclaw.js +15 -0
  24. package/lib/server/routes/auth.js +5 -1
  25. package/lib/server/routes/telegram.js +185 -60
  26. package/lib/server/routes/usage.js +133 -0
  27. package/lib/server/usage-db.js +570 -0
  28. package/lib/server.js +21 -1
  29. package/lib/setup/core-prompts/AGENTS.md +0 -101
  30. package/package.json +1 -1
  31. package/lib/public/js/components/telegram-workspace.js +0 -1365
@@ -0,0 +1,616 @@
1
+ import { h } from "https://esm.sh/preact";
2
+ import { useState, useEffect } from "https://esm.sh/preact/hooks";
3
+ import htm from "https://esm.sh/htm";
4
+ import { Badge } from "../badge.js";
5
+ import { showToast } from "../toast.js";
6
+ import { ActionButton } from "../action-button.js";
7
+ import { ConfirmDialog } from "../confirm-dialog.js";
8
+ import * as api from "../../lib/telegram-api.js";
9
+
10
+ const html = htm.bind(h);
11
+
12
+ export const StepIndicator = ({ currentStep, steps }) => html`
13
+ <div class="flex items-center gap-1 mb-6">
14
+ ${steps.map(
15
+ (s, i) => html`
16
+ <div
17
+ class="h-1 flex-1 rounded-full transition-colors ${i <= currentStep
18
+ ? "bg-accent"
19
+ : "bg-border"}"
20
+ style=${i <= currentStep ? "background: var(--accent)" : ""}
21
+ />
22
+ `,
23
+ )}
24
+ </div>
25
+ `;
26
+
27
+ // Step 1: Verify Bot
28
+ export const VerifyBotStep = ({ botInfo, setBotInfo, onNext }) => {
29
+ const [loading, setLoading] = useState(false);
30
+ const [error, setError] = useState(null);
31
+
32
+ const verify = async () => {
33
+ setLoading(true);
34
+ setError(null);
35
+ try {
36
+ const data = await api.verifyBot();
37
+ if (!data.ok) throw new Error(data.error);
38
+ setBotInfo(data.bot);
39
+ } catch (e) {
40
+ setError(e.message);
41
+ }
42
+ setLoading(false);
43
+ };
44
+
45
+ useEffect(() => {
46
+ if (!botInfo) verify();
47
+ }, []);
48
+
49
+ return html`
50
+ <div class="space-y-4">
51
+ <h3 class="text-sm font-semibold">Verify Bot Setup</h3>
52
+
53
+ ${botInfo &&
54
+ html`
55
+ <div class="bg-black/20 border border-border rounded-lg p-3">
56
+ <div class="flex items-center gap-2">
57
+ <span class="text-sm text-gray-300 font-medium">@${botInfo.username}</span>
58
+ <${Badge} tone="success">Connected</${Badge}>
59
+ </div>
60
+ <p class="text-xs text-gray-500 mt-1">${botInfo.first_name}</p>
61
+ </div>
62
+ `}
63
+ ${error &&
64
+ html`
65
+ <div class="bg-red-500/10 border border-red-500/20 rounded-lg p-3">
66
+ <p class="text-sm text-red-400">${error}</p>
67
+ </div>
68
+ `}
69
+ ${!botInfo &&
70
+ !loading &&
71
+ !error &&
72
+ html` <p class="text-sm text-gray-400">Checking bot token...</p> `}
73
+
74
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
75
+ <p class="text-xs font-medium text-gray-300">
76
+ Before continuing, configure BotFather:
77
+ </p>
78
+ <ol class="text-xs text-gray-400 space-y-1.5 list-decimal list-inside">
79
+ <li>
80
+ Open <span class="text-gray-300">@BotFather</span> in Telegram
81
+ </li>
82
+ <li>
83
+ Send <code class="bg-black/40 px-1 rounded">/mybots</code> and
84
+ select your bot
85
+ </li>
86
+ <li>
87
+ Go to <span class="text-gray-300">Bot Settings</span> >
88
+ <span class="text-gray-300">Group Privacy</span>
89
+ </li>
90
+ <li>Turn it <span class="text-yellow-400 font-medium">OFF</span></li>
91
+ </ol>
92
+ </div>
93
+
94
+ <div class="grid grid-cols-2 gap-2">
95
+ <div />
96
+ <button
97
+ onclick=${onNext}
98
+ disabled=${!botInfo}
99
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan ${!botInfo
100
+ ? "opacity-50 cursor-not-allowed"
101
+ : ""}"
102
+ >
103
+ Next
104
+ </button>
105
+ </div>
106
+ </div>
107
+ `;
108
+ };
109
+
110
+ // Step 2: Create Group
111
+ export const CreateGroupStep = ({ onNext, onBack }) => html`
112
+ <div class="space-y-4">
113
+ <h3 class="text-sm font-semibold">Create a Telegram Group</h3>
114
+
115
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
116
+ <p class="text-xs font-medium text-gray-300">Create the group</p>
117
+ <ol class="text-xs text-gray-400 space-y-2 list-decimal list-inside">
118
+ <li>
119
+ Open Telegram and create a
120
+ <span class="text-gray-300">new group</span>
121
+ </li>
122
+ <li>
123
+ Search for and add <span class="text-gray-300">your bot</span> as a
124
+ member
125
+ </li>
126
+ <li>
127
+ Hit <span class="text-gray-300">Next</span>, give the group a name
128
+ (e.g. "My Workspace"), and create it
129
+ </li>
130
+ </ol>
131
+ </div>
132
+
133
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
134
+ <p class="text-xs font-medium text-gray-300">Enable topics</p>
135
+ <ol class="text-xs text-gray-400 space-y-2 list-decimal list-inside">
136
+ <li>Tap the group name at the top to open settings</li>
137
+ <li>
138
+ Tap <span class="text-gray-300">Edit</span> (pencil icon), scroll to
139
+ <span class="text-gray-300"> Topics</span>, toggle it
140
+ <span class="text-yellow-400 font-medium"> ON</span>
141
+ </li>
142
+ </ol>
143
+ </div>
144
+
145
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
146
+ <p class="text-xs font-medium text-gray-300">Make the bot an admin</p>
147
+ <ol class="text-xs text-gray-400 space-y-2 list-decimal list-inside">
148
+ <li>Go to <span class="text-gray-300">Members</span>, tap your bot</li>
149
+ <li>
150
+ Promote it to <span class="text-yellow-400 font-medium">Admin</span>
151
+ </li>
152
+ <li>
153
+ Make sure
154
+ <span class="text-yellow-400 font-medium"> Manage Topics </span>
155
+ permission is enabled
156
+ </li>
157
+ </ol>
158
+ </div>
159
+
160
+ <p class="text-xs text-gray-500">
161
+ Once all three steps are done, continue to verify the setup.
162
+ </p>
163
+
164
+ <div class="grid grid-cols-2 gap-2">
165
+ <button
166
+ onclick=${onBack}
167
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
168
+ >
169
+ Back
170
+ </button>
171
+ <button
172
+ onclick=${onNext}
173
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan"
174
+ >
175
+ Next
176
+ </button>
177
+ </div>
178
+ </div>
179
+ `;
180
+
181
+ // Step 3: Add Bot to Group / Verify Group
182
+ export const AddBotStep = ({
183
+ groupId,
184
+ setGroupId,
185
+ groupInfo,
186
+ setGroupInfo,
187
+ userId,
188
+ setUserId,
189
+ verifyGroupError,
190
+ setVerifyGroupError,
191
+ onNext,
192
+ onBack,
193
+ }) => {
194
+ const [input, setInput] = useState(groupId || "");
195
+ const [loading, setLoading] = useState(false);
196
+ const [saving, setSaving] = useState(false);
197
+ const verifyWarnings = groupInfo
198
+ ? [
199
+ ...(!groupInfo.chat?.isForum
200
+ ? ["Topics are OFF. Enable Topics in Telegram group settings."]
201
+ : []),
202
+ ...(!groupInfo.bot?.isAdmin
203
+ ? ["Bot is not an admin. Promote it to admin in group members."]
204
+ : []),
205
+ ...(!groupInfo.bot?.canManageTopics
206
+ ? [
207
+ "Bot is missing Manage Topics permission. Enable it in admin permissions.",
208
+ ]
209
+ : []),
210
+ ]
211
+ : [];
212
+
213
+ const verify = async () => {
214
+ const id = input.trim();
215
+ if (!id) return;
216
+ setLoading(true);
217
+ setVerifyGroupError(null);
218
+ try {
219
+ const data = await api.verifyGroup(id);
220
+ if (!data.ok) throw new Error(data.error);
221
+ setGroupId(id);
222
+ setGroupInfo(data);
223
+ if (!String(userId || "").trim() && data.suggestedUserId) {
224
+ setUserId(String(data.suggestedUserId));
225
+ }
226
+ } catch (e) {
227
+ setVerifyGroupError(e.message);
228
+ setGroupInfo(null);
229
+ }
230
+ setLoading(false);
231
+ };
232
+ const canContinue = !!(
233
+ groupInfo &&
234
+ groupInfo.chat?.isForum &&
235
+ groupInfo.bot?.isAdmin &&
236
+ groupInfo.bot?.canManageTopics
237
+ );
238
+ const continueWithConfig = async () => {
239
+ if (!canContinue || saving) return;
240
+ setVerifyGroupError(null);
241
+ setSaving(true);
242
+ try {
243
+ const userIdValue = String(userId || "").trim();
244
+ const data = await api.configureGroup(groupId, {
245
+ ...(userIdValue ? { userId: userIdValue } : {}),
246
+ groupName: groupInfo?.chat?.title || groupId,
247
+ requireMention: false,
248
+ });
249
+ if (!data?.ok)
250
+ throw new Error(data?.error || "Failed to configure Telegram group");
251
+ if (data.userId) setUserId(String(data.userId));
252
+ onNext();
253
+ } catch (e) {
254
+ setVerifyGroupError(e.message);
255
+ }
256
+ setSaving(false);
257
+ };
258
+
259
+ return html`
260
+ <div class="space-y-4">
261
+ <h3 class="text-sm font-semibold">Verify Group</h3>
262
+
263
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
264
+ <p class="text-xs text-gray-400">To get your group chat ID:</p>
265
+ <ol class="text-xs text-gray-400 space-y-1 list-decimal list-inside">
266
+ <li>
267
+ Invite <span class="text-gray-300">@myidbot</span> to your group
268
+ </li>
269
+ <li>
270
+ Send <code class="bg-black/40 px-1 rounded">/getgroupid</code>
271
+ </li>
272
+ <li>
273
+ Copy the ID (starts with
274
+ <code class="bg-black/40 px-1 rounded">-100</code>)
275
+ </li>
276
+ </ol>
277
+ </div>
278
+
279
+ <div class="flex gap-2">
280
+ <input
281
+ type="text"
282
+ value=${input}
283
+ onInput=${(e) => setInput(e.target.value)}
284
+ placeholder="-100XXXXXXXXXX"
285
+ class="flex-1 bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500"
286
+ />
287
+ <${ActionButton}
288
+ onClick=${verify}
289
+ disabled=${!input.trim() || loading}
290
+ loading=${loading}
291
+ tone="secondary"
292
+ size="md"
293
+ idleLabel="Verify"
294
+ loadingMode="inline"
295
+ className="rounded-lg"
296
+ />
297
+ </div>
298
+
299
+ ${verifyGroupError &&
300
+ html`
301
+ <div class="bg-red-500/10 border border-red-500/20 rounded-lg p-3">
302
+ <p class="text-sm text-red-400">${verifyGroupError}</p>
303
+ </div>
304
+ `}
305
+ ${groupInfo &&
306
+ html`
307
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
308
+ <div class="flex items-center gap-2">
309
+ <span class="text-sm text-gray-300 font-medium">${groupInfo.chat.title}</span>
310
+ <${Badge} tone="success">Verified</${Badge}>
311
+ </div>
312
+ <div class="flex gap-3 text-xs text-gray-500">
313
+ <span>Topics: ${groupInfo.chat.isForum ? "ON" : "OFF"}</span>
314
+ <span>Bot: ${groupInfo.bot.status}</span>
315
+ </div>
316
+ </div>
317
+ `}
318
+ ${groupInfo &&
319
+ verifyWarnings.length === 0 &&
320
+ html`
321
+ <div class="bg-black/20 border border-border rounded-lg p-3 space-y-2">
322
+ <p class="text-xs text-gray-500">Your Telegram User ID</p>
323
+ <input
324
+ type="text"
325
+ value=${userId}
326
+ onInput=${(e) => setUserId(e.target.value)}
327
+ placeholder="e.g. 123456789"
328
+ class="w-full bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500"
329
+ />
330
+ <p class="text-xs text-gray-500">
331
+ Auto-filled from Telegram admins. Edit if needed.
332
+ </p>
333
+ </div>
334
+ `}
335
+ ${verifyWarnings.length > 0 &&
336
+ html`
337
+ <div
338
+ class="bg-red-500/10 border border-red-500/20 rounded-lg p-3 space-y-3"
339
+ >
340
+ <p class="text-xs font-medium text-red-300">
341
+ Fix these before continuing:
342
+ </p>
343
+ <ul class="text-xs text-red-200 space-y-1 list-disc list-inside">
344
+ ${verifyWarnings.map((message) => html`<li>${message}</li>`)}
345
+ </ul>
346
+ <p class="text-xs text-red-300 ">Once fixed, hit Verify again.</p>
347
+ </div>
348
+ `}
349
+
350
+ <div class="grid grid-cols-2 gap-2">
351
+ <button
352
+ onclick=${onBack}
353
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
354
+ >
355
+ Back
356
+ </button>
357
+ <button
358
+ onclick=${continueWithConfig}
359
+ disabled=${!canContinue || saving}
360
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan ${!canContinue ||
361
+ saving
362
+ ? "opacity-50 cursor-not-allowed"
363
+ : ""}"
364
+ >
365
+ ${saving ? "Saving..." : "Next"}
366
+ </button>
367
+ </div>
368
+ </div>
369
+ `;
370
+ };
371
+
372
+ // Step 4: Create Topics
373
+ export const TopicsStep = ({ groupId, topics, setTopics, onNext, onBack }) => {
374
+ const [newTopicName, setNewTopicName] = useState("");
375
+ const [newTopicInstructions, setNewTopicInstructions] = useState("");
376
+ const [creating, setCreating] = useState(false);
377
+ const [error, setError] = useState(null);
378
+ const [deleting, setDeleting] = useState(null);
379
+ const [deleteTopicConfirm, setDeleteTopicConfirm] = useState(null);
380
+
381
+ const loadTopics = async () => {
382
+ const data = await api.listTopics(groupId);
383
+ if (data.ok) setTopics(data.topics);
384
+ };
385
+
386
+ useEffect(() => {
387
+ loadTopics();
388
+ }, [groupId]);
389
+
390
+ const createSingle = async () => {
391
+ const name = newTopicName.trim();
392
+ const systemInstructions = newTopicInstructions.trim();
393
+ if (!name) return;
394
+ setCreating(true);
395
+ setError(null);
396
+ try {
397
+ const data = await api.createTopicsBulk(groupId, [
398
+ { name, ...(systemInstructions ? { systemInstructions } : {}) },
399
+ ]);
400
+ if (!data.ok)
401
+ throw new Error(data.results?.[0]?.error || "Failed to create topic");
402
+ const failed = data.results.filter((r) => !r.ok);
403
+ if (failed.length > 0) throw new Error(failed[0].error);
404
+ setNewTopicName("");
405
+ setNewTopicInstructions("");
406
+ await loadTopics();
407
+ showToast(`Created topic: ${name}`, "success");
408
+ } catch (e) {
409
+ setError(e.message);
410
+ }
411
+ setCreating(false);
412
+ };
413
+
414
+ const handleDelete = async (topicId, topicName) => {
415
+ setDeleting(topicId);
416
+ try {
417
+ const data = await api.deleteTopic(groupId, topicId);
418
+ if (!data.ok) throw new Error(data.error);
419
+ await loadTopics();
420
+ if (data.removedFromRegistryOnly) {
421
+ showToast(`Removed stale topic from registry: ${topicName}`, "success");
422
+ } else {
423
+ showToast(`Deleted topic: ${topicName}`, "success");
424
+ }
425
+ } catch (e) {
426
+ showToast(`Failed to delete: ${e.message}`, "error");
427
+ }
428
+ setDeleting(null);
429
+ };
430
+
431
+ const topicEntries = Object.entries(topics || {});
432
+
433
+ return html`
434
+ <div class="space-y-4">
435
+ <h3 class="text-sm font-semibold">Create Topics</h3>
436
+
437
+ ${topicEntries.length > 0 &&
438
+ html`
439
+ <div
440
+ class="bg-black/20 border border-border rounded-lg overflow-hidden"
441
+ >
442
+ <table class="w-full text-xs">
443
+ <thead>
444
+ <tr class="border-b border-border">
445
+ <th class="text-left px-3 py-2 text-gray-500 font-medium">
446
+ Topic
447
+ </th>
448
+ <th class="text-left px-3 py-2 text-gray-500 font-medium">
449
+ Thread ID
450
+ </th>
451
+ <th class="px-3 py-2 w-8" />
452
+ </tr>
453
+ </thead>
454
+ <tbody>
455
+ ${topicEntries.map(
456
+ ([id, t]) => html`
457
+ <tr class="border-b border-border last:border-0">
458
+ <td class="px-3 py-2 text-gray-300">${t.name}</td>
459
+ <td class="px-3 py-2 text-gray-500 font-mono">${id}</td>
460
+ <td class="px-3 py-2">
461
+ <button
462
+ onclick=${() =>
463
+ setDeleteTopicConfirm({
464
+ id: String(id),
465
+ name: String(t.name || ""),
466
+ })}
467
+ disabled=${deleting === id}
468
+ class="text-gray-600 hover:text-red-400 transition-colors ${deleting ===
469
+ id
470
+ ? "opacity-50"
471
+ : ""}"
472
+ title="Delete topic"
473
+ >
474
+ <svg
475
+ width="14"
476
+ height="14"
477
+ viewBox="0 0 16 16"
478
+ fill="currentColor"
479
+ >
480
+ <path
481
+ d="M4.646 4.646a.5.5 0 01.708 0L8 7.293l2.646-2.647a.5.5 0 01.708.708L8.707 8l2.647 2.646a.5.5 0 01-.708.708L8 8.707l-2.646 2.647a.5.5 0 01-.708-.708L7.293 8 4.646 5.354a.5.5 0 010-.708z"
482
+ />
483
+ </svg>
484
+ </button>
485
+ </td>
486
+ </tr>
487
+ `,
488
+ )}
489
+ </tbody>
490
+ </table>
491
+ </div>
492
+ `}
493
+
494
+ <div class="space-y-2">
495
+ <label class="text-xs text-gray-500">Add a topic</label>
496
+ <div class="space-y-2">
497
+ <div class="flex gap-2">
498
+ <input
499
+ type="text"
500
+ value=${newTopicName}
501
+ onInput=${(e) => setNewTopicName(e.target.value)}
502
+ onKeyDown=${(e) => {
503
+ if (e.key === "Enter") createSingle();
504
+ }}
505
+ placeholder="Topic name"
506
+ class="flex-1 bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500"
507
+ />
508
+ </div>
509
+ <textarea
510
+ value=${newTopicInstructions}
511
+ onInput=${(e) => setNewTopicInstructions(e.target.value)}
512
+ placeholder="System instructions (optional)"
513
+ rows="4"
514
+ class="w-full bg-black/30 border border-border rounded-lg px-3 py-2 text-sm text-gray-200 placeholder-gray-600 focus:outline-none focus:border-gray-500 resize-y"
515
+ />
516
+ <div class="flex justify-end">
517
+ <${ActionButton}
518
+ onClick=${createSingle}
519
+ disabled=${creating || !newTopicName.trim()}
520
+ loading=${creating}
521
+ tone="secondary"
522
+ size="lg"
523
+ idleLabel="Add"
524
+ loadingMode="inline"
525
+ className="min-w-[88px]"
526
+ />
527
+ </div>
528
+ </div>
529
+ </div>
530
+ <div class="border-t border-white/10 pt-2" />
531
+
532
+ ${error &&
533
+ html`
534
+ <div class="bg-red-500/10 border border-red-500/20 rounded-lg p-3">
535
+ <p class="text-sm text-red-400">${error}</p>
536
+ </div>
537
+ `}
538
+
539
+ <div class="grid grid-cols-2 gap-2">
540
+ <button
541
+ onclick=${onBack}
542
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
543
+ >
544
+ Back
545
+ </button>
546
+ <button
547
+ onclick=${onNext}
548
+ disabled=${topicEntries.length === 0}
549
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan"
550
+ >
551
+ Next
552
+ </button>
553
+ </div>
554
+ <${ConfirmDialog}
555
+ visible=${!!deleteTopicConfirm}
556
+ title="Delete topic?"
557
+ message=${deleteTopicConfirm
558
+ ? `This will delete "${deleteTopicConfirm.name}" (thread ${deleteTopicConfirm.id}) from your Telegram workspace.`
559
+ : "This will delete this topic from your Telegram workspace."}
560
+ confirmLabel="Delete topic"
561
+ confirmLoadingLabel="Deleting..."
562
+ confirmTone="warning"
563
+ confirmLoading=${!!deleting}
564
+ cancelLabel="Cancel"
565
+ onCancel=${() => {
566
+ if (deleting) return;
567
+ setDeleteTopicConfirm(null);
568
+ }}
569
+ onConfirm=${async () => {
570
+ if (!deleteTopicConfirm) return;
571
+ const pendingDelete = deleteTopicConfirm;
572
+ setDeleteTopicConfirm(null);
573
+ await handleDelete(pendingDelete.id, pendingDelete.name);
574
+ }}
575
+ />
576
+ </div>
577
+ `;
578
+ };
579
+
580
+ // Step 5: Summary
581
+ export const SummaryStep = ({ groupId, groupInfo, topics, onBack, onDone }) => {
582
+ return html`
583
+ <div class="space-y-4">
584
+ <div class="max-w-xl mx-auto text-center space-y-10 mt-10">
585
+ <p class="text-sm font-medium text-green-300">🎉 Setup complete</p>
586
+ <p class="text-xs text-gray-400">
587
+ The topic registry has been injected into
588
+ <code class="bg-black/40 px-1 rounded">TOOLS.md</code> so your agent
589
+ knows which thread ID maps to which topic name.
590
+ </p>
591
+
592
+ <div class="bg-black/20 border border-border rounded-lg p-3">
593
+ <p class="text-xs text-gray-500">
594
+ If you used <span class="text-gray-300">@myidbot</span> to find IDs,
595
+ you can remove it from the group now.
596
+ </p>
597
+ </div>
598
+ </div>
599
+
600
+ <div class="grid grid-cols-2 gap-2">
601
+ <button
602
+ onclick=${onBack}
603
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all border border-border text-gray-300 hover:border-gray-500"
604
+ >
605
+ Back
606
+ </button>
607
+ <button
608
+ onclick=${onDone}
609
+ class="w-full text-sm font-medium px-4 py-2 rounded-xl transition-all ac-btn-cyan"
610
+ >
611
+ Done
612
+ </button>
613
+ </div>
614
+ </div>
615
+ `;
616
+ };