feat(dashboard,cli,i18n): add sidebar entries + 308 redirects + cliCommon/cliCode/cliAgents/acpAgents i18n namespaces (plan 14 F9)

- sidebarVisibility.ts: replace cli-tools with cli-code, add cli-agents, keep acp-agents order cli-code→cli-agents→acp-agents→cloud-agents in TOOLS_GROUP; update HIDEABLE_SIDEBAR_ITEM_IDS and DEVELOPER_SHOWN preset
- next.config.mjs: 4 permanent (308) redirects for /dashboard/cli-tools→/dashboard/cli-code and /dashboard/agents→/dashboard/acp-agents (with :path* wildcards)
- pt-BR.json + en.json: add cliCommon, cliCode, cliAgents, acpAgents namespaces (~140 keys each) + sidebar keys for the 3 new IDs
- request.ts: merge EN as namespace-level fallback so 39 non-EN/pt-BR locales display new namespaces in English until translations ship
- Header.tsx: update HEADER_DESCRIPTIONS map to use new HideableSidebarItemId values
- tests: 4 new unit test files (38 assertions), update sidebar-visibility.test.ts omni-proxy item order
This commit is contained in:
diegosouzapw
2026-05-28 01:49:49 -03:00
parent 3a535625b5
commit 649a2219f0
11 changed files with 616 additions and 15 deletions

View File

@@ -344,6 +344,19 @@ const nextConfig = {
destination: "/docs/ops/vm-deployment-guide",
permanent: true,
},
// CLI Pages — Plano 14 (F9)
{ source: "/dashboard/cli-tools", destination: "/dashboard/cli-code", permanent: true },
{
source: "/dashboard/cli-tools/:path*",
destination: "/dashboard/cli-code/:path*",
permanent: true,
},
{ source: "/dashboard/agents", destination: "/dashboard/acp-agents", permanent: true },
{
source: "/dashboard/agents/:path*",
destination: "/dashboard/acp-agents/:path*",
permanent: true,
},
];
},

View File

