Files
buzz/desktop/src/features/agents/managedAgentRuntimeStatus.ts
Arjun Mahanti 38115b7aec fix(desktop): preserve gated Bestie identity
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: Arjun Mahanti <arjun@squareup.com>
2026-08-28 10:56:46 -04:00

118 lines
3.8 KiB
TypeScript

import type { ManagedAgentRuntimeStatus } from "@/shared/api/types";
export type AgentCommunityAvailability =
| "Here"
| "Waking"
| "Needs setup on this device"
| "Unavailable";
export function agentCommunityAvailability(
runtime: ManagedAgentRuntimeStatus,
): AgentCommunityAvailability {
if (!runtime.localSetup) return "Needs setup on this device";
switch (runtime.lifecycle) {
case "starting":
case "listening":
case "waking":
return "Waking";
case "ready":
return "Here";
case "failed":
case "stopped":
return "Unavailable";
}
}
export function agentCommunityStatusDetail(
runtime: ManagedAgentRuntimeStatus,
): string | null {
if (!runtime.localSetup)
return "Set up this agent on this device to start it.";
if (runtime.lifecycle === "stopped") return "Stopped by you";
if (runtime.lifecycle === "failed")
return runtime.error ?? "Could not connect";
return null;
}
export function managedAgentRuntimeKey(
runtime: Pick<ManagedAgentRuntimeStatus, "pubkey" | "relayUrl">,
): string {
return JSON.stringify([runtime.pubkey, runtime.relayUrl]);
}
export type ManagedAgentPairAction = "start" | "stop" | "restart";
/** Menu action for one agent+community pair. A missing runtime row means the
* pair is not running here, so the only sensible action is to start it. */
export function managedAgentPairAction(
runtime: ManagedAgentRuntimeStatus | undefined,
): ManagedAgentPairAction {
if (!runtime || runtime.lifecycle === "stopped") return "start";
if (runtime.lifecycle === "failed") return "restart";
return "stop";
}
export const MANAGED_AGENT_PAIR_ACTION_LABELS: Record<
ManagedAgentPairAction,
string
> = {
start: "Start Agent",
stop: "Stop Agent",
restart: "Restart Agent",
};
/**
* Canonicalize a relay URL the way the backend keys runtime pairs, so a
* stored community URL (e.g. `ws://localhost:3000`) matches backend rows
* (`ws://127.0.0.1:3000`). Mirrors buzz-core's `normalize_relay_url`
* (`crates/buzz-core/src/relay.rs`): lowercase host, loopback hosts folded
* to 127.0.0.1, default ports and root-path trailing slash stripped.
* Returns null when the URL cannot be parsed as ws/wss.
*/
export function canonicalRelayUrl(raw: string): string | null {
let url: URL;
try {
url = new URL(raw.trim());
} catch {
return null;
}
if (url.protocol !== "ws:" && url.protocol !== "wss:") return null;
if (url.username !== "" || url.password !== "" || url.hash !== "") {
return null;
}
if (url.hostname === "") return null;
let host = url.hostname.toLowerCase();
if (host === "localhost" || host === "[::1]" || host.startsWith("127.")) {
host = "127.0.0.1";
}
const defaultPort = url.protocol === "ws:" ? "80" : "443";
const port = url.port && url.port !== defaultPort ? `:${url.port}` : "";
const path = url.pathname === "/" ? "" : url.pathname;
// The backend trims trailing slashes from the final rendered URL.
return `${url.protocol}//${host}${port}${path}${url.search}`.replace(
/\/+$/,
"",
);
}
export function findManagedAgentRuntime(
runtimes: readonly ManagedAgentRuntimeStatus[],
pubkey: string,
relayUrl: string,
): ManagedAgentRuntimeStatus | undefined {
const normalizedPubkey = pubkey.toLowerCase();
// Backend rows carry the canonical pair URL; the caller passes the
// community's stored URL, which may differ in spelling (localhost vs
// 127.0.0.1, default port, trailing slash). Compare canonically, keeping
// the exact-string checks as a fallback for unparsable stored URLs.
const canonical = canonicalRelayUrl(relayUrl);
return runtimes.find(
(runtime) =>
runtime.pubkey.toLowerCase() === normalizedPubkey &&
(runtime.relayUrl === relayUrl ||
runtime.requestedRelayUrl === relayUrl ||
(canonical !== null && runtime.relayUrl === canonical)),
);
}