@chrysb/alphaclaw 0.3.5-beta.1 → 0.4.1-beta.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/bin/alphaclaw.js +1 -31
- package/lib/public/assets/icons/google_icon.svg +8 -0
- package/lib/public/css/explorer.css +53 -0
- package/lib/public/css/shell.css +21 -19
- package/lib/public/css/theme.css +17 -0
- package/lib/public/js/app.js +205 -109
- package/lib/public/js/components/credentials-modal.js +36 -8
- package/lib/public/js/components/file-tree.js +212 -22
- package/lib/public/js/components/file-viewer/editor-surface.js +5 -0
- package/lib/public/js/components/file-viewer/index.js +47 -6
- package/lib/public/js/components/file-viewer/markdown-split-view.js +2 -0
- package/lib/public/js/components/file-viewer/status-banners.js +11 -6
- package/lib/public/js/components/file-viewer/toolbar.js +56 -1
- package/lib/public/js/components/file-viewer/use-editor-selection-restore.js +6 -0
- package/lib/public/js/components/file-viewer/use-file-diff.js +11 -0
- package/lib/public/js/components/file-viewer/use-file-loader.js +12 -2
- package/lib/public/js/components/file-viewer/use-file-viewer.js +142 -15
- package/lib/public/js/components/google/account-row.js +131 -0
- package/lib/public/js/components/google/add-account-modal.js +93 -0
- package/lib/public/js/components/google/gmail-setup-wizard.js +450 -0
- package/lib/public/js/components/google/gmail-watch-toggle.js +81 -0
- package/lib/public/js/components/google/index.js +553 -0
- package/lib/public/js/components/google/use-gmail-watch.js +140 -0
- package/lib/public/js/components/google/use-google-accounts.js +41 -0
- package/lib/public/js/components/icons.js +26 -0
- package/lib/public/js/components/scope-picker.js +1 -1
- package/lib/public/js/components/sidebar-git-panel.js +48 -20
- package/lib/public/js/components/sidebar.js +93 -75
- package/lib/public/js/components/toast.js +11 -7
- package/lib/public/js/components/usage-tab/constants.js +31 -0
- package/lib/public/js/components/usage-tab/formatters.js +24 -0
- package/lib/public/js/components/usage-tab/index.js +72 -0
- package/lib/public/js/components/usage-tab/overview-section.js +147 -0
- package/lib/public/js/components/usage-tab/sessions-section.js +175 -0
- package/lib/public/js/components/usage-tab/use-usage-tab.js +241 -0
- package/lib/public/js/components/webhooks.js +182 -129
- package/lib/public/js/lib/api.js +178 -9
- package/lib/public/js/lib/browse-file-policies.js +29 -11
- package/lib/public/js/lib/format.js +71 -0
- package/lib/public/js/lib/syntax-highlighters/index.js +6 -5
- package/lib/public/shared/browse-file-policies.json +13 -0
- package/lib/server/constants.js +47 -7
- package/lib/server/gmail-push.js +109 -0
- package/lib/server/gmail-serve.js +254 -0
- package/lib/server/gmail-watch.js +725 -0
- package/lib/server/google-state.js +317 -0
- package/lib/server/helpers.js +17 -11
- package/lib/server/internal-files-migration.js +31 -3
- package/lib/server/onboarding/github.js +21 -2
- package/lib/server/onboarding/index.js +1 -3
- package/lib/server/onboarding/openclaw.js +3 -0
- package/lib/server/onboarding/workspace.js +40 -0
- package/lib/server/routes/browse/index.js +90 -2
- package/lib/server/routes/gmail.js +128 -0
- package/lib/server/routes/google.js +433 -213
- package/lib/server/routes/system.js +107 -0
- package/lib/server/routes/usage.js +29 -2
- package/lib/server/routes/webhooks.js +52 -17
- package/lib/server/usage-db.js +283 -15
- package/lib/server/watchdog.js +66 -0
- package/lib/server/webhook-middleware.js +99 -1
- package/lib/server/webhooks.js +214 -65
- package/lib/server.js +27 -0
- package/lib/setup/gitignore +6 -0
- package/lib/setup/hourly-git-sync.sh +29 -2
- package/package.json +1 -1
- package/lib/public/js/components/google.js +0 -228
- package/lib/public/js/components/usage-tab.js +0 -531
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
fetchWebhookRequests,
|
|
10
10
|
fetchWebhooks,
|
|
11
11
|
} from "../lib/api.js";
|
|
12
|
+
import {
|
|
13
|
+
formatLocaleDateTime,
|
|
14
|
+
formatLocaleDateTimeWithTodayTime,
|
|
15
|
+
} from "../lib/format.js";
|
|
12
16
|
import { showToast } from "./toast.js";
|
|
13
17
|
import { PageHeader } from "./page-header.js";
|
|
14
18
|
import { ConfirmDialog } from "./confirm-dialog.js";
|
|
15
19
|
import { ActionButton } from "./action-button.js";
|
|
16
20
|
import { ModalShell } from "./modal-shell.js";
|
|
21
|
+
import { Badge } from "./badge.js";
|
|
17
22
|
import { CloseIcon } from "./icons.js";
|
|
18
23
|
|
|
19
24
|
const html = htm.bind(h);
|
|
@@ -21,29 +26,11 @@ const kNamePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
21
26
|
const kStatusFilters = ["all", "success", "error"];
|
|
22
27
|
|
|
23
28
|
const formatDateTime = (value) => {
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
return new Date(value).toLocaleString();
|
|
27
|
-
} catch {
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
29
|
+
return formatLocaleDateTime(value, { fallback: "—" });
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
const formatLastReceived = (value) => {
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
const timestamp = new Date(value);
|
|
36
|
-
const now = new Date();
|
|
37
|
-
const isSameDay =
|
|
38
|
-
timestamp.getFullYear() === now.getFullYear() &&
|
|
39
|
-
timestamp.getMonth() === now.getMonth() &&
|
|
40
|
-
timestamp.getDate() === now.getDate();
|
|
41
|
-
return isSameDay
|
|
42
|
-
? timestamp.toLocaleTimeString()
|
|
43
|
-
: timestamp.toLocaleString();
|
|
44
|
-
} catch {
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
33
|
+
return formatLocaleDateTimeWithTodayTime(value, { fallback: "—" });
|
|
47
34
|
};
|
|
48
35
|
|
|
49
36
|
const formatBytes = (size) => {
|
|
@@ -197,6 +184,7 @@ export const Webhooks = ({
|
|
|
197
184
|
onSelectHook = () => {},
|
|
198
185
|
onBackToList = () => {},
|
|
199
186
|
onRestartRequired = () => {},
|
|
187
|
+
onOpenFile = () => {},
|
|
200
188
|
}) => {
|
|
201
189
|
const [isCreating, setIsCreating] = useState(false);
|
|
202
190
|
const [newName, setNewName] = useState("");
|
|
@@ -238,12 +226,13 @@ export const Webhooks = ({
|
|
|
238
226
|
);
|
|
239
227
|
|
|
240
228
|
const selectedWebhook = detailPoll.data;
|
|
229
|
+
const selectedWebhookManaged = Boolean(selectedWebhook?.managed);
|
|
241
230
|
const requests = requestsPoll.data?.requests || [];
|
|
242
231
|
const webhookUrl =
|
|
243
232
|
selectedWebhook?.fullUrl || `.../hooks/${selectedHookName}`;
|
|
244
233
|
const webhookUrlWithQueryToken =
|
|
245
234
|
selectedWebhook?.queryStringUrl ||
|
|
246
|
-
`${webhookUrl}${webhookUrl.includes("?") ? "&" : "?"}token=<
|
|
235
|
+
`${webhookUrl}${webhookUrl.includes("?") ? "&" : "?"}token=<OPENCLAW_HOOKS_TOKEN>`;
|
|
247
236
|
const derivedTokenFromQuery = (() => {
|
|
248
237
|
try {
|
|
249
238
|
const parsed = new URL(webhookUrlWithQueryToken);
|
|
@@ -256,17 +245,33 @@ export const Webhooks = ({
|
|
|
256
245
|
selectedWebhook?.authHeaderValue ||
|
|
257
246
|
(derivedTokenFromQuery
|
|
258
247
|
? `Authorization: Bearer ${derivedTokenFromQuery}`
|
|
259
|
-
: "Authorization: Bearer <
|
|
248
|
+
: "Authorization: Bearer <OPENCLAW_HOOKS_TOKEN>");
|
|
260
249
|
const bearerTokenValue = authHeaderValue.startsWith("Authorization: ")
|
|
261
250
|
? authHeaderValue.slice("Authorization: ".length)
|
|
262
251
|
: authHeaderValue;
|
|
263
|
-
const webhookTestPayload = useMemo(
|
|
264
|
-
()
|
|
252
|
+
const webhookTestPayload = useMemo(() => {
|
|
253
|
+
if (String(selectedHookName || "").trim().toLowerCase() === "gmail") {
|
|
254
|
+
return {
|
|
255
|
+
payload: {
|
|
256
|
+
account: "test@gmail.com",
|
|
257
|
+
messages: [
|
|
258
|
+
{
|
|
259
|
+
id: "test-message-1",
|
|
260
|
+
from: "alerts@example.com",
|
|
261
|
+
to: ["test@gmail.com"],
|
|
262
|
+
subject: "Test Gmail webhook event",
|
|
263
|
+
snippet: "This is a simulated Gmail message payload for webhook testing.",
|
|
264
|
+
receivedAt: new Date().toISOString(),
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
265
271
|
source: "manual-test",
|
|
266
|
-
message: `This is a test of the ${selectedHookName || "webhook"} webhook
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
);
|
|
272
|
+
message: `This is a test of the ${selectedHookName || "webhook"} webhook.`,
|
|
273
|
+
};
|
|
274
|
+
}, [selectedHookName]);
|
|
270
275
|
const webhookTestPayloadJson = JSON.stringify(webhookTestPayload);
|
|
271
276
|
const curlCommandHeaders =
|
|
272
277
|
`curl -X POST "${webhookUrl}" ` +
|
|
@@ -277,8 +282,9 @@ export const Webhooks = ({
|
|
|
277
282
|
`curl -X POST "${webhookUrlWithQueryToken}" ` +
|
|
278
283
|
`-H "Content-Type: application/json" ` +
|
|
279
284
|
`-d '${webhookTestPayloadJson}'`;
|
|
285
|
+
const effectiveAuthMode = selectedWebhookManaged ? "headers" : authMode;
|
|
280
286
|
const activeCurlCommand =
|
|
281
|
-
|
|
287
|
+
effectiveAuthMode === "query" ? curlCommandQuery : curlCommandHeaders;
|
|
282
288
|
|
|
283
289
|
const canCreate = useMemo(() => {
|
|
284
290
|
const name = String(newName || "")
|
|
@@ -376,9 +382,9 @@ export const Webhooks = ({
|
|
|
376
382
|
if (!selectedHookName || sendingTestWebhook) return;
|
|
377
383
|
setSendingTestWebhook(true);
|
|
378
384
|
const requestUrl =
|
|
379
|
-
|
|
385
|
+
effectiveAuthMode === "query" ? webhookUrlWithQueryToken : webhookUrl;
|
|
380
386
|
const headers = { "Content-Type": "application/json" };
|
|
381
|
-
if (
|
|
387
|
+
if (effectiveAuthMode === "headers") {
|
|
382
388
|
headers.Authorization = bearerTokenValue;
|
|
383
389
|
}
|
|
384
390
|
try {
|
|
@@ -412,8 +418,8 @@ export const Webhooks = ({
|
|
|
412
418
|
setSendingTestWebhook(false);
|
|
413
419
|
}
|
|
414
420
|
}, [
|
|
415
|
-
authMode,
|
|
416
421
|
bearerTokenValue,
|
|
422
|
+
effectiveAuthMode,
|
|
417
423
|
requestsPoll.refresh,
|
|
418
424
|
selectedHookName,
|
|
419
425
|
sendingTestWebhook,
|
|
@@ -430,9 +436,9 @@ export const Webhooks = ({
|
|
|
430
436
|
return;
|
|
431
437
|
}
|
|
432
438
|
const requestUrl =
|
|
433
|
-
|
|
439
|
+
effectiveAuthMode === "query" ? webhookUrlWithQueryToken : webhookUrl;
|
|
434
440
|
const headers = { "Content-Type": "application/json" };
|
|
435
|
-
if (
|
|
441
|
+
if (effectiveAuthMode === "headers") {
|
|
436
442
|
headers.Authorization = bearerTokenValue;
|
|
437
443
|
}
|
|
438
444
|
setReplayingRequestId(item.id);
|
|
@@ -468,8 +474,8 @@ export const Webhooks = ({
|
|
|
468
474
|
}
|
|
469
475
|
},
|
|
470
476
|
[
|
|
471
|
-
authMode,
|
|
472
477
|
bearerTokenValue,
|
|
478
|
+
effectiveAuthMode,
|
|
473
479
|
replayingRequestId,
|
|
474
480
|
requestsPoll.refresh,
|
|
475
481
|
webhookUrl,
|
|
@@ -482,7 +488,10 @@ export const Webhooks = ({
|
|
|
482
488
|
await navigator.clipboard.writeText(String(value || ""));
|
|
483
489
|
showToast(`${label} copied`, "success");
|
|
484
490
|
} catch {
|
|
485
|
-
showToast(
|
|
491
|
+
showToast(
|
|
492
|
+
`Could not copy ${String(label || "value").toLowerCase()}`,
|
|
493
|
+
"error",
|
|
494
|
+
);
|
|
486
495
|
}
|
|
487
496
|
}, []);
|
|
488
497
|
|
|
@@ -538,36 +547,40 @@ export const Webhooks = ({
|
|
|
538
547
|
<div
|
|
539
548
|
class="bg-black/20 border border-border rounded-lg p-3 space-y-4"
|
|
540
549
|
>
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
550
|
+
${selectedWebhookManaged
|
|
551
|
+
? null
|
|
552
|
+
: html`
|
|
553
|
+
<div class="space-y-2">
|
|
554
|
+
<p class="text-xs text-gray-500">Auth mode</p>
|
|
555
|
+
<div class="flex items-center gap-2">
|
|
556
|
+
<button
|
|
557
|
+
class="text-xs px-2 py-1 rounded border transition-colors ${authMode ===
|
|
558
|
+
"headers"
|
|
559
|
+
? "border-cyan-400 text-cyan-200 bg-cyan-400/10"
|
|
560
|
+
: "border-border text-gray-400 hover:text-gray-200"}"
|
|
561
|
+
onclick=${() => setAuthMode("headers")}
|
|
562
|
+
>
|
|
563
|
+
Headers
|
|
564
|
+
</button>
|
|
565
|
+
<button
|
|
566
|
+
class="text-xs px-2 py-1 rounded border transition-colors ${authMode ===
|
|
567
|
+
"query"
|
|
568
|
+
? "border-cyan-400 text-cyan-200 bg-cyan-400/10"
|
|
569
|
+
: "border-border text-gray-400 hover:text-gray-200"}"
|
|
570
|
+
onclick=${() => setAuthMode("query")}
|
|
571
|
+
>
|
|
572
|
+
Query string
|
|
573
|
+
</button>
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
`}
|
|
564
577
|
<div class="space-y-2">
|
|
565
578
|
<p class="text-xs text-gray-500">Webhook URL</p>
|
|
566
579
|
<div class="flex items-center gap-2">
|
|
567
580
|
<input
|
|
568
581
|
type="text"
|
|
569
582
|
readonly
|
|
570
|
-
value=${
|
|
583
|
+
value=${effectiveAuthMode === "query"
|
|
571
584
|
? webhookUrlWithQueryToken
|
|
572
585
|
: webhookUrl}
|
|
573
586
|
class="h-8 flex-1 bg-black/30 border border-border rounded-lg px-3 text-xs text-gray-200 outline-none font-mono"
|
|
@@ -577,7 +590,7 @@ export const Webhooks = ({
|
|
|
577
590
|
onclick=${async () => {
|
|
578
591
|
try {
|
|
579
592
|
await navigator.clipboard.writeText(
|
|
580
|
-
|
|
593
|
+
effectiveAuthMode === "query"
|
|
581
594
|
? webhookUrlWithQueryToken
|
|
582
595
|
: webhookUrl,
|
|
583
596
|
);
|
|
@@ -591,44 +604,46 @@ export const Webhooks = ({
|
|
|
591
604
|
</button>
|
|
592
605
|
</div>
|
|
593
606
|
</div>
|
|
594
|
-
${
|
|
595
|
-
?
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
<div class="
|
|
599
|
-
<
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
607
|
+
${selectedWebhookManaged
|
|
608
|
+
? null
|
|
609
|
+
: effectiveAuthMode === "headers"
|
|
610
|
+
? html`
|
|
611
|
+
<div class="space-y-2">
|
|
612
|
+
<p class="text-xs text-gray-500">Auth headers</p>
|
|
613
|
+
<div class="flex items-center gap-2">
|
|
614
|
+
<input
|
|
615
|
+
type="text"
|
|
616
|
+
readonly
|
|
617
|
+
value=${authHeaderValue}
|
|
618
|
+
class="h-8 flex-1 bg-black/30 border border-border rounded-lg px-3 text-xs text-gray-200 outline-none font-mono"
|
|
619
|
+
/>
|
|
620
|
+
<button
|
|
621
|
+
class="h-8 text-xs px-2.5 rounded-lg ac-btn-secondary shrink-0"
|
|
622
|
+
onclick=${async () => {
|
|
623
|
+
try {
|
|
624
|
+
await navigator.clipboard.writeText(
|
|
625
|
+
bearerTokenValue,
|
|
626
|
+
);
|
|
627
|
+
showToast("Bearer token copied", "success");
|
|
628
|
+
} catch {
|
|
629
|
+
showToast(
|
|
630
|
+
"Could not copy bearer token",
|
|
631
|
+
"error",
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
}}
|
|
635
|
+
>
|
|
636
|
+
Copy
|
|
637
|
+
</button>
|
|
638
|
+
</div>
|
|
623
639
|
</div>
|
|
624
|
-
|
|
625
|
-
`
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
`}
|
|
640
|
+
`
|
|
641
|
+
: html`
|
|
642
|
+
<p class="text-xs text-yellow-300">
|
|
643
|
+
Always use auth headers when possible. Query string is
|
|
644
|
+
less secure.
|
|
645
|
+
</p>
|
|
646
|
+
`}
|
|
632
647
|
</div>
|
|
633
648
|
|
|
634
649
|
<div
|
|
@@ -674,11 +689,16 @@ export const Webhooks = ({
|
|
|
674
689
|
<div class="bg-black/20 border border-border rounded-lg p-3">
|
|
675
690
|
<div class="flex items-center gap-2 text-xs text-gray-300">
|
|
676
691
|
<span class="text-gray-500">Transform:</span>
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
692
|
+
${selectedWebhook?.transformPath
|
|
693
|
+
? html`<button
|
|
694
|
+
type="button"
|
|
695
|
+
class="ac-tip-link flex-1 min-w-0 truncate block text-left font-mono"
|
|
696
|
+
title=${selectedWebhook.transformPath}
|
|
697
|
+
onclick=${() => onOpenFile(selectedWebhook.transformPath)}
|
|
698
|
+
>
|
|
699
|
+
${selectedWebhook.transformPath}
|
|
700
|
+
</button>`
|
|
701
|
+
: html`<code class="flex-1 min-w-0 truncate block">—</code>`}
|
|
682
702
|
<span
|
|
683
703
|
class=${`ml-auto inline-flex items-center gap-1 px-1.5 py-0.5 rounded border font-sans ${
|
|
684
704
|
selectedWebhook?.transformExists
|
|
@@ -700,23 +720,37 @@ export const Webhooks = ({
|
|
|
700
720
|
<p class="text-xs text-gray-600">
|
|
701
721
|
Created: ${formatDateTime(selectedWebhook?.createdAt)}
|
|
702
722
|
</p>
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
723
|
+
${selectedWebhookManaged
|
|
724
|
+
? null
|
|
725
|
+
: html`<${ActionButton}
|
|
726
|
+
onClick=${() => {
|
|
727
|
+
if (deleting) return;
|
|
728
|
+
setDeleteTransformDir(true);
|
|
729
|
+
setShowDeleteConfirm(true);
|
|
730
|
+
}}
|
|
731
|
+
disabled=${deleting}
|
|
732
|
+
loading=${deleting}
|
|
733
|
+
tone="danger"
|
|
734
|
+
size="sm"
|
|
735
|
+
idleLabel="Delete"
|
|
736
|
+
loadingLabel="Deleting..."
|
|
737
|
+
className="shrink-0 px-2.5 py-1"
|
|
738
|
+
/>`}
|
|
717
739
|
</div>
|
|
718
740
|
</div>
|
|
719
741
|
|
|
742
|
+
${selectedWebhookManaged
|
|
743
|
+
? html`
|
|
744
|
+
<div
|
|
745
|
+
class="rounded-lg border border-yellow-500/30 bg-yellow-500/10 p-3"
|
|
746
|
+
>
|
|
747
|
+
<p class="text-xs text-yellow-200">
|
|
748
|
+
This webhook is managed by Gmail Watch setup and cannot
|
|
749
|
+
be deleted or edited from this page.
|
|
750
|
+
</p>
|
|
751
|
+
</div>
|
|
752
|
+
`
|
|
753
|
+
: null}
|
|
720
754
|
<div
|
|
721
755
|
class="bg-surface border border-border rounded-xl p-4 space-y-3"
|
|
722
756
|
>
|
|
@@ -749,10 +783,9 @@ export const Webhooks = ({
|
|
|
749
783
|
</p>`
|
|
750
784
|
: html`
|
|
751
785
|
<div class="ac-history-list">
|
|
752
|
-
${requests.map(
|
|
753
|
-
(item)
|
|
754
|
-
|
|
755
|
-
return html`
|
|
786
|
+
${requests.map((item) => {
|
|
787
|
+
const statusTone = getRequestStatusTone(item.status);
|
|
788
|
+
return html`
|
|
756
789
|
<details
|
|
757
790
|
class="ac-history-item"
|
|
758
791
|
open=${expandedRows.has(item.id)}
|
|
@@ -764,15 +797,21 @@ export const Webhooks = ({
|
|
|
764
797
|
>
|
|
765
798
|
<summary class="ac-history-summary">
|
|
766
799
|
<div class="ac-history-summary-row">
|
|
767
|
-
<span
|
|
768
|
-
|
|
800
|
+
<span
|
|
801
|
+
class="inline-flex items-center gap-2 min-w-0"
|
|
802
|
+
>
|
|
803
|
+
<span
|
|
804
|
+
class="ac-history-toggle shrink-0"
|
|
805
|
+
aria-hidden="true"
|
|
769
806
|
>▸</span
|
|
770
807
|
>
|
|
771
808
|
<span class="truncate text-xs text-gray-300">
|
|
772
809
|
${formatLastReceived(item.createdAt)}
|
|
773
810
|
</span>
|
|
774
811
|
</span>
|
|
775
|
-
<span
|
|
812
|
+
<span
|
|
813
|
+
class="inline-flex items-center gap-2 shrink-0"
|
|
814
|
+
>
|
|
776
815
|
<span class="text-xs text-gray-500"
|
|
777
816
|
>${formatBytes(item.payloadSize)}</span
|
|
778
817
|
>
|
|
@@ -827,7 +866,9 @@ ${jsonPretty(item.headers)}</pre
|
|
|
827
866
|
>
|
|
828
867
|
${jsonPretty(item.payload)}</pre
|
|
829
868
|
>
|
|
830
|
-
<div
|
|
869
|
+
<div
|
|
870
|
+
class="mt-2 flex justify-start gap-2"
|
|
871
|
+
>
|
|
831
872
|
<button
|
|
832
873
|
class="h-7 text-xs px-2.5 rounded-lg ac-btn-secondary"
|
|
833
874
|
onclick=${() =>
|
|
@@ -882,8 +923,7 @@ ${jsonPretty(item.gatewayBody)}</pre
|
|
|
882
923
|
: null}
|
|
883
924
|
</details>
|
|
884
925
|
`;
|
|
885
|
-
|
|
886
|
-
)}
|
|
926
|
+
})}
|
|
887
927
|
</div>
|
|
888
928
|
`}
|
|
889
929
|
</div>
|
|
@@ -912,11 +952,12 @@ ${jsonPretty(item.gatewayBody)}</pre
|
|
|
912
952
|
<th class="pb-2 pr-3">Last received</th>
|
|
913
953
|
<th class="pb-2 pr-3">Errors</th>
|
|
914
954
|
<th class="pb-2 pr-3">Health</th>
|
|
955
|
+
<th class="pb-2 pr-3">Type</th>
|
|
915
956
|
</tr>
|
|
916
957
|
</thead>
|
|
917
958
|
<tbody>
|
|
918
959
|
<tr aria-hidden="true">
|
|
919
|
-
<td class="h-2 p-0" colspan="
|
|
960
|
+
<td class="h-2 p-0" colspan="5"></td>
|
|
920
961
|
</tr>
|
|
921
962
|
${webhooks.map(
|
|
922
963
|
(item) => html`
|
|
@@ -955,6 +996,16 @@ ${jsonPretty(item.gatewayBody)}</pre
|
|
|
955
996
|
title=${item.health}
|
|
956
997
|
/>
|
|
957
998
|
</td>
|
|
999
|
+
<td
|
|
1000
|
+
class="px-3 py-2.5 text-xs text-gray-400 group-hover:bg-white/5 transition-colors"
|
|
1001
|
+
>
|
|
1002
|
+
${item.managed
|
|
1003
|
+
? html`<span
|
|
1004
|
+
class="inline-flex items-center rounded-full px-2 py-0.5 text-[11px] bg-cyan-500/10 text-cyan-200"
|
|
1005
|
+
>Managed</span
|
|
1006
|
+
>`
|
|
1007
|
+
: html`<${Badge} tone="neutral">Custom</${Badge}>`}
|
|
1008
|
+
</td>
|
|
958
1009
|
</tr>
|
|
959
1010
|
`,
|
|
960
1011
|
)}
|
|
@@ -976,7 +1027,9 @@ ${jsonPretty(item.gatewayBody)}</pre
|
|
|
976
1027
|
onClose=${() => setIsCreating(false)}
|
|
977
1028
|
/>
|
|
978
1029
|
<${ConfirmDialog}
|
|
979
|
-
visible=${showDeleteConfirm &&
|
|
1030
|
+
visible=${showDeleteConfirm &&
|
|
1031
|
+
!!selectedHookName &&
|
|
1032
|
+
!selectedWebhookManaged}
|
|
980
1033
|
title="Delete webhook?"
|
|
981
1034
|
message=${`This removes "/hooks/${selectedHookName}" from openclaw.json.`}
|
|
982
1035
|
details=${html`
|