docs(reference): import Dune: Awakening server-manager references
All checks were successful
CI / backend-types (push) Successful in 10s
CI / frontend-build (push) Successful in 15s
CI / agent-tests (push) Successful in 39s
CI / integration (push) Successful in 22s

Phase 2 references for the host-agent Dune adapter, moved out of volatile /tmp
into docs/reference-repos/ (per Commander). Three upstream projects, .git +
node_modules + compiled binaries stripped (16MB source). Nested AI-instruction
files (.claude/, CLAUDE.md) removed so they don't pollute Corrosion sessions.

- icehunter/    dune-admin (Go+React) — 4 control planes; SETUP_DOCKER.md is the
                closest analog to our agent's Dune docker control plane (compose
                lifecycle, docker logs, RabbitMQ-via-exec, dune Postgres schema)
- adainrivers/  Rust/Tauri desktop — SSH+k8s BattleGroup control, maintenance
                daemon, in-game admin console (Rust idiom reference)
- the4rchangel/ Node web UI replacing battlegroup.bat — matches the Commander's
                Hyper-V self-host path + game-config schema

See docs/reference-repos/README.md for the full index + how we use each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-06-11 21:08:05 -04:00
parent 0715492ddf
commit 651a35d4be
1334 changed files with 238971 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
import { invoke } from "@tauri-apps/api/core";
import type {
ClusterDto,
CommandSpec,
CronPreviewResult,
DumpPruneItem,
DumpPruneResult,
DumpPruneTarget,
HealthDto,
HistoryDto,
ItemDto,
LogDto,
ManagementConnRequest,
ManagementInstallRequest,
ManagementInstallResult,
ManagementServiceStatus,
PlayerDto,
PlayerLocationDto,
PublishResultDto,
RunDto,
JourneyNodeDto,
ScheduleConfig,
ScheduleConfigUpdate,
SkillModuleDto,
VehicleDto,
WelcomeGrantDto,
XpEventTagDto,
} from "../types/management";
export const managementService = {
install: (req: ManagementInstallRequest) =>
invoke<ManagementInstallResult>("install_management_service", { request: req }),
uninstall: (req: ManagementConnRequest) =>
invoke<void>("uninstall_management_service", { request: req }),
status: (req: ManagementConnRequest) =>
invoke<ManagementServiceStatus>("management_service_status", { request: req }),
bundledVersion: () => invoke<string>("management_service_bundled_version"),
restart: (req: ManagementConnRequest) =>
invoke<void>("restart_management_service", { request: req }),
};
export const managementApi = {
health: (tunnelId: string) => invoke<HealthDto>("ms_health", { tunnelId }),
listRuns: (tunnelId: string, limit?: number, task?: string) =>
invoke<RunDto[]>("ms_list_runs", { tunnelId, limit, task }),
listLogs: (tunnelId: string, limit?: number, runId?: number) =>
invoke<LogDto[]>("ms_list_logs", { tunnelId, limit, runId }),
triggerRun: (tunnelId: string, task: string, options?: Record<string, unknown>) =>
invoke<{ ok: boolean; task: string }>("ms_trigger_run", { tunnelId, task, options }),
listCommands: (tunnelId: string) =>
invoke<CommandSpec[]>("ms_list_commands", { tunnelId }),
searchItems: (tunnelId: string, q: string, limit?: number) =>
invoke<ItemDto[]>("ms_search_items", { tunnelId, q, limit }),
searchVehicles: (tunnelId: string, q: string, limit?: number) =>
invoke<VehicleDto[]>("ms_search_vehicles", { tunnelId, q, limit }),
searchSkillModules: (tunnelId: string, q: string, limit?: number) =>
invoke<SkillModuleDto[]>("ms_search_skill_modules", { tunnelId, q, limit }),
searchJourneyNodes: (tunnelId: string, q: string, limit?: number) =>
invoke<JourneyNodeDto[]>("ms_search_journey_nodes", { tunnelId, q, limit }),
searchXpEventTags: (tunnelId: string, q: string, limit?: number) =>
invoke<XpEventTagDto[]>("ms_search_xp_event_tags", { tunnelId, q, limit }),
getConfig: (tunnelId: string) => invoke<ScheduleConfig>("ms_get_config", { tunnelId }),
setConfig: (tunnelId: string, config: ScheduleConfigUpdate) =>
invoke<{ ok: boolean }>("ms_set_config", { tunnelId, config }),
listTimezones: (tunnelId: string) => invoke<string[]>("ms_list_timezones", { tunnelId }),
cronPreview: (tunnelId: string, expr: string, count?: number) =>
invoke<CronPreviewResult>("ms_cron_preview", { tunnelId, expr, count }),
dumpPrunePreview: (tunnelId: string) =>
invoke<DumpPruneItem[]>("ms_dump_prune_preview", { tunnelId }),
dumpPruneExecute: (tunnelId: string, items: DumpPruneTarget[]) =>
invoke<DumpPruneResult>("ms_dump_prune_execute", { tunnelId, items }),
searchPlayers: (tunnelId: string, q: string, limit?: number) =>
invoke<PlayerDto[]>("ms_search_players", { tunnelId, q, limit }),
playerLocation: (tunnelId: string, flsId: string) =>
invoke<PlayerLocationDto>("ms_player_location", { tunnelId, flsId }),
cluster: (tunnelId: string) => invoke<ClusterDto>("ms_cluster", { tunnelId }),
history: (tunnelId: string, limit?: number) =>
invoke<HistoryDto[]>("ms_history", { tunnelId, limit }),
welcomeGrants: (tunnelId: string, limit?: number) =>
invoke<WelcomeGrantDto[]>("ms_welcome_grants", { tunnelId, limit }),
retryWelcomeGrant: (
tunnelId: string,
playerId: string,
packageVersion: string,
accountId: number,
) =>
invoke<{ ok: boolean; removed: number }>("ms_welcome_grant_retry", {
tunnelId,
playerId,
packageVersion,
accountId,
}),
sendWelcomeWhisper: (
tunnelId: string,
recipientPlayerId: string,
sourcePlayerId: string,
message: string,
) =>
invoke<PublishResultDto>("ms_welcome_whisper", {
tunnelId,
recipientPlayerId,
sourcePlayerId,
message,
}),
publish: (tunnelId: string, command: string, fields: Record<string, unknown>) =>
invoke<PublishResultDto>("ms_publish", { tunnelId, command, fields }),
};