@@ -930,7 +930,13 @@
"settingsAuthzSubtitle": "Route inventory and bypass policy",
"docsSubtitle": "Documentation",
"issuesSubtitle": "Report a bug",
"changelogSubtitle": "Release notes"
"changelogSubtitle": "Release notes",
"cliCode": "CLI Code's",
"cliCodeSubtitle": "Code tools pointing to OmniRoute",
"cliAgents": "CLI Agents",
"cliAgentsSubtitle": "Autonomous CLI agents",
"acpAgents": "ACP Agents",
"acpAgentsSubtitle": "CLIs spawned by OmniRoute"
},
"webhooks": {
"title": "Webhooks",
@@ -7289,5 +7295,137 @@
"policyLabel": "Policy:",
"resetIn": "reset in",
"quotaTotal": "total"
},
"cliCommon": {
"concept": {
"code": {
"title": "CLI Code's",
"phrase": "Code tools you point at OmniRoute",
"flow": "You → CLI Code → OmniRoute → Provider",
"seeOther": "See →"
},
"agent": {
"title": "CLI Agents",
"phrase": "Broad-purpose autonomous CLI agents you point at OmniRoute",
"flow": "You → CLI Agent → OmniRoute → Provider",
"seeOther": "See →"
},
"acp": {
"title": "ACP Agents",
"phrase": "CLIs that OmniRoute spawns as execution backend (reverse flow)",
"flow": "Client → OmniRoute → spawn CLI (stdio/ACP) → response",
"seeOther": "See →"
}
},
"comparison": {
"title": "Understand the 3 CLI types in OmniRoute",
"thisPage": "[This page ✓]",
"code": {
"title": "Code tool",
"desc": "Points to Omni",
"flow": "you → CLI → Omni → provider",
"examples": "e.g.: claude, codex"
},
"agent": {
"title": "Broad autonomous agent",
"desc": "Points to Omni",
"flow": "you → agent → Omni",
"examples": "e.g.: hermes, goose"
},
"acp": {
"title": "CLI used as backend by Omni",
"desc": "Reverse execution",
"flow": "Omni → spawn CLI → resp",
"examples": "e.g.: claude, codex (ACP)"
}
},
"card": {
"detected": "Detected",
"notDetected": "Not detected",
"configured": "Configured",
"notConfigured": "Not configured",
"configure": "Configure →",
"howToInstall": "How to install →",
"manualConfig": "Manual config",
"installGuide": "Install guide",
"endpointLabel": "Endpoint",
"baseUrlPartial": "Partial Base URL",
"refreshDetection": "Refresh detection",
"alsoAcp": "also ACP"
},
"detail": {
"back": "Back",
"apply": "Save",
"reset": "Clear",
"manualConfig": "Manual configuration",
"vendor": "Vendor",
"category": "Type",
"detectionStatus": "Detection",
"configStatus": "Configuration",
"baseUrlLabel": "Base URL",
"apiKeyLabel": "API Key",
"modelMappingLabel": "Model mapping",
"noActiveProviders": "No active providers.",
"noActiveProvidersDesc": "Go to Providers to connect at least 1 provider before configuring the CLI.",
"openProviders": "Open Providers →"
}
},
"cliCode": {
"pageTitle": "CLI Code's",
"pageSubtitle": "Code tools pointing to OmniRoute",
"searchPlaceholder": "Search CLI…",
"filterDetectionLabel": "Detection",
"filterBaseUrlLabel": "Base URL",
"detectionAll": "All",
"detectionInstalled": "Installed",
"detectionNotFound": "Not found",
"baseUrlAll": "All",
"baseUrlFull": "Full",
"baseUrlPartial": "Partial"
},
"cliAgents": {
"pageTitle": "CLI Agents",
"pageSubtitle": "Broad-purpose autonomous CLI agents",
"refreshDetection": "Refresh detection",
"searchPlaceholder": "Search agent…",
"detectionFilterLabel": "Detection",
"detectionAll": "All",
"detectionInstalled": "Installed",
"detectionNotInstalled": "Not installed",
"visibleCount": "{count} visible",
"emptyState": "No CLI agents found with the current filters."
},
"acpAgents": {
"pageTitle": "ACP Agents",
"pageSubtitle": "CLIs that OmniRoute spawns as execution backend",
"scanning": "Detecting agents…",
"refresh": "Refresh",
"setupGuideTitle": "Setup guide",
"setupGuideDetectCliTitle": "Detect CLI",
"setupGuideDetectCliDesc": "Agents are identified by running the --version command in PATH.",
"setupGuideCustomAgentTitle": "Add custom agent",
"setupGuideCustomAgentDesc": "Fill in the form below to register a custom CLI agent.",
"setupGuideCommandMissingTitle": "Command not found",
"setupGuideCommandMissingDesc": "Check that the binary is in PATH.",
"fingerprintSettingsHint": "Configure routing and fingerprints in",
"settingsRoutingLink": "Settings → Routing",
"installed": "Installed",
"notFound": "Not found",
"builtIn": "Built-in",
"custom": "Custom",
"agentUseCaseHint": "Available for spawn via ACP.",
"remove": "Remove",
"addCustomAgent": "Add custom agent",
"addCustomAgentDesc": "Register a custom CLI agent to be spawned via ACP.",
"addAgent": "Add agent",
"agentName": "Name",
"agentNamePlaceholder": "e.g.: My Agent",
"binaryName": "Binary",
"binaryNamePlaceholder": "e.g.: myagent",
"versionCommand": "Version command",
"versionCommandPlaceholder": "e.g.: myagent --version",
"spawnArgs": "Spawn arguments",
"spawnArgsPlaceholder": "e.g.: --quiet, --json",
"cliCodeRedirectCta": "Open CLI Code's"
}
}

View File

