Files
kandev/apps/web/lib/settings-discovery/resolve.ts
T
2026-08-05 14:07:52 +01:00

220 lines
7.2 KiB
TypeScript

import type {
ResolvedSettingsDiscoveryItem,
SettingsDiscoveryContext,
SettingsDiscoveryDefinition,
} from "./types";
import { SETTINGS_DISCOVERY_DEFINITIONS } from "./catalog";
import { SETTINGS_DISCOVERY_GROUPS } from "./catalog/groups";
import {
agentProfileDiscoveryTarget,
executorProfileDiscoveryTarget,
workspaceDiscoveryTarget,
} from "./dynamic-targets";
function workspaceDefinitions(
workspaces: SettingsDiscoveryContext["workspaces"],
): SettingsDiscoveryDefinition[] {
const entries: SettingsDiscoveryDefinition[] = [];
for (const [workspaceIndex, workspace] of workspaces.entries()) {
const workspaceId = `workspace:${workspace.id}`;
const href = `/settings/workspace/${encodeURIComponent(workspace.id)}`;
const order = 10_000 + workspaceIndex * 100;
entries.push({
id: workspaceId,
kind: "page",
label: workspace.name,
parentId: "workspaces",
groupId: "workspaces",
href,
order,
});
for (const [index, [suffix, labelKey]] of [
["name", "workspaces:name"],
["default-executor", "workspaces:defaultExecutor"],
["default-agent-profile", "workspaces:defaultAgentProfile"],
].entries()) {
entries.push({
id: `${workspaceId}:${suffix}`,
kind: "control",
labelKey,
parentId: workspaceId,
groupId: "workspaces",
href,
targetId: workspaceDiscoveryTarget(
workspace.id,
suffix as "name" | "default-executor" | "default-agent-profile",
),
order: order + index + 1,
});
}
for (const [index, [suffix, labelKey]] of [
["repositories", "sidebar:repositories"],
["workflows", "workflows:workflows"],
["automations", "common:automations"],
["integrations", "common:integrations"],
].entries()) {
entries.push({
id: `${workspaceId}:${suffix}`,
kind: "page",
labelKey,
parentId: workspaceId,
groupId: "workspaces",
href: `${href}/${suffix}`,
order: order + index + 10,
});
}
}
return entries;
}
function agentProfileDefinitions(
agents: SettingsDiscoveryContext["agents"],
): SettingsDiscoveryDefinition[] {
const entries: SettingsDiscoveryDefinition[] = [];
for (const [agentIndex, agent] of agents.entries()) {
for (const [profileIndex, profile] of agent.profiles.entries()) {
const profileId = `agent-profile:${profile.id}`;
const href = `/settings/agents/${encodeURIComponent(agent.name)}/profiles/${encodeURIComponent(profile.id)}`;
const order = 20_000 + agentIndex * 100 + profileIndex * 10;
entries.push({
id: profileId,
kind: "page",
label: `${profile.agentDisplayName || agent.name} • ${profile.name}`,
parentId: "agents",
groupId: "agents",
href,
order,
});
for (const [index, [suffix, labelKey]] of [
["profile-settings", "agents:profileSettings"],
["cli-flags", "agents:agentCliFlags"],
["environment-variables", "executors:environmentVariables"],
["command-preview", "agents:commandPreview"],
].entries()) {
entries.push({
id: `${profileId}:${suffix}`,
kind: "section",
labelKey,
parentId: profileId,
groupId: "agents",
href,
targetId: agentProfileDiscoveryTarget(
profile.id,
suffix as
| "profile-settings"
| "cli-flags"
| "environment-variables"
| "command-preview",
),
order: order + index + 1,
});
}
}
}
return entries;
}
function executorProfileDefinitions(
executors: SettingsDiscoveryContext["executors"],
): SettingsDiscoveryDefinition[] {
const entries: SettingsDiscoveryDefinition[] = [];
for (const [executorIndex, executor] of executors.entries()) {
for (const [profileIndex, profile] of (executor.profiles ?? []).entries()) {
const profileId = `executor-profile:${profile.id}`;
const href = `/settings/executors/${encodeURIComponent(profile.id)}`;
const order = 30_000 + executorIndex * 100 + profileIndex * 10;
entries.push({
id: profileId,
kind: "page",
label: profile.name,
parentId: "executors",
groupId: "executors",
href,
order,
});
for (const [index, [suffix, labelKey]] of [
["profile-details", "executors:profileDetails"],
["environment-variables", "executors:environmentVariables"],
["prepare-script", "executors:prepareScript"],
["mcp-policy", "executors:mcpPolicy"],
].entries()) {
entries.push({
id: `${profileId}:${suffix}`,
kind: "section",
labelKey,
parentId: profileId,
groupId: "executors",
href,
targetId: executorProfileDiscoveryTarget(
profile.id,
suffix as "profile-details" | "environment-variables" | "prepare-script" | "mcp-policy",
),
order: order + index + 1,
});
}
}
}
return entries;
}
function dynamicDefinitions(context: SettingsDiscoveryContext): SettingsDiscoveryDefinition[] {
return [
...workspaceDefinitions(context.workspaces),
...agentProfileDefinitions(context.agents),
...executorProfileDefinitions(context.executors),
];
}
function isVisible(entry: SettingsDiscoveryDefinition, context: SettingsDiscoveryContext) {
if (entry.requires === "account") return context.showAccount;
if (entry.requires === "users") return context.showUsers;
if (entry.requires === "workspace") return context.workspaces.length > 0;
return true;
}
function aliasesFor(entry: SettingsDiscoveryDefinition, context: SettingsDiscoveryContext) {
if (!entry.aliasesKey) return [];
return context
.t(entry.aliasesKey)
.split(",")
.map((alias) => alias.trim())
.filter(Boolean);
}
export function resolveSettingsDiscovery(
context: SettingsDiscoveryContext,
): ResolvedSettingsDiscoveryItem[] {
const definitions = [...SETTINGS_DISCOVERY_DEFINITIONS, ...dynamicDefinitions(context)].filter(
(entry) => isVisible(entry, context),
);
const byId = new Map(definitions.map((entry) => [entry.id, entry]));
const groups = new Map(SETTINGS_DISCOVERY_GROUPS.map((group) => [group.id, group]));
const labelFor = (entry: SettingsDiscoveryDefinition) =>
entry.label ?? context.t(entry.labelKey ?? entry.id);
return definitions.map((entry) => {
const breadcrumb: string[] = [];
let parent = entry.parentId ? byId.get(entry.parentId) : undefined;
while (parent) {
breadcrumb.unshift(labelFor(parent));
parent = parent.parentId ? byId.get(parent.parentId) : undefined;
}
const group = groups.get(entry.groupId);
const href = entry.targetId
? `${entry.href}#${encodeURIComponent(entry.targetId)}`
: entry.href;
return {
id: entry.id,
kind: entry.kind,
label: labelFor(entry),
aliases: aliasesFor(entry, context),
breadcrumb,
groupId: entry.groupId,
groupLabel: context.t(group?.labelKey ?? entry.groupId),
href,
targetId: entry.targetId,
order: (group?.order ?? 99) * 100_000 + entry.order,
};
});
}