View File

@@ -0,0 +1,142 @@
import type { RemoteServerRecord } from "../types/server";
import type { CustomTunnelDef } from "../types/tunnel";
import type { ActivePage, ServerSubPage } from "../types/ui";
import { SERVER_SUB_PAGES } from "../types/ui";
const remoteServersStorageKey = "dune-manager.remote-servers";
const activePageStorageKey = "dune-manager.active-page";
const logSidebarStorageKey = "dune-manager.log-sidebar";
export function isRemoteServerRecord(value: unknown): value is RemoteServerRecord {
if (!value || typeof value !== "object") return false;
const record = value as Partial<RemoteServerRecord>;
return (
record.type === "ubuntu" &&
typeof record.id === "string" &&
typeof record.name === "string" &&
typeof record.host === "string" &&
typeof record.keyPath === "string"
);
}
export function readRemoteServers(): RemoteServerRecord[] {
const text = window.localStorage.getItem(remoteServersStorageKey);
if (!text) return [];
try {
const parsed = JSON.parse(text);
if (!Array.isArray(parsed)) return [];
return parsed.filter(isRemoteServerRecord);
} catch {
window.localStorage.removeItem(remoteServersStorageKey);
return [];
}
}
export function mergeRemoteServers(
current: RemoteServerRecord[],
incoming: RemoteServerRecord[],
): RemoteServerRecord[] {
const byId = new Map(current.map((server) => [server.id, server]));
for (const server of incoming) {
byId.set(server.id, { ...byId.get(server.id), ...server });
}
return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name));
}
export function persistRemoteServers(servers: RemoteServerRecord[]): RemoteServerRecord[] {
const unique = mergeRemoteServers([], servers);
window.localStorage.setItem(remoteServersStorageKey, JSON.stringify(unique));
return unique;
}
export function upsertRemoteServer(
servers: RemoteServerRecord[],
server: RemoteServerRecord,
): RemoteServerRecord[] {
return mergeRemoteServers(servers, [server]);
}
type PersistedActivePage = { activeServerId?: string; activeSub?: ServerSubPage };
function isServerSubPage(value: unknown): value is ServerSubPage {
return typeof value === "string" && (SERVER_SUB_PAGES as readonly string[]).includes(value);
}
export function readActivePage(attachedServerIds: string[]): ActivePage {
const text = window.localStorage.getItem(activePageStorageKey);
if (!text) return { kind: "servers" };
try {
const parsed = JSON.parse(text) as PersistedActivePage;
const id = parsed?.activeServerId;
if (!id || !attachedServerIds.includes(id)) return { kind: "servers" };
const sub = isServerSubPage(parsed?.activeSub) ? parsed.activeSub : "dashboard";
return { kind: "server", serverId: id, sub };
} catch {
window.localStorage.removeItem(activePageStorageKey);
return { kind: "servers" };
}
}
export function writeActivePage(page: ActivePage): void {
if (page.kind === "servers") {
window.localStorage.removeItem(activePageStorageKey);
return;
}
const payload: PersistedActivePage = { activeServerId: page.serverId, activeSub: page.sub };
window.localStorage.setItem(activePageStorageKey, JSON.stringify(payload));
}
type PersistedLogSidebar = { collapsed?: boolean; scopeToActiveServer?: boolean };
export function readLogSidebar(): PersistedLogSidebar {
const text = window.localStorage.getItem(logSidebarStorageKey);
if (!text) return {};
try {
const parsed = JSON.parse(text) as PersistedLogSidebar;
return {
collapsed: typeof parsed.collapsed === "boolean" ? parsed.collapsed : undefined,
scopeToActiveServer:
typeof parsed.scopeToActiveServer === "boolean" ? parsed.scopeToActiveServer : undefined,
};
} catch {
window.localStorage.removeItem(logSidebarStorageKey);
return {};
}
}
export function writeLogSidebar(state: PersistedLogSidebar): void {
window.localStorage.setItem(logSidebarStorageKey, JSON.stringify(state));
}
function customTunnelsKey(serverId: string): string {
return `dune-manager.custom-tunnels.${serverId}`;
}
function isCustomTunnelDef(value: unknown): value is CustomTunnelDef {
if (!value || typeof value !== "object") return false;
const d = value as Partial<CustomTunnelDef>;
return (
typeof d.id === "string" &&
typeof d.name === "string" &&
(d.protocol === "http" || d.protocol === "https" || d.protocol === "postgresql") &&
typeof d.remotePort === "number" &&
typeof d.localPort === "number"
);
}
export function readCustomTunnels(serverId: string): CustomTunnelDef[] {
const text = window.localStorage.getItem(customTunnelsKey(serverId));
if (!text) return [];
try {
const parsed = JSON.parse(text);
if (!Array.isArray(parsed)) return [];
return parsed.filter(isCustomTunnelDef);
} catch {
window.localStorage.removeItem(customTunnelsKey(serverId));
return [];
}
}
export function writeCustomTunnels(serverId: string, defs: CustomTunnelDef[]): void {
window.localStorage.setItem(customTunnelsKey(serverId), JSON.stringify(defs));
}