@@ -930,7 +930,13 @@
"settingsAuthzSubtitle": "Inventário de rotas e política de bypass",
"docsSubtitle": "Documentação",
"issuesSubtitle": "Reportar um bug",
"changelogSubtitle": "Notas de versão"
"changelogSubtitle": "Notas de versão",
"cliCode": "CLI Code's",
"cliCodeSubtitle": "Ferramentas de código que apontam para o OmniRoute",
"cliAgents": "CLI Agents",
"cliAgentsSubtitle": "Agentes autônomos com CLI",
"acpAgents": "ACP Agents",
"acpAgentsSubtitle": "CLIs spawnadas pelo OmniRoute"
},
"webhooks": {
"title": "Webhooks",
@@ -7279,5 +7285,137 @@
"policyLabel": "Política:",
"resetIn": "redefinir em",
"quotaTotal": "total"
},
"cliCommon": {
"concept": {
"code": {
"title": "CLI Code's",
"phrase": "Ferramentas de código que você aponta para o OmniRoute",
"flow": "Você → CLI Code → OmniRoute → Provider",
"seeOther": "Ver →"
},
"agent": {
"title": "CLI Agents",
"phrase": "Agentes autônomos (CLI) de propósito amplo que você aponta para o OmniRoute",
"flow": "Você → CLI Agent → OmniRoute → Provider",
"seeOther": "Ver →"
},
"acp": {
"title": "ACP Agents",
"phrase": "CLIs que o OmniRoute spawna como backend de execução (fluxo reverso)",
"flow": "Cliente → OmniRoute → spawn CLI (stdio/ACP) → resposta",
"seeOther": "Ver →"
}
},
"comparison": {
"title": "Entenda os 3 tipos de CLI no OmniRoute",
"thisPage": "[Esta página ✓]",
"code": {
"title": "Ferramenta de código",
"desc": "Aponta para o Omni",
"flow": "você → CLI → Omni → provider",
"examples": "ex: claude, codex"
},
"agent": {
"title": "Agente autônomo amplo",
"desc": "Aponta para o Omni",
"flow": "você → agente → Omni",
"examples": "ex: hermes, goose"
},
"acp": {
"title": "CLI usada como backend pelo Omni",
"desc": "Execução reversa",
"flow": "Omni → spawn CLI → resp",
"examples": "ex: claude, codex (ACP)"
}
},
"card": {
"detected": "Detectado",
"notDetected": "Não detectado",
"configured": "Configurado",
"notConfigured": "Não configurado",
"configure": "Configurar →",
"howToInstall": "Como instalar →",
"manualConfig": "Manual config",
"installGuide": "Install guide",
"endpointLabel": "Endpoint",
"baseUrlPartial": "Base URL parcial",
"refreshDetection": "Atualizar detecção",
"alsoAcp": "também ACP"
},
"detail": {
"back": "Voltar",
"apply": "Salvar",
"reset": "Limpar",
"manualConfig": "Configuração manual",
"vendor": "Origem",
"category": "Tipo",
"detectionStatus": "Detecção",
"configStatus": "Configuração",
"baseUrlLabel": "Base URL",
"apiKeyLabel": "API Key",
"modelMappingLabel": "Mapeamento de modelos",
"noActiveProviders": "Nenhum provider ativo.",
"noActiveProvidersDesc": "Vá em Providers para conectar pelo menos 1 provider antes de configurar a CLI.",
"openProviders": "Abrir Providers →"
}
},
"cliCode": {
"pageTitle": "CLI Code's",
"pageSubtitle": "Ferramentas de código que apontam para o OmniRoute",
"searchPlaceholder": "Buscar CLI…",
"filterDetectionLabel": "Detecção",
"filterBaseUrlLabel": "Base URL",
"detectionAll": "Todas",
"detectionInstalled": "Instaladas",
"detectionNotFound": "Não detectadas",
"baseUrlAll": "Todas",
"baseUrlFull": "Completa",
"baseUrlPartial": "Parcial"
},
"cliAgents": {
"pageTitle": "CLI Agents",
"pageSubtitle": "Agentes autônomos (CLI) de propósito amplo",
"refreshDetection": "Atualizar detecção",
"searchPlaceholder": "Buscar agente…",
"detectionFilterLabel": "Detecção",
"detectionAll": "Todos",
"detectionInstalled": "Instalados",
"detectionNotInstalled": "Não instalados",
"visibleCount": "{count} visíveis",
"emptyState": "Nenhum agente CLI encontrado com os filtros atuais."
},
"acpAgents": {
"pageTitle": "ACP Agents",
"pageSubtitle": "CLIs que o OmniRoute spawna como backend de execução",
"scanning": "Detectando agentes…",
"refresh": "Atualizar",
"setupGuideTitle": "Guia de setup",
"setupGuideDetectCliTitle": "Detectar CLI",
"setupGuideDetectCliDesc": "Os agentes são identificados rodando o comando --version no PATH.",
"setupGuideCustomAgentTitle": "Adicionar agente customizado",
"setupGuideCustomAgentDesc": "Preencha o formulário abaixo para registrar um agente CLI customizado.",
"setupGuideCommandMissingTitle": "Comando não encontrado",
"setupGuideCommandMissingDesc": "Verifique se o binário está no PATH.",
"fingerprintSettingsHint": "Configure roteamento e fingerprints em",
"settingsRoutingLink": "Configurações → Roteamento",
"installed": "Instalado",
"notFound": "Não encontrado",
"builtIn": "Built-in",
"custom": "Customizado",
"agentUseCaseHint": "Disponível para spawn via ACP.",
"remove": "Remover",
"addCustomAgent": "Adicionar agente customizado",
"addCustomAgentDesc": "Registre um agente CLI customizado que será spawnado via ACP.",
"addAgent": "Adicionar agente",
"agentName": "Nome",
"agentNamePlaceholder": "ex: Meu Agente",
"binaryName": "Binário",
"binaryNamePlaceholder": "ex: meuagente",
"versionCommand": "Comando de versão",
"versionCommandPlaceholder": "ex: meuagente --version",
"spawnArgs": "Argumentos de spawn",
"spawnArgsPlaceholder": "ex: --quiet, --json",
"cliCodeRedirectCta": "Abrir CLI Code's"
}
}

