same version for everyone this time. This makes stuff easy
This commit is contained in:
Generated
+5
-5
@@ -3701,7 +3701,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wakey"
|
name = "wakey"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -3722,7 +3722,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wakey-agent"
|
name = "wakey-agent"
|
||||||
version = "0.1.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@@ -3746,7 +3746,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wakey-control-plane"
|
name = "wakey-control-plane"
|
||||||
version = "0.1.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@@ -3774,7 +3774,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wakey-core"
|
name = "wakey-core"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"macaddr",
|
"macaddr",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -3785,7 +3785,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wakey-linux"
|
name = "wakey-linux"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures",
|
"futures",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wakey"
|
name = "wakey"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,7 @@ export function AppLayout() {
|
|||||||
<div className="mx-auto max-w-7xl p-4">
|
<div className="mx-auto max-w-7xl p-4">
|
||||||
<header className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
<header className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-semibold tracking-tight">
|
<h1 className="text-2xl font-semibold tracking-tight">Wakey</h1>
|
||||||
Wakey
|
|
||||||
</h1>
|
|
||||||
<p className="mt-1 text-sm text-muted-foreground">
|
<p className="mt-1 text-sm text-muted-foreground">
|
||||||
Fleet devices, presence, and wake controls
|
Fleet devices, presence, and wake controls
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -198,7 +198,8 @@ export function DevicesPage({ agents, onAfterWake }: Props) {
|
|||||||
if (sort.key === "last_seen") {
|
if (sort.key === "last_seen") {
|
||||||
return dir * ((a.last_seen_unix ?? 0) - (b.last_seen_unix ?? 0));
|
return dir * ((a.last_seen_unix ?? 0) - (b.last_seen_unix ?? 0));
|
||||||
}
|
}
|
||||||
if (sort.key === "agents") return dir * (a.agents.length - b.agents.length);
|
if (sort.key === "agents")
|
||||||
|
return dir * (a.agents.length - b.agents.length);
|
||||||
if (sort.key === "ip") {
|
if (sort.key === "ip") {
|
||||||
return dir * (a.ips[0] ?? "").localeCompare(b.ips[0] ?? "");
|
return dir * (a.ips[0] ?? "").localeCompare(b.ips[0] ?? "");
|
||||||
}
|
}
|
||||||
@@ -220,7 +221,8 @@ export function DevicesPage({ agents, onAfterWake }: Props) {
|
|||||||
<div>
|
<div>
|
||||||
<CardTitle>Devices</CardTitle>
|
<CardTitle>Devices</CardTitle>
|
||||||
<p className="mt-1 text-sm text-muted-foreground">
|
<p className="mt-1 text-sm text-muted-foreground">
|
||||||
{devices.length} fleet rows from stored observations and known devices
|
{devices.length} fleet rows from stored observations and known
|
||||||
|
devices
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
@@ -239,7 +241,9 @@ export function DevicesPage({ agents, onAfterWake }: Props) {
|
|||||||
disabled={refreshing || connectedCount === 0}
|
disabled={refreshing || connectedCount === 0}
|
||||||
>
|
>
|
||||||
<RefreshCw className="size-4" aria-hidden />
|
<RefreshCw className="size-4" aria-hidden />
|
||||||
{refreshing ? "Collecting" : `Collect fleet (${connectedCount})`}
|
{refreshing
|
||||||
|
? "Collecting"
|
||||||
|
: `Collect fleet (${connectedCount})`}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -272,9 +276,20 @@ export function DevicesPage({ agents, onAfterWake }: Props) {
|
|||||||
/>
|
/>
|
||||||
<label className="grid gap-1 text-sm text-muted-foreground">
|
<label className="grid gap-1 text-sm text-muted-foreground">
|
||||||
<span>Agent</span>
|
<span>Agent</span>
|
||||||
<Select value={agentId} onValueChange={(value) => setAgentId(value ?? "all")}>
|
<Select
|
||||||
|
value={agentId}
|
||||||
|
onValueChange={(value) => setAgentId(value ?? "all")}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<span>{agentId === "all" ? "all agents" : agentLabel(agentId, agents.find((agent) => agent.agent_id === agentId)?.nickname)}</span>
|
<span>
|
||||||
|
{agentId === "all"
|
||||||
|
? "all agents"
|
||||||
|
: agentLabel(
|
||||||
|
agentId,
|
||||||
|
agents.find((agent) => agent.agent_id === agentId)
|
||||||
|
?.nickname,
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent alignItemWithTrigger={false}>
|
<SelectContent alignItemWithTrigger={false}>
|
||||||
<SelectItem value="all">all agents</SelectItem>
|
<SelectItem value="all">all agents</SelectItem>
|
||||||
@@ -413,7 +428,11 @@ function FleetDeviceRow({
|
|||||||
</span>
|
</span>
|
||||||
<span className="min-w-0 text-muted-foreground xl:truncate">
|
<span className="min-w-0 text-muted-foreground xl:truncate">
|
||||||
<MobileLabel label="Agents" />
|
<MobileLabel label="Agents" />
|
||||||
{summarize(device.agents.map((agent) => agentLabel(agent.agent_id, agent.nickname)))}
|
{summarize(
|
||||||
|
device.agents.map((agent) =>
|
||||||
|
agentLabel(agent.agent_id, agent.nickname),
|
||||||
|
),
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">
|
||||||
<MobileLabel label="Last seen" />
|
<MobileLabel label="Last seen" />
|
||||||
@@ -544,9 +563,13 @@ function FleetDeviceDetailsDialog({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<div className="flex min-w-0 items-start justify-between gap-3 pr-8">
|
<div className="flex min-w-0 items-start justify-between gap-3 pr-8">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<DialogTitle className="truncate">{device.display_name}</DialogTitle>
|
<DialogTitle className="truncate">
|
||||||
|
{device.display_name}
|
||||||
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
{device.known_device ? "Remembered fleet device" : "Observed fleet device"}
|
{device.known_device
|
||||||
|
? "Remembered fleet device"
|
||||||
|
: "Observed fleet device"}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</div>
|
</div>
|
||||||
<PresenceBadge presence={device.presence} />
|
<PresenceBadge presence={device.presence} />
|
||||||
@@ -562,10 +585,16 @@ function FleetDeviceDetailsDialog({
|
|||||||
<div className="grid gap-3 md:grid-cols-2">
|
<div className="grid gap-3 md:grid-cols-2">
|
||||||
<DetailBlock label="IPs" values={device.ips} onCopy={onCopy} />
|
<DetailBlock label="IPs" values={device.ips} onCopy={onCopy} />
|
||||||
<DetailBlock label="MACs" values={device.macs} onCopy={onCopy} />
|
<DetailBlock label="MACs" values={device.macs} onCopy={onCopy} />
|
||||||
<DetailBlock label="Hostnames" values={device.hostnames} onCopy={onCopy} />
|
<DetailBlock
|
||||||
|
label="Hostnames"
|
||||||
|
values={device.hostnames}
|
||||||
|
onCopy={onCopy}
|
||||||
|
/>
|
||||||
<DetailBlock
|
<DetailBlock
|
||||||
label="Agents"
|
label="Agents"
|
||||||
values={device.agents.map((agent) => agentLabel(agent.agent_id, agent.nickname))}
|
values={device.agents.map((agent) =>
|
||||||
|
agentLabel(agent.agent_id, agent.nickname),
|
||||||
|
)}
|
||||||
onCopy={onCopy}
|
onCopy={onCopy}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -584,7 +613,10 @@ function FleetDeviceDetailsDialog({
|
|||||||
{busy ? "Waking" : "Wake"}
|
{busy ? "Waking" : "Wake"}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Select value={routeId} onValueChange={(value) => setRouteId(value ?? "")}>
|
<Select
|
||||||
|
value={routeId}
|
||||||
|
onValueChange={(value) => setRouteId(value ?? "")}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<span>
|
<span>
|
||||||
{selectedRoute
|
{selectedRoute
|
||||||
@@ -613,21 +645,32 @@ function FleetDeviceDetailsDialog({
|
|||||||
onChange={(event) => setDisplayName(event.target.value)}
|
onChange={(event) => setDisplayName(event.target.value)}
|
||||||
placeholder="Device name"
|
placeholder="Device name"
|
||||||
/>
|
/>
|
||||||
<Button onClick={() => void createRemembered()} disabled={!identifiers.length}>
|
<Button
|
||||||
|
onClick={() => void createRemembered()}
|
||||||
|
disabled={!identifiers.length}
|
||||||
|
>
|
||||||
Remember device
|
Remember device
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="grid gap-2 md:grid-cols-[minmax(0,1fr)_auto_auto]">
|
<div className="grid gap-2 md:grid-cols-[minmax(0,1fr)_auto_auto]">
|
||||||
<Select value={targetDeviceId} onValueChange={(value) => setTargetDeviceId(value ?? "")}>
|
<Select
|
||||||
|
value={targetDeviceId}
|
||||||
|
onValueChange={(value) => setTargetDeviceId(value ?? "")}
|
||||||
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<span>
|
<span>
|
||||||
{targetDeviceId
|
{targetDeviceId
|
||||||
? knownDevices.find((known) => known.device_id === targetDeviceId)?.display_name
|
? knownDevices.find(
|
||||||
|
(known) => known.device_id === targetDeviceId,
|
||||||
|
)?.display_name
|
||||||
: "Select known device"}
|
: "Select known device"}
|
||||||
</span>
|
</span>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent className="max-w-[min(92vw,30rem)]" alignItemWithTrigger={false}>
|
<SelectContent
|
||||||
|
className="max-w-[min(92vw,30rem)]"
|
||||||
|
alignItemWithTrigger={false}
|
||||||
|
>
|
||||||
{otherKnownDevices.map((known) => (
|
{otherKnownDevices.map((known) => (
|
||||||
<SelectItem key={known.device_id} value={known.device_id}>
|
<SelectItem key={known.device_id} value={known.device_id}>
|
||||||
{known.display_name}
|
{known.display_name}
|
||||||
@@ -651,7 +694,9 @@ function FleetDeviceDetailsDialog({
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Proposed identifiers: {identifiers.map((id) => `${id.kind}:${id.value}`).join(", ") || "-"}
|
Proposed identifiers:{" "}
|
||||||
|
{identifiers.map((id) => `${id.kind}:${id.value}`).join(", ") ||
|
||||||
|
"-"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -719,7 +764,10 @@ function DetailBlock({
|
|||||||
onClick={() => onCopy(label, value)}
|
onClick={() => onCopy(label, value)}
|
||||||
>
|
>
|
||||||
<span className="min-w-0 truncate">{value}</span>
|
<span className="min-w-0 truncate">{value}</span>
|
||||||
<Copy className="size-3.5 shrink-0 text-muted-foreground" aria-hidden />
|
<Copy
|
||||||
|
className="size-3.5 shrink-0 text-muted-foreground"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
@@ -771,7 +819,9 @@ function presenceRank(presence: string): number {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function identifiersFor(device: FleetDevice): { kind: string; value: string }[] {
|
function identifiersFor(
|
||||||
|
device: FleetDevice,
|
||||||
|
): { kind: string; value: string }[] {
|
||||||
return [
|
return [
|
||||||
...device.macs.map((value) => ({ kind: "mac", value })),
|
...device.macs.map((value) => ({ kind: "mac", value })),
|
||||||
...device.ips.map((value) => ({ kind: "ip", value })),
|
...device.ips.map((value) => ({ kind: "ip", value })),
|
||||||
@@ -785,6 +835,8 @@ function agentLabel(agentId: string, nickname?: string | null): string {
|
|||||||
|
|
||||||
function routeLabel(route: FleetWakeRoute): string {
|
function routeLabel(route: FleetWakeRoute): string {
|
||||||
const status = route.connected ? "connected" : "offline";
|
const status = route.connected ? "connected" : "offline";
|
||||||
const target = route.mac ? `${route.mac}${route.ip ? ` / ${route.ip}` : ""}` : route.ip ?? "-";
|
const target = route.mac
|
||||||
|
? `${route.mac}${route.ip ? ` / ${route.ip}` : ""}`
|
||||||
|
: (route.ip ?? "-");
|
||||||
return `${agentLabel(route.agent_id, route.nickname)} · ${target} · ${route.source} · ${status}`;
|
return `${agentLabel(route.agent_id, route.nickname)} · ${target} · ${route.source} · ${status}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wakey-agent"
|
name = "wakey-agent"
|
||||||
version = "0.1.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wakey-control-plane"
|
name = "wakey-control-plane"
|
||||||
version = "0.1.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wakey-core"
|
name = "wakey-core"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "wakey-linux"
|
name = "wakey-linux"
|
||||||
version = "0.2.2"
|
version = "0.2.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
publish = ["gitea"]
|
publish = ["gitea"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user