@chrysb/alphaclaw 0.7.0 → 0.7.1
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/lib/public/css/cron.css +26 -17
- package/lib/public/css/theme.css +14 -0
- package/lib/public/js/components/cron-tab/cron-calendar.js +17 -12
- package/lib/public/js/components/cron-tab/cron-job-list.js +11 -1
- package/lib/public/js/components/cron-tab/index.js +16 -2
- package/lib/public/js/components/icons.js +11 -0
- package/lib/public/js/components/routes/watchdog-route.js +1 -1
- package/lib/public/js/components/watchdog-tab/console/index.js +115 -0
- package/lib/public/js/components/watchdog-tab/console/use-console.js +137 -0
- package/lib/public/js/components/watchdog-tab/helpers.js +106 -0
- package/lib/public/js/components/watchdog-tab/incidents/index.js +56 -0
- package/lib/public/js/components/watchdog-tab/incidents/use-incidents.js +33 -0
- package/lib/public/js/components/watchdog-tab/index.js +84 -0
- package/lib/public/js/components/watchdog-tab/resource-bar.js +76 -0
- package/lib/public/js/components/watchdog-tab/resources/index.js +85 -0
- package/lib/public/js/components/watchdog-tab/resources/use-resources.js +13 -0
- package/lib/public/js/components/watchdog-tab/settings/index.js +44 -0
- package/lib/public/js/components/watchdog-tab/settings/use-settings.js +117 -0
- package/lib/public/js/components/watchdog-tab/terminal/index.js +20 -0
- package/lib/public/js/components/watchdog-tab/terminal/use-terminal.js +263 -0
- package/lib/public/js/components/watchdog-tab/use-watchdog-tab.js +55 -0
- package/lib/public/js/lib/api.js +39 -0
- package/lib/server/init/register-server-routes.js +239 -0
- package/lib/server/init/runtime-init.js +44 -0
- package/lib/server/init/server-lifecycle.js +55 -0
- package/lib/server/routes/watchdog.js +62 -0
- package/lib/server/watchdog-terminal-ws.js +114 -0
- package/lib/server/watchdog-terminal.js +262 -0
- package/lib/server.js +89 -215
- package/package.json +3 -2
- package/lib/public/js/components/watchdog-tab.js +0 -535
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { Gateway } from "../gateway.js";
|
|
4
|
+
import { useWatchdogTab } from "./use-watchdog-tab.js";
|
|
5
|
+
import { WatchdogResourcesCard } from "./resources/index.js";
|
|
6
|
+
import { WatchdogSettingsCard } from "./settings/index.js";
|
|
7
|
+
import { WatchdogConsoleCard } from "./console/index.js";
|
|
8
|
+
import { WatchdogIncidentsCard } from "./incidents/index.js";
|
|
9
|
+
|
|
10
|
+
const html = htm.bind(h);
|
|
11
|
+
|
|
12
|
+
export const WatchdogTab = ({
|
|
13
|
+
gatewayStatus = null,
|
|
14
|
+
openclawVersion = null,
|
|
15
|
+
watchdogStatus = null,
|
|
16
|
+
onRefreshStatuses = () => {},
|
|
17
|
+
restartingGateway = false,
|
|
18
|
+
onRestartGateway,
|
|
19
|
+
restartSignal = 0,
|
|
20
|
+
openclawUpdateInProgress = false,
|
|
21
|
+
onOpenclawVersionActionComplete = () => {},
|
|
22
|
+
onOpenclawUpdate,
|
|
23
|
+
}) => {
|
|
24
|
+
const state = useWatchdogTab({
|
|
25
|
+
watchdogStatus,
|
|
26
|
+
onRefreshStatuses,
|
|
27
|
+
restartSignal,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return html`
|
|
31
|
+
<div class="space-y-4">
|
|
32
|
+
<${Gateway}
|
|
33
|
+
status=${gatewayStatus}
|
|
34
|
+
openclawVersion=${openclawVersion}
|
|
35
|
+
restarting=${restartingGateway}
|
|
36
|
+
onRestart=${onRestartGateway}
|
|
37
|
+
watchdogStatus=${state.currentWatchdogStatus}
|
|
38
|
+
onRepair=${state.onRepair}
|
|
39
|
+
repairing=${state.isRepairInProgress}
|
|
40
|
+
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
41
|
+
onOpenclawVersionActionComplete=${onOpenclawVersionActionComplete}
|
|
42
|
+
onOpenclawUpdate=${onOpenclawUpdate}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
<${WatchdogResourcesCard}
|
|
46
|
+
resources=${state.resources}
|
|
47
|
+
memoryExpanded=${state.memoryExpanded}
|
|
48
|
+
onSetMemoryExpanded=${state.setMemoryExpanded}
|
|
49
|
+
/>
|
|
50
|
+
|
|
51
|
+
<${WatchdogSettingsCard}
|
|
52
|
+
settings=${state.settings}
|
|
53
|
+
savingSettings=${state.savingSettings}
|
|
54
|
+
onToggleAutoRepair=${state.onToggleAutoRepair}
|
|
55
|
+
onToggleNotifications=${state.onToggleNotifications}
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<${WatchdogConsoleCard}
|
|
59
|
+
activeConsoleTab=${state.activeConsoleTab}
|
|
60
|
+
stickToBottom=${state.stickToBottom}
|
|
61
|
+
onSetStickToBottom=${state.setStickToBottom}
|
|
62
|
+
onSelectConsoleTab=${state.handleSelectConsoleTab}
|
|
63
|
+
connectingTerminal=${state.connectingTerminal}
|
|
64
|
+
terminalConnected=${state.terminalConnected}
|
|
65
|
+
terminalEnded=${state.terminalEnded}
|
|
66
|
+
terminalStatusText=${state.terminalStatusText}
|
|
67
|
+
terminalUiSettling=${state.terminalUiSettling}
|
|
68
|
+
onRestartTerminalSession=${state.onRestartTerminalSession}
|
|
69
|
+
logsRef=${state.logsRef}
|
|
70
|
+
logs=${state.logs}
|
|
71
|
+
loadingLogs=${state.loadingLogs}
|
|
72
|
+
terminalPanelRef=${state.terminalPanelRef}
|
|
73
|
+
terminalHostRef=${state.terminalHostRef}
|
|
74
|
+
terminalInstanceRef=${state.terminalInstanceRef}
|
|
75
|
+
logsPanelHeightPx=${state.logsPanelHeightPx}
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<${WatchdogIncidentsCard}
|
|
79
|
+
events=${state.events}
|
|
80
|
+
onRefresh=${state.refreshEvents}
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
`;
|
|
84
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
const barColor = (percent) => {
|
|
7
|
+
if (percent == null) return "bg-gray-600";
|
|
8
|
+
return "bg-cyan-400";
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const ResourceBar = ({
|
|
12
|
+
label,
|
|
13
|
+
percent,
|
|
14
|
+
detail,
|
|
15
|
+
segments = null,
|
|
16
|
+
expanded = false,
|
|
17
|
+
onToggle = null,
|
|
18
|
+
}) => html`
|
|
19
|
+
<div
|
|
20
|
+
class=${onToggle ? "cursor-pointer group" : ""}
|
|
21
|
+
onclick=${onToggle || undefined}
|
|
22
|
+
>
|
|
23
|
+
<span
|
|
24
|
+
class=${`text-xs text-gray-400 ${onToggle ? "group-hover:text-gray-200 transition-colors" : ""}`}
|
|
25
|
+
>${label}</span
|
|
26
|
+
>
|
|
27
|
+
<div
|
|
28
|
+
class=${`h-0.5 w-full bg-white/15 rounded-full overflow-hidden mt-1.5 flex ${onToggle ? "group-hover:bg-white/10 transition-colors" : ""}`}
|
|
29
|
+
>
|
|
30
|
+
${expanded && segments
|
|
31
|
+
? segments.map(
|
|
32
|
+
(seg) => html`
|
|
33
|
+
<div
|
|
34
|
+
class="h-full"
|
|
35
|
+
style=${{
|
|
36
|
+
width: `${Math.min(100, seg.percent ?? 0)}%`,
|
|
37
|
+
backgroundColor: seg.color,
|
|
38
|
+
transition:
|
|
39
|
+
"width 0.8s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.5s ease",
|
|
40
|
+
}}
|
|
41
|
+
></div>
|
|
42
|
+
`,
|
|
43
|
+
)
|
|
44
|
+
: html`
|
|
45
|
+
<div
|
|
46
|
+
class=${`h-full rounded-full ${barColor(percent)}`}
|
|
47
|
+
style=${{
|
|
48
|
+
width: `${Math.min(100, percent ?? 0)}%`,
|
|
49
|
+
transition:
|
|
50
|
+
"width 0.8s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.5s ease",
|
|
51
|
+
}}
|
|
52
|
+
></div>
|
|
53
|
+
`}
|
|
54
|
+
</div>
|
|
55
|
+
<div class="flex flex-wrap items-center gap-x-3 mt-2.5">
|
|
56
|
+
<span class="text-xs text-gray-500 font-mono flex-1">${detail}</span>
|
|
57
|
+
${expanded &&
|
|
58
|
+
segments &&
|
|
59
|
+
segments
|
|
60
|
+
.filter((segment) => segment.label)
|
|
61
|
+
.map(
|
|
62
|
+
(segment) => html`
|
|
63
|
+
<span
|
|
64
|
+
class="inline-flex items-center gap-1 text-xs text-gray-500 font-mono"
|
|
65
|
+
>
|
|
66
|
+
<span
|
|
67
|
+
class="inline-block w-1.5 h-1.5 rounded-full"
|
|
68
|
+
style=${{ backgroundColor: segment.color }}
|
|
69
|
+
></span>
|
|
70
|
+
${segment.label}
|
|
71
|
+
</span>
|
|
72
|
+
`,
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
`;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { formatBytes } from "../helpers.js";
|
|
4
|
+
import { ResourceBar } from "../resource-bar.js";
|
|
5
|
+
|
|
6
|
+
const html = htm.bind(h);
|
|
7
|
+
|
|
8
|
+
export const WatchdogResourcesCard = ({
|
|
9
|
+
resources = null,
|
|
10
|
+
memoryExpanded = false,
|
|
11
|
+
onSetMemoryExpanded = () => {},
|
|
12
|
+
}) => {
|
|
13
|
+
if (!resources) return null;
|
|
14
|
+
const memorySegments = (() => {
|
|
15
|
+
const processes = resources.processes;
|
|
16
|
+
const totalBytes = resources.memory?.totalBytes;
|
|
17
|
+
const usedBytes = resources.memory?.usedBytes;
|
|
18
|
+
if (!processes || !totalBytes || !usedBytes) return null;
|
|
19
|
+
const segments = [];
|
|
20
|
+
let trackedBytes = 0;
|
|
21
|
+
if (processes.gateway?.rssBytes != null) {
|
|
22
|
+
trackedBytes += processes.gateway.rssBytes;
|
|
23
|
+
segments.push({
|
|
24
|
+
percent: (processes.gateway.rssBytes / totalBytes) * 100,
|
|
25
|
+
color: "#22d3ee",
|
|
26
|
+
label: `Gateway ${formatBytes(processes.gateway.rssBytes)}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (processes.alphaclaw?.rssBytes != null) {
|
|
30
|
+
trackedBytes += processes.alphaclaw.rssBytes;
|
|
31
|
+
segments.push({
|
|
32
|
+
percent: (processes.alphaclaw.rssBytes / totalBytes) * 100,
|
|
33
|
+
color: "#a78bfa",
|
|
34
|
+
label: `AlphaClaw ${formatBytes(processes.alphaclaw.rssBytes)}`,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const otherBytes = Math.max(0, usedBytes - trackedBytes);
|
|
38
|
+
if (otherBytes > 0) {
|
|
39
|
+
segments.push({
|
|
40
|
+
percent: (otherBytes / totalBytes) * 100,
|
|
41
|
+
color: "#4b5563",
|
|
42
|
+
label: `Other ${formatBytes(otherBytes)}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return segments.length ? segments : null;
|
|
46
|
+
})();
|
|
47
|
+
|
|
48
|
+
return html`
|
|
49
|
+
<div class="bg-surface border border-border rounded-xl p-4">
|
|
50
|
+
${memoryExpanded
|
|
51
|
+
? html`
|
|
52
|
+
<${ResourceBar}
|
|
53
|
+
label="Memory"
|
|
54
|
+
detail=${`${formatBytes(resources.memory?.usedBytes)} / ${formatBytes(resources.memory?.totalBytes)}`}
|
|
55
|
+
percent=${resources.memory?.percent}
|
|
56
|
+
expanded=${true}
|
|
57
|
+
onToggle=${() => onSetMemoryExpanded(false)}
|
|
58
|
+
segments=${memorySegments}
|
|
59
|
+
/>
|
|
60
|
+
`
|
|
61
|
+
: html`
|
|
62
|
+
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4">
|
|
63
|
+
<${ResourceBar}
|
|
64
|
+
label="Memory"
|
|
65
|
+
percent=${resources.memory?.percent}
|
|
66
|
+
detail=${`${formatBytes(resources.memory?.usedBytes)} / ${formatBytes(resources.memory?.totalBytes)}`}
|
|
67
|
+
onToggle=${() => onSetMemoryExpanded(true)}
|
|
68
|
+
/>
|
|
69
|
+
<${ResourceBar}
|
|
70
|
+
label=${`Disk${resources.disk?.path ? ` (${resources.disk.path})` : ""}`}
|
|
71
|
+
percent=${resources.disk?.percent}
|
|
72
|
+
detail=${`${formatBytes(resources.disk?.usedBytes)} / ${formatBytes(resources.disk?.totalBytes)}`}
|
|
73
|
+
/>
|
|
74
|
+
<${ResourceBar}
|
|
75
|
+
label=${`CPU${resources.cpu?.cores ? ` (${resources.cpu.cores} vCPU)` : ""}`}
|
|
76
|
+
percent=${resources.cpu?.percent}
|
|
77
|
+
detail=${resources.cpu?.percent != null
|
|
78
|
+
? `${resources.cpu.percent}%`
|
|
79
|
+
: "—"}
|
|
80
|
+
/>
|
|
81
|
+
</div>
|
|
82
|
+
`}
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useState } from "https://esm.sh/preact/hooks";
|
|
2
|
+
import { usePolling } from "../../../hooks/usePolling.js";
|
|
3
|
+
import { fetchWatchdogResources } from "../../../lib/api.js";
|
|
4
|
+
|
|
5
|
+
export const useWatchdogResources = () => {
|
|
6
|
+
const resourcesPoll = usePolling(() => fetchWatchdogResources(), 5000);
|
|
7
|
+
const [memoryExpanded, setMemoryExpanded] = useState(false);
|
|
8
|
+
return {
|
|
9
|
+
resources: resourcesPoll.data?.resources || null,
|
|
10
|
+
memoryExpanded,
|
|
11
|
+
setMemoryExpanded,
|
|
12
|
+
};
|
|
13
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { InfoTooltip } from "../../info-tooltip.js";
|
|
4
|
+
import { ToggleSwitch } from "../../toggle-switch.js";
|
|
5
|
+
|
|
6
|
+
const html = htm.bind(h);
|
|
7
|
+
|
|
8
|
+
export const WatchdogSettingsCard = ({
|
|
9
|
+
settings = {},
|
|
10
|
+
savingSettings = false,
|
|
11
|
+
onToggleAutoRepair = () => {},
|
|
12
|
+
onToggleNotifications = () => {},
|
|
13
|
+
}) => html`
|
|
14
|
+
<div class="bg-surface border border-border rounded-xl p-4">
|
|
15
|
+
<div class="flex items-center justify-between gap-3">
|
|
16
|
+
<div class="inline-flex items-center gap-2 text-xs text-gray-400">
|
|
17
|
+
<span>Auto-repair</span>
|
|
18
|
+
<${InfoTooltip}
|
|
19
|
+
text="Automatically runs OpenClaw doctor repair when watchdog detects gateway health failures or crash loops."
|
|
20
|
+
/>
|
|
21
|
+
</div>
|
|
22
|
+
<${ToggleSwitch}
|
|
23
|
+
checked=${!!settings.autoRepair}
|
|
24
|
+
disabled=${savingSettings}
|
|
25
|
+
onChange=${onToggleAutoRepair}
|
|
26
|
+
label=""
|
|
27
|
+
/>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="flex items-center justify-between gap-3 mt-3">
|
|
30
|
+
<div class="inline-flex items-center gap-2 text-xs text-gray-400">
|
|
31
|
+
<span>Notifications</span>
|
|
32
|
+
<${InfoTooltip}
|
|
33
|
+
text="Sends channel notices for watchdog alerts and auto-repair outcomes."
|
|
34
|
+
/>
|
|
35
|
+
</div>
|
|
36
|
+
<${ToggleSwitch}
|
|
37
|
+
checked=${!!settings.notificationsEnabled}
|
|
38
|
+
disabled=${savingSettings}
|
|
39
|
+
onChange=${onToggleNotifications}
|
|
40
|
+
label=""
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
`;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useEffect, useState } from "https://esm.sh/preact/hooks";
|
|
2
|
+
import {
|
|
3
|
+
fetchWatchdogSettings,
|
|
4
|
+
triggerWatchdogRepair,
|
|
5
|
+
updateWatchdogSettings,
|
|
6
|
+
} from "../../../lib/api.js";
|
|
7
|
+
import { showToast } from "../../toast.js";
|
|
8
|
+
|
|
9
|
+
export const useWatchdogSettings = ({
|
|
10
|
+
watchdogStatus = null,
|
|
11
|
+
onRefreshStatuses = () => {},
|
|
12
|
+
onRefreshIncidents = () => {},
|
|
13
|
+
} = {}) => {
|
|
14
|
+
const [settings, setSettings] = useState({
|
|
15
|
+
autoRepair: false,
|
|
16
|
+
notificationsEnabled: true,
|
|
17
|
+
});
|
|
18
|
+
const [savingSettings, setSavingSettings] = useState(false);
|
|
19
|
+
const [repairing, setRepairing] = useState(false);
|
|
20
|
+
const isRepairInProgress =
|
|
21
|
+
repairing || !!(watchdogStatus || {})?.operationInProgress;
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
let active = true;
|
|
25
|
+
const loadSettings = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const data = await fetchWatchdogSettings();
|
|
28
|
+
if (!active) return;
|
|
29
|
+
setSettings(
|
|
30
|
+
data.settings || {
|
|
31
|
+
autoRepair: false,
|
|
32
|
+
notificationsEnabled: true,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (!active) return;
|
|
37
|
+
showToast(error.message || "Could not load watchdog settings", "error");
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
loadSettings();
|
|
41
|
+
return () => {
|
|
42
|
+
active = false;
|
|
43
|
+
};
|
|
44
|
+
}, []);
|
|
45
|
+
|
|
46
|
+
const onToggleAutoRepair = async (nextValue) => {
|
|
47
|
+
if (savingSettings) return;
|
|
48
|
+
setSavingSettings(true);
|
|
49
|
+
try {
|
|
50
|
+
const data = await updateWatchdogSettings({ autoRepair: !!nextValue });
|
|
51
|
+
setSettings(
|
|
52
|
+
data.settings || {
|
|
53
|
+
...settings,
|
|
54
|
+
autoRepair: !!nextValue,
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
onRefreshStatuses();
|
|
58
|
+
showToast(`Auto-repair ${nextValue ? "enabled" : "disabled"}`, "success");
|
|
59
|
+
} catch (error) {
|
|
60
|
+
showToast(error.message || "Could not update auto-repair", "error");
|
|
61
|
+
} finally {
|
|
62
|
+
setSavingSettings(false);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const onToggleNotifications = async (nextValue) => {
|
|
67
|
+
if (savingSettings) return;
|
|
68
|
+
setSavingSettings(true);
|
|
69
|
+
try {
|
|
70
|
+
const data = await updateWatchdogSettings({
|
|
71
|
+
notificationsEnabled: !!nextValue,
|
|
72
|
+
});
|
|
73
|
+
setSettings(
|
|
74
|
+
data.settings || {
|
|
75
|
+
...settings,
|
|
76
|
+
notificationsEnabled: !!nextValue,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
onRefreshStatuses();
|
|
80
|
+
showToast(
|
|
81
|
+
`Notifications ${nextValue ? "enabled" : "disabled"}`,
|
|
82
|
+
"success",
|
|
83
|
+
);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
showToast(error.message || "Could not update notifications", "error");
|
|
86
|
+
} finally {
|
|
87
|
+
setSavingSettings(false);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const onRepair = async () => {
|
|
92
|
+
if (isRepairInProgress) return;
|
|
93
|
+
setRepairing(true);
|
|
94
|
+
try {
|
|
95
|
+
const data = await triggerWatchdogRepair();
|
|
96
|
+
if (!data.ok) throw new Error(data.error || "Repair failed");
|
|
97
|
+
showToast("Repair triggered", "success");
|
|
98
|
+
setTimeout(() => {
|
|
99
|
+
onRefreshStatuses();
|
|
100
|
+
onRefreshIncidents();
|
|
101
|
+
}, 800);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
showToast(error.message || "Could not run repair", "error");
|
|
104
|
+
} finally {
|
|
105
|
+
setRepairing(false);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
settings,
|
|
111
|
+
savingSettings,
|
|
112
|
+
isRepairInProgress,
|
|
113
|
+
onToggleAutoRepair,
|
|
114
|
+
onToggleNotifications,
|
|
115
|
+
onRepair,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
|
|
4
|
+
const html = htm.bind(h);
|
|
5
|
+
|
|
6
|
+
export const WatchdogTerminal = ({
|
|
7
|
+
panelRef = null,
|
|
8
|
+
hostRef = null,
|
|
9
|
+
terminalInstanceRef = null,
|
|
10
|
+
panelHeightPx = 320,
|
|
11
|
+
}) => html`
|
|
12
|
+
<div
|
|
13
|
+
ref=${panelRef}
|
|
14
|
+
class="watchdog-logs-panel bg-black/40 border border-border rounded-lg p-3 overflow-hidden"
|
|
15
|
+
style=${{ height: `${panelHeightPx}px` }}
|
|
16
|
+
onClick=${() => terminalInstanceRef?.current?.focus()}
|
|
17
|
+
>
|
|
18
|
+
<div ref=${hostRef} class="watchdog-terminal-host w-full h-full"></div>
|
|
19
|
+
</div>
|
|
20
|
+
`;
|