View File

@@ -21,8 +21,23 @@ export default getRequestConfig(async () => {
const messages = (await import(`./messages/${locale}.json`)).default;
// 4. Merge EN as namespace-level fallback for locales that are missing new namespaces.
// Only applied when the active locale is not EN (avoids a redundant import).
// Merging is shallow at the top-level namespace key — if a namespace is already
// present in the locale file it is kept as-is; missing namespaces fall back to EN.
// This ensures new namespaces (e.g. cliCode, cliAgents, acpAgents, cliCommon added
// in plan 14 F9) are displayed in English for the 39 non-EN/non-pt-BR locales until
// translations are shipped.
let mergedMessages: Record<string, unknown> = messages as Record<string, unknown>;
if (locale !== DEFAULT_LOCALE) {
const enMessages = (
await import(`./messages/${DEFAULT_LOCALE}.json`)
).default as Record<string, unknown>;
mergedMessages = { ...enMessages, ...mergedMessages };
}
return {
locale,
messages,
messages: mergedMessages,
};
});

View File

@@ -48,7 +48,9 @@ const HEADER_DESCRIPTIONS: Partial<Record<HideableSidebarItemId, string>> = {
quota: "limitsDescription",
runtime: "runtimeDescription",
media: "mediaDescription",
agents: "agentsDescription",
"cli-code": "cliToolsDescription",
"cli-agents": "agentsDescription",
"acp-agents": "agentsDescription",
"cloud-agents": "cloudAgentsDescription",
memory: "memoryDescription",
skills: "skillsDescription",
@@ -98,8 +100,6 @@ const HEADER_DESCRIPTIONS: Partial<Record<HideableSidebarItemId, string>> = {
// Proxy sub-pages
"mitm-proxy": "mitmProxyDescription",
"1proxy": "oneProxyDescription",
// OmniProxy items
"cli-tools": "cliToolsDescription",
};
// Build href → sidebar item lookup (non-external items only)

View File

@@ -13,7 +13,8 @@ export const HIDEABLE_SIDEBAR_ITEM_IDS = [
"context-rtk",
"context-combos",
// OmniProxy > Tools
"cli-tools",
"cli-code",
"cli-agents",
"acp-agents",
"cloud-agents",
// OmniProxy > Integrations
@@ -229,18 +230,25 @@ const TOOLS_GROUP: SidebarItemGroup = {
titleFallback: "Tools",
items: [
{
id: "cli-tools",
href: "/dashboard/cli-tools",
i18nKey: "cliTools",
subtitleKey: "cliToolsSubtitle",
id: "cli-code",
href: "/dashboard/cli-code",
i18nKey: "cliCode",
subtitleKey: "cliCodeSubtitle",
icon: "terminal",
},
{
id: "cli-agents",
href: "/dashboard/cli-agents",
i18nKey: "cliAgents",
subtitleKey: "cliAgentsSubtitle",
icon: "smart_toy",
},
{
id: "acp-agents",
href: "/dashboard/acp-agents",
i18nKey: "acpAgents",
subtitleKey: "acpAgentsSubtitle",
icon: "smart_toy",
icon: "device_hub",
},
{
id: "cloud-agents",
@@ -801,7 +809,8 @@ const DEVELOPER_SHOWN: ReadonlySet<HideableSidebarItemId> = new Set([
"context-caveman",
"context-rtk",
"context-combos",
"cli-tools",
"cli-code",
"cli-agents",
"acp-agents",
"api-endpoints",
"analytics",

View File

@@ -0,0 +1,124 @@
import test from "node:test";
import assert from "node:assert/strict";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const pt = require("../../src/i18n/messages/pt-BR.json");
const en = require("../../src/i18n/messages/en.json");
// ─── PT-BR namespace presence ─────────────────────────────────────────────────
test("pt-BR has cliCommon namespace", () => {
assert.ok(pt.cliCommon, "expected pt-BR.json to have 'cliCommon' namespace");
});
test("pt-BR has cliCode namespace", () => {
assert.ok(pt.cliCode, "expected pt-BR.json to have 'cliCode' namespace");
});
test("pt-BR has cliAgents namespace", () => {
assert.ok(pt.cliAgents, "expected pt-BR.json to have 'cliAgents' namespace");
});
test("pt-BR has acpAgents namespace", () => {
assert.ok(pt.acpAgents, "expected pt-BR.json to have 'acpAgents' namespace");
});
// ─── PT-BR page titles ────────────────────────────────────────────────────────
test("pt-BR cliCode.pageTitle is 'CLI Code's'", () => {
assert.equal(pt.cliCode.pageTitle, "CLI Code's");
});
test("pt-BR cliAgents.pageTitle is 'CLI Agents'", () => {
assert.equal(pt.cliAgents.pageTitle, "CLI Agents");
});
test("pt-BR acpAgents.pageTitle is 'ACP Agents'", () => {
assert.equal(pt.acpAgents.pageTitle, "ACP Agents");
});
// ─── PT-BR cliCommon content ──────────────────────────────────────────────────
test("pt-BR cliCommon.concept.code.phrase contains 'código'", () => {
assert.ok(
typeof pt.cliCommon.concept?.code?.phrase === "string" &&
pt.cliCommon.concept.code.phrase.includes("código"),
`expected cliCommon.concept.code.phrase to contain 'código', got: ${pt.cliCommon.concept?.code?.phrase}`
);
});
test("pt-BR cliCommon.comparison.title is non-empty string", () => {
assert.ok(
typeof pt.cliCommon.comparison?.title === "string" && pt.cliCommon.comparison.title.length > 0
);
});
// ─── PT-BR sidebar keys ───────────────────────────────────────────────────────
test("pt-BR sidebar has cliCode key", () => {
assert.ok(pt.sidebar?.cliCode, "expected pt-BR sidebar to have 'cliCode' key");
});
test("pt-BR sidebar has cliAgents key", () => {
assert.ok(pt.sidebar?.cliAgents, "expected pt-BR sidebar to have 'cliAgents' key");
});
test("pt-BR sidebar has acpAgents key", () => {
assert.ok(pt.sidebar?.acpAgents, "expected pt-BR sidebar to have 'acpAgents' key");
});
// ─── EN namespace presence ────────────────────────────────────────────────────
test("en has cliCommon namespace", () => {
assert.ok(en.cliCommon, "expected en.json to have 'cliCommon' namespace");
});
test("en has cliCode namespace", () => {
assert.ok(en.cliCode, "expected en.json to have 'cliCode' namespace");
});
test("en has cliAgents namespace", () => {
assert.ok(en.cliAgents, "expected en.json to have 'cliAgents' namespace");
});
test("en has acpAgents namespace", () => {
assert.ok(en.acpAgents, "expected en.json to have 'acpAgents' namespace");
});
// ─── EN page titles ───────────────────────────────────────────────────────────
test("en cliCode.pageTitle is 'CLI Code's'", () => {
assert.equal(en.cliCode.pageTitle, "CLI Code's");
});
test("en cliAgents.pageTitle is 'CLI Agents'", () => {
assert.equal(en.cliAgents.pageTitle, "CLI Agents");
});
test("en cliAgents.pageTitle is 'ACP Agents'", () => {
assert.equal(en.acpAgents.pageTitle, "ACP Agents");
});
// ─── EN cliCommon content ─────────────────────────────────────────────────────
test("en cliCommon.concept.code.phrase is a non-empty string", () => {
assert.ok(
typeof en.cliCommon.concept?.code?.phrase === "string" &&
en.cliCommon.concept.code.phrase.length > 0
);
});
// ─── EN sidebar keys ──────────────────────────────────────────────────────────
test("en sidebar has cliCode key", () => {
assert.ok(en.sidebar?.cliCode, "expected en sidebar to have 'cliCode' key");
});
test("en sidebar has cliAgents key", () => {
assert.ok(en.sidebar?.cliAgents, "expected en sidebar to have 'cliAgents' key");
});
test("en sidebar has acpAgents key", () => {
assert.ok(en.sidebar?.acpAgents, "expected en sidebar to have 'acpAgents' key");
});

View File

@@ -0,0 +1,50 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { join } from "node:path";
const repoRoot = join(import.meta.dirname, "../..");
const configSource = await readFile(join(repoRoot, "next.config.mjs"), "utf8");
test("next.config.mjs has permanent redirect from /dashboard/cli-tools to /dashboard/cli-code", () => {
assert.ok(
configSource.includes('source: "/dashboard/cli-tools"') &&
configSource.includes('destination: "/dashboard/cli-code"'),
"expected /dashboard/cli-tools → /dashboard/cli-code redirect in next.config.mjs"
);
});
test("next.config.mjs has permanent wildcard redirect from /dashboard/cli-tools/:path* to /dashboard/cli-code/:path*", () => {
assert.ok(
configSource.includes('source: "/dashboard/cli-tools/:path*"') &&
configSource.includes('destination: "/dashboard/cli-code/:path*"'),
"expected /dashboard/cli-tools/:path* → /dashboard/cli-code/:path* redirect in next.config.mjs"
);
});
test("next.config.mjs has permanent redirect from /dashboard/agents to /dashboard/acp-agents", () => {
assert.ok(
configSource.includes('source: "/dashboard/agents"') &&
configSource.includes('destination: "/dashboard/acp-agents"'),
"expected /dashboard/agents → /dashboard/acp-agents redirect in next.config.mjs"
);
});
test("next.config.mjs has permanent wildcard redirect from /dashboard/agents/:path* to /dashboard/acp-agents/:path*", () => {
assert.ok(
configSource.includes('source: "/dashboard/agents/:path*"') &&
configSource.includes('destination: "/dashboard/acp-agents/:path*"'),
"expected /dashboard/agents/:path* → /dashboard/acp-agents/:path* redirect in next.config.mjs"
);
});
test("all 4 CLI redirect entries use permanent: true", () => {
// Extract the CLI Pages block
const cliBlock = configSource.slice(configSource.indexOf("// CLI Pages — Plano 14 (F9)"));
assert.ok(cliBlock.length > 0, "expected CLI Pages block to be present");
const permanentCount = (cliBlock.match(/permanent: true/g) || []).length;
assert.ok(
permanentCount >= 4,
`expected at least 4 'permanent: true' entries in CLI Pages block, found ${permanentCount}`
);
});

View File

@@ -0,0 +1,41 @@
import test from "node:test";
import assert from "node:assert/strict";
const sidebarVisibility = await import("../../src/shared/constants/sidebarVisibility.ts");
test("HIDEABLE_SIDEBAR_ITEM_IDS includes cli-code (plan 14 rename)", () => {
assert.ok(
(sidebarVisibility.HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes("cli-code"),
"expected 'cli-code' in HIDEABLE_SIDEBAR_ITEM_IDS"
);
});
test("HIDEABLE_SIDEBAR_ITEM_IDS includes cli-agents (plan 14 new entry)", () => {
assert.ok(
(sidebarVisibility.HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes("cli-agents"),
"expected 'cli-agents' in HIDEABLE_SIDEBAR_ITEM_IDS"
);
});
test("HIDEABLE_SIDEBAR_ITEM_IDS includes acp-agents (renamed from agents)", () => {
assert.ok(
(sidebarVisibility.HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes("acp-agents"),
"expected 'acp-agents' in HIDEABLE_SIDEBAR_ITEM_IDS"
);
});
test("HIDEABLE_SIDEBAR_ITEM_IDS does NOT include legacy cli-tools", () => {
assert.equal(
(sidebarVisibility.HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes("cli-tools"),
false,
"expected 'cli-tools' to be removed from HIDEABLE_SIDEBAR_ITEM_IDS (plan 14 rename to cli-code)"
);
});
test("HIDEABLE_SIDEBAR_ITEM_IDS does NOT include legacy agents", () => {
assert.equal(
(sidebarVisibility.HIDEABLE_SIDEBAR_ITEM_IDS as readonly string[]).includes("agents"),
false,
"expected 'agents' to be removed from HIDEABLE_SIDEBAR_ITEM_IDS (plan 14 rename to acp-agents)"
);
});

View File

@@ -0,0 +1,72 @@
import test from "node:test";
import assert from "node:assert/strict";
const sidebarVisibility = await import("../../src/shared/constants/sidebarVisibility.ts");
function getToolsGroup() {
const omniProxySection = sidebarVisibility.SIDEBAR_SECTIONS.find(
(section) => section.id === "omni-proxy"
);
assert.ok(omniProxySection, "expected omni-proxy section to exist");
const toolsGroup = omniProxySection.children.find(
(child): child is (typeof sidebarVisibility.SIDEBAR_SECTIONS)[number]["children"][number] & {
type: "group";
} =>
"type" in child &&
(child as { type: string }).type === "group" &&
(child as { id: string }).id === "tools"
);
assert.ok(toolsGroup, "expected tools group to exist in omni-proxy section");
return toolsGroup as {
type: "group";
id: string;
items: readonly { id: string; href: string; i18nKey: string }[];
};
}
test("TOOLS_GROUP items follow plan 14 order: cli-code → cli-agents → acp-agents → cloud-agents", () => {
const toolsGroup = getToolsGroup();
const itemIds = toolsGroup.items.map((item) => item.id);
assert.deepEqual(
itemIds,
["cli-code", "cli-agents", "acp-agents", "cloud-agents"],
"TOOLS_GROUP items order must be cli-code, cli-agents, acp-agents, cloud-agents"
);
});
test("TOOLS_GROUP cli-code item has correct href and i18nKey", () => {
const toolsGroup = getToolsGroup();
const cliCode = toolsGroup.items.find((item) => item.id === "cli-code");
assert.ok(cliCode, "expected cli-code in TOOLS_GROUP");
assert.equal(cliCode.href, "/dashboard/cli-code");
assert.equal(cliCode.i18nKey, "cliCode");
});
test("TOOLS_GROUP cli-agents item has correct href and i18nKey", () => {
const toolsGroup = getToolsGroup();
const cliAgents = toolsGroup.items.find((item) => item.id === "cli-agents");
assert.ok(cliAgents, "expected cli-agents in TOOLS_GROUP");
assert.equal(cliAgents.href, "/dashboard/cli-agents");
assert.equal(cliAgents.i18nKey, "cliAgents");
});
test("TOOLS_GROUP acp-agents item has correct href and i18nKey", () => {
const toolsGroup = getToolsGroup();
const acpAgents = toolsGroup.items.find((item) => item.id === "acp-agents");
assert.ok(acpAgents, "expected acp-agents in TOOLS_GROUP");
assert.equal(acpAgents.href, "/dashboard/acp-agents");
assert.equal(acpAgents.i18nKey, "acpAgents");
});
test("TOOLS_GROUP does NOT contain legacy cli-tools or agents entries", () => {
const toolsGroup = getToolsGroup();
const legacyIds = toolsGroup.items
.map((item) => item.id)
.filter((id) => id === "cli-tools" || id === "agents");
assert.deepEqual(
legacyIds,
[],
"TOOLS_GROUP must not contain legacy 'cli-tools' or 'agents' entries"
);
});

View File

@@ -49,8 +49,9 @@ test("primary sidebar items place limits after cache", () => {
"context-caveman",
"context-rtk",
"context-combos",
"cli-tools",
"agents",
"cli-code",
"cli-agents",
"acp-agents",
"cloud-agents",
"api-endpoints",
"webhooks",