View File

@@ -0,0 +1,170 @@
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { open as openShell } from "@tauri-apps/plugin-shell";
import { relaunch as relaunchProcess } from "@tauri-apps/plugin-process";
import type {
RemoteComponentLogResult,
RemoteComponentRestartResult,
} from "../types/component";
import type {
RemoteServerComponent,
RemoteServerKind,
RemoteServerRecord,
RemoteServerStatus,
} from "../types/server";
import type { CustomTunnelStartRequest, ServerTunnelStartRequest, ServerTunnelStatus } from "../types/tunnel";
type RemoteActionRequest = {
serverType: RemoteServerKind;
host: string;
user: string;
keyPath?: string;
port?: number;
namespace: string;
battlegroupName: string;
};
type DetectRemoteServersRequest = {
host: string;
keyPath: string;
serverType: RemoteServerKind;
user: string;
port?: number;
};
type RemoteComponentLogRequest = {
serverType: RemoteServerKind;
host: string;
user: string;
keyPath?: string;
port?: number;
namespace: string;
component: string;
tail: number;
};
type RemoteComponentRestartRequest = {
serverType: RemoteServerKind;
host: string;
user: string;
keyPath?: string;
port?: number;
namespace: string;
component: string;
};
export async function detectRemoteUbuntuServers(
request: DetectRemoteServersRequest,
): Promise<RemoteServerRecord[]> {
return invoke<RemoteServerRecord[]>("detect_remote_ubuntu_servers", { request });
}
export async function getRemoteServerStatus(request: RemoteActionRequest): Promise<RemoteServerStatus> {
return invoke<RemoteServerStatus>("remote_server_status", { request });
}
export async function getRemoteServerComponents(
request: RemoteActionRequest,
): Promise<RemoteServerComponent[]> {
return invoke<RemoteServerComponent[]>("remote_server_components", { request });
}
export async function startRemoteBattlegroup(request: RemoteActionRequest): Promise<RemoteServerStatus> {
return invoke<RemoteServerStatus>("start_remote_battlegroup", { request });
}
export async function stopRemoteBattlegroup(request: RemoteActionRequest): Promise<RemoteServerStatus> {
return invoke<RemoteServerStatus>("stop_remote_battlegroup", { request });
}
export async function updateRemoteBattlegroup(request: RemoteActionRequest): Promise<RemoteServerStatus> {
return invoke<RemoteServerStatus>("update_remote_battlegroup", { request });
}
export async function restartRemoteBattlegroup(request: RemoteActionRequest): Promise<RemoteServerStatus> {
return invoke<RemoteServerStatus>("restart_remote_battlegroup", { request });
}
export async function startServerTunnel(request: ServerTunnelStartRequest): Promise<ServerTunnelStatus> {
return invoke<ServerTunnelStatus>("start_server_tunnel", { request });
}
export async function startCustomTunnel(request: CustomTunnelStartRequest): Promise<ServerTunnelStatus> {
return invoke<ServerTunnelStatus>("start_custom_tunnel", { request });
}
export async function stopServerTunnel(tunnelId: string): Promise<void> {
await invoke("stop_server_tunnel", { request: { tunnelId } });
}
export async function serverTunnelStatus(tunnelId: string): Promise<ServerTunnelStatus | null> {
return invoke<ServerTunnelStatus | null>("server_tunnel_status", { request: { tunnelId } });
}
export async function stopAllTunnels(): Promise<void> {
await invoke("stop_all_tunnels");
}
export async function remoteComponentLogTail(
request: RemoteComponentLogRequest,
): Promise<RemoteComponentLogResult> {
return invoke<RemoteComponentLogResult>("remote_component_log_tail", { request });
}
export async function restartRemoteComponent(
request: RemoteComponentRestartRequest,
): Promise<RemoteComponentRestartResult> {
return invoke<RemoteComponentRestartResult>("restart_remote_component", { request });
}
export function listenToEvent<T>(
channel: string,
handler: (payload: T) => void,
): Promise<UnlistenFn> {
return listen<T>(channel, (event) => handler(event.payload));
}
export async function openFileDialog(title: string): Promise<string | null> {
const selected = await openDialog({ directory: false, multiple: false, title });
return typeof selected === "string" ? selected : null;
}
export async function openExternal(url: string): Promise<void> {
await openShell(url);
}
export async function relaunch(): Promise<void> {
await relaunchProcess();
}
export type PreflightCheck = {
sshOk: boolean;
sudoToDuneOk: boolean;
duneNopasswdOk: boolean;
isDuneLogin: boolean;
rawOutput: string;
};
export async function checkRemoteSudo(request: {
host: string;
user: string;
keyPath: string;
port?: number;
}): Promise<PreflightCheck> {
return invoke<PreflightCheck>("check_remote_sudo", { request });
}
export async function recordOperationLog(level: string, scope: string, message: string): Promise<void> {
await invoke("record_operation_log", { level, scope, message });
}
export async function getLogsFolder(): Promise<string> {
return invoke<string>("get_logs_folder");
}
export async function openLogsFolder(): Promise<void> {
const path = await getLogsFolder();
if (path) await openShell(path);
}

View File

@@ -0,0 +1,15 @@
import { check, type DownloadEvent, type Update } from "@tauri-apps/plugin-updater";
export type { DownloadEvent, Update } from "@tauri-apps/plugin-updater";
export async function checkForUpdate(timeoutMs = 15_000): Promise<Update | null> {
return check({ timeout: timeoutMs });
}
export async function downloadAndInstallUpdate(
update: Update,
onEvent: (event: DownloadEvent) => void,
timeoutMs = 120_000,
): Promise<void> {
await update.downloadAndInstall(onEvent, { timeout: timeoutMs });
}