feat(dashboard): list MITM hosts-file entries in the tool card (#4325)

The CLI-tools MITM card's "How it works" section showed a single hardcoded example
domain (only antigravity/kiro). It now lists every 127.0.0.1 <host> entry for the
selected tool, so users on locked-down machines — where the automatic, sudo-gated
hosts-file edit isn't available — can add them manually. Hosts come from a client-safe
projection of the canonical src/mitm/targets registry (MITM_TOOL_HOSTS), kept in
lock-step with the registry by a sync test (no duplicated source of truth).

Co-authored-by: mrcyclo <13806369+mrcyclo@users.noreply.github.com>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-19 21:39:56 -03:00
committed by GitHub
parent 5c68048650
commit 0d6c5686d2
4 changed files with 72 additions and 11 deletions

View File

@@ -11,6 +11,7 @@ _In development — bullets added per PR; finalized at release._
### ✨ New Features
- **feat(api): `x-omniroute-no-memory` request header — per-request opt-out of memory/skills injection** — clients that manage their own context (e.g. their own RAG/memory) can send `x-omniroute-no-memory: true` (mirrors the existing `x-omniroute-no-cache` convention) to skip the gateway injecting up to `memorySettings.maxTokens` (~2k) tokens of memory **and** skills context into that chat request — avoiding the token/cost inflation it otherwise adds on every call. Absent the header, behavior is unchanged. (PRD-2026-06-19-no-memory-header)
- **feat(dashboard): MITM tool card lists the exact hosts-file entries to add manually** — the CLI-tools MITM card's "How it works" section now lists the full set of `127.0.0.1 <host>` lines for the selected tool (sourced from the canonical MITM target registry) instead of a single example domain. Users on locked-down machines — where the automatic, sudo-gated hosts-file edit isn't available — can now copy every required entry by hand. (thanks @mrcyclo)
### 🔧 Changed

View File

@@ -2,6 +2,7 @@
import { useState, useEffect } from "react";
import { Card, Button, Badge, Modal, Input, ModelSelectModal } from "@/shared/components";
import { MITM_TOOL_HOSTS } from "@/shared/constants/mitmToolHosts";
import { useTranslations } from "next-intl";
import ProviderIcon from "@/shared/components/ProviderIcon";
@@ -364,13 +365,11 @@ export default function AntigravityToolCard({
{/* When stopped: how it works */}
{!isRunning &&
(() => {
// Dynamic MITM instructions per tool (#505)
const mitmDomains: Record<string, string> = {
antigravity: "daily-cloudcode-pa.googleapis.com",
kiro: "api.anthropic.com",
};
// Per-tool MITM hosts redirected to 127.0.0.1 (#505, 9router#788). List every
// host so users on locked-down machines can add the entries to their hosts file
// manually when the automatic (sudo-gated) edit isn't available.
const toolName = tool.name || tool.id;
const domain = mitmDomains[tool.id] || mitmDomains.antigravity;
const hosts = MITM_TOOL_HOSTS[tool.id] ?? MITM_TOOL_HOSTS.antigravity;
return (
<div className="flex flex-col gap-1.5 px-1">
<p className="text-xs text-text-muted">
@@ -379,11 +378,15 @@ export default function AntigravityToolCard({
</p>
<div className="flex flex-col gap-0.5 text-[11px] text-text-muted">
<span>{t("mitmStep1")}</span>
<span>
{t("mitmStep2Prefix")}{" "}
<code className="text-[10px] bg-surface px-1 rounded">{domain}</code>{" "}
{t("mitmStep2Suffix")}
</span>
<span>{t("mitmStep2Prefix")}</span>
<ul className="list-none my-0.5 flex flex-col gap-0.5 font-mono text-[10px] text-text-muted break-all">
{hosts.map((host) => (
<li key={host}>
<span className="text-primary">127.0.0.1</span> {host}
</li>
))}
</ul>
<span>{t("mitmStep2Suffix")}</span>
<span>{t("mitmStep3", { toolName })}</span>
</div>
</div>

View File

@@ -0,0 +1,31 @@
/**
* Per-tool MITM DNS hosts — the domains redirected to 127.0.0.1 when a tool's MITM DNS
* routing is enabled. Surfaced in the dashboard so users on locked-down machines (where the
* automatic hosts-file edit needs admin/sudo) can add the entries manually.
*
* This is a CLIENT-SAFE projection of the canonical server-side registry in
* `src/mitm/targets/`. The MITM target modules pull in node-only handler logic, so the
* dashboard cannot import them directly — this plain-data map is kept in lock-step with
* `ALL_TARGETS` by `tests/unit/mitm-tool-hosts.test.ts` (a drift here fails that test).
*/
export const MITM_TOOL_HOSTS: Record<string, string[]> = {
antigravity: [
"daily-cloudcode-pa.googleapis.com",
"cloudcode-pa.googleapis.com",
"daily-cloudcode-pa.sandbox.googleapis.com",
"autopush-cloudcode-pa.sandbox.googleapis.com",
],
kiro: ["api.anthropic.com"],
copilot: ["api.githubcopilot.com", "copilot-proxy.githubusercontent.com"],
codex: ["chatgpt.com"],
cursor: ["api2.cursor.sh"],
zed: ["api.zed.dev"],
"claude-code": ["api.anthropic.com"],
"open-code": ["opencode.ai"],
trae: ["trae.invalid"],
};
/** Hosts for a tool id, falling back to an empty list for unknown ids. */
export function getMitmToolHosts(toolId: string): string[] {
return MITM_TOOL_HOSTS[toolId] ?? [];
}

View File

@@ -0,0 +1,26 @@
import test from "node:test";
import assert from "node:assert/strict";
import { ALL_TARGETS } from "../../src/mitm/targets/index.ts";
import { MITM_TOOL_HOSTS, getMitmToolHosts } from "../../src/shared/constants/mitmToolHosts.ts";
// The dashboard cannot import the node-only MITM target modules, so MITM_TOOL_HOSTS is a
// client-safe copy of each target's hosts. This guards against the copy drifting from the
// canonical registry (port from 9router#788).
test("MITM_TOOL_HOSTS stays in sync with the canonical MITM target registry", () => {
const fromRegistry: Record<string, string[]> = {};
for (const target of ALL_TARGETS) {
fromRegistry[target.id] = target.hosts;
}
assert.deepEqual(
MITM_TOOL_HOSTS,
fromRegistry,
"MITM_TOOL_HOSTS must exactly match ALL_TARGETS hosts — update src/shared/constants/mitmToolHosts.ts"
);
});
test("getMitmToolHosts returns the hosts for a known tool and [] for unknown ids", () => {
assert.ok(getMitmToolHosts("antigravity").includes("cloudcode-pa.googleapis.com"));
assert.deepEqual(getMitmToolHosts("does-not-exist"), []);
});