Merge release/v3.8.8 into refactor/pages-v3-14 (CLI pages redesign)

Conflicts: CLAUDE.md base; i18n deep-merge (costsSection=Custos); .source regenerated (fumadocs-mdx, +1 doc); openapi regenerated.

CLIToolsPageClient.tsx: accepted #2839 deletion (redesign replaced cli-tools/ with cli-code/cli-agents/acp-agents; base #2858 only removed obsolete MITM cards; AgentBridge reachable via sidebar; 0 orphan refs). sidebar-visibility test passes (cli items + agent-bridge merged).
This commit is contained in:
diegosouzapw
2026-05-30 03:10:34 -03:00
1071 changed files with 128906 additions and 11033 deletions

View File

@@ -7,10 +7,18 @@ import Button from "./Button";
import Input from "./Input";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
const GOOGLE_OAUTH_PROVIDERS = new Set(["antigravity", "gemini-cli"]);
const GOOGLE_OAUTH_PROVIDERS = new Set(["antigravity", "agy", "gemini-cli"]);
/** Providers that use a local callback server on a random port (PKCE browser flow). */
const PKCE_CALLBACK_SERVER_PROVIDERS = new Set(["codex", "windsurf", "devin-cli"]);
const PKCE_CALLBACK_SERVER_PROVIDERS = new Set(["codex"]);
/**
* Phase 1 hotfix (2026-05-29): windsurf & devin-cli only support import-token.
* Their PKCE flow targeting app.devin.ai/editor/signin returned 404 post-rebrand.
* Phase 2 will reintroduce browser login via Firebase OAuth + RegisterUser.
* Spec: docs/superpowers/specs/2026-05-29-windsurf-login-fix-design.md.
*/
const IMPORT_TOKEN_ONLY_PROVIDERS = new Set(["windsurf", "devin-cli"]);
type OAuthModalProps = {
isOpen: boolean;
@@ -45,11 +53,16 @@ export default function OAuthModal({
const [deviceData, setDeviceData] = useState(null);
const [polling, setPolling] = useState(false);
// API-key paste mode: for providers that accept a token directly (windsurf, devin-cli)
const [showPasteToken, setShowPasteToken] = useState(false);
const [showPasteToken, setShowPasteToken] = useState(
provider === "windsurf" || provider === "devin-cli"
);
const [pasteToken, setPasteToken] = useState("");
const [savingToken, setSavingToken] = useState(false);
const supportsTokenPaste = provider === "windsurf" || provider === "devin-cli";
// Phase 1 hotfix (2026-05-29): windsurf/devin-cli are import-token-only.
// Hide the "Browser Login" tab — Phase 2 will restore it via Firebase OAuth.
const importTokenOnly = IMPORT_TOKEN_ONLY_PROVIDERS.has(provider);
const popupRef = useRef(null);
const { copied, copy } = useCopyToClipboard();
const deviceVerificationUrl =
@@ -695,8 +708,10 @@ export default function OAuthModal({
size="lg"
>
<div className="flex flex-col gap-4">
{/* Paste-token tab toggle (Windsurf / Devin CLI only) */}
{supportsTokenPaste && step !== "success" && (
{/* Paste-token tab toggle (Windsurf / Devin CLI only).
Phase 1 hotfix: when importTokenOnly is true, hide the entire toggle —
there is no "Browser Login" tab to switch to until Phase 2 ships. */}
{supportsTokenPaste && !importTokenOnly && step !== "success" && (
<div className="flex gap-2 border-b border-border pb-3">
<button
className={`text-sm px-3 py-1 rounded-t ${!showPasteToken ? "font-semibold border-b-2 border-primary text-primary" : "text-text-muted"}`}

View File

@@ -94,10 +94,10 @@ export default function ProxyLogDetail({ log, onClose }) {
</div>
<div>
<div className="text-[10px] text-text-muted uppercase tracking-wider mb-1">
Public IP
Client IP
</div>
<div className="text-sm font-medium font-mono text-emerald-400">
{log.publicIp || "—"}
{log.clientIp || "—"}
</div>
</div>
<div>

View File

@@ -67,7 +67,7 @@ export default function ProxyLogger() {
{ key: "provider", label: t("colProvider") },
{ key: "target", label: t("colTarget") },
{ key: "latency", label: t("colLatency") },
{ key: "ip", label: t("colPublicIp") },
{ key: "ip", label: t("colClientIp") },
{ key: "time", label: t("colTime") },
],
[t]
@@ -435,7 +435,7 @@ export default function ProxyLogger() {
)}
{visibleColumns.ip && (
<th className="px-3 py-2.5 font-semibold text-text-muted uppercase tracking-wider text-[10px]">
{t("colPublicIp")}
{t("colClientIp")}
</th>
)}
{visibleColumns.time && (
@@ -552,7 +552,7 @@ export default function ProxyLogger() {
)}
{visibleColumns.ip && (
<td className="px-3 py-2 font-mono text-[11px] text-emerald-400">
{log.publicIp || "—"}
{log.clientIp || "—"}
</td>
)}
{visibleColumns.time && (

View File

@@ -0,0 +1,81 @@
"use client";
import { useEffect } from "react";
import { useTranslations } from "next-intl";
import Button from "@/shared/components/Button";
export interface RiskNoticeModalProps {
open: boolean;
title: string;
body: string;
dontShowAgainKey: string;
onAccept: () => void;
onCancel: () => void;
}
/**
* Generic risk notice modal (D16).
* Persists "don't show again" preference to localStorage using `dontShowAgainKey`.
*/
export function RiskNoticeModal({
open,
title,
body,
dontShowAgainKey,
onAccept,
onCancel,
}: RiskNoticeModalProps) {
const t = useTranslations("common");
useEffect(() => {
if (!open) return;
const handler = (e: KeyboardEvent) => {
if (e.key === "Escape") onCancel();
};
document.addEventListener("keydown", handler);
return () => document.removeEventListener("keydown", handler);
}, [open, onCancel]);
if (!open) return null;
const handleAccept = () => {
try {
localStorage.setItem(dontShowAgainKey, "true");
} catch {
// ignore storage errors
}
onAccept();
};
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
role="dialog"
aria-modal="true"
aria-labelledby="risk-modal-title"
>
<div className="w-full max-w-md rounded-xl border border-amber-500/30 bg-card p-6 shadow-xl">
<div className="flex items-start gap-3 mb-4">
<div className="p-2 rounded-lg bg-amber-500/10 text-amber-500 shrink-0">
<span className="material-symbols-outlined text-[20px]">warning</span>
</div>
<h2 id="risk-modal-title" className="text-base font-semibold text-text-main pt-1">
{title}
</h2>
</div>
<p className="text-sm text-text-muted mb-6 leading-relaxed">{body}</p>
<div className="flex gap-3 justify-end">
<Button variant="ghost" onClick={onCancel}>
{t("cancel") || "Cancel"}
</Button>
<Button variant="primary" onClick={handleAccept}>
<span className="material-symbols-outlined text-[14px] mr-1">check</span>
{t("understand") || "I understand"}
</Button>
</div>
</div>
</div>
);
}

View File

@@ -301,6 +301,7 @@ const LOBE_PROVIDER_ALIASES = {
"amazon-q": "Aws",
anthropic: "Anthropic",
antigravity: "Antigravity",
agy: "Antigravity", // Antigravity CLI — same brand icon as the antigravity provider
assemblyai: "AssemblyAI",
"aws-polly": "Aws",
azure: "Azure",