@tonyclaw/agent-inspector 3.0.25 → 3.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/cli.js +80 -11
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BnvOPwpe.js → CompareDrawer-BVCAAbbu.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CYOXB81n.js +4234 -0
- package/.output/public/assets/{ReplayDialog-BJgoQWdW.js → ReplayDialog-DMX23O5G.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CpBZEHnM.js → RequestAnatomy-S17pYDRc.js} +1 -1
- package/.output/public/assets/{ResponseView-CWmyRb2M.js → ResponseView-Cf1XrawD.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BWhC8bwx.js → StreamingChunkSequence-BdOrVlCi.js} +1 -1
- package/.output/public/assets/{_sessionId-Cq-2OqUU.js → _sessionId-BnKGmJrR.js} +1 -1
- package/.output/public/assets/{index-Cf8O0Oos.js → index-9fMLCU4d.js} +1 -1
- package/.output/public/assets/{index-BKi9IRtV.js → index-B1atk19i.js} +3 -3
- package/.output/public/assets/index-CmeF5XXS.css +1 -0
- package/.output/public/assets/{index-DdgTJ4QJ.js → index-gY3I-7-D.js} +1 -1
- package/.output/public/assets/{json-viewer-Bqu_4hmm.js → json-viewer-CpyYpPjx.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +6 -6
- package/.output/server/_libs/three.mjs +26016 -0
- package/.output/server/{_sessionId-Ba7H3Gjw.mjs → _sessionId-eA_h2_s6.mjs} +9 -6
- package/.output/server/_ssr/{CompareDrawer-aH59Ab6x.mjs → CompareDrawer-K2k9G7r1.mjs} +10 -7
- package/.output/server/_ssr/{ProxyViewerContainer-By-I0tJv.mjs → ProxyViewerContainer-CbyBpCAj.mjs} +804 -363
- package/.output/server/_ssr/{ReplayDialog-BO4Vy0et.mjs → ReplayDialog-DacI2Iv0.mjs} +10 -7
- package/.output/server/_ssr/{RequestAnatomy-FtOr7vSS.mjs → RequestAnatomy-gh-pAdkO.mjs} +9 -6
- package/.output/server/_ssr/{ResponseView-cvw6ajue.mjs → ResponseView-BRJKN50-.mjs} +10 -7
- package/.output/server/_ssr/{StreamingChunkSequence-Oc8S2kD6.mjs → StreamingChunkSequence-BQLVEuWY.mjs} +9 -6
- package/.output/server/_ssr/{index-BDRlQheR.mjs → index-DuSWrjbH.mjs} +9 -6
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-Cd6trhQr.mjs → json-viewer-DNs1ZjKI.mjs} +9 -6
- package/.output/server/_ssr/{router-bofP-Ekl.mjs → router-Dgx6-ot3.mjs} +713 -218
- package/.output/server/{_tanstack-start-manifest_v-BPuf86qB.mjs → _tanstack-start-manifest_v-CoGjnkQD.mjs} +1 -1
- package/.output/server/index.mjs +65 -65
- package/README.md +8 -0
- package/package.json +6 -5
- package/src/cli.ts +43 -9
- package/src/components/inspector-pet/InspectorPet.tsx +66 -51
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +455 -0
- package/src/components/inspector-pet/inspectorPetLogic.ts +28 -43
- package/src/components/providers/ProviderCard.tsx +3 -36
- package/src/components/providers/ProviderForm.tsx +251 -175
- package/src/components/providers/ProvidersPanel.tsx +18 -8
- package/src/components/providers/SettingsDialog.tsx +3 -3
- package/src/components/providers/providerFormCapabilities.ts +36 -0
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -3
- package/src/contracts/anthropic.ts +0 -5
- package/src/contracts/index.ts +0 -4
- package/src/contracts/log.ts +0 -1
- package/src/contracts/openai.ts +0 -3
- package/src/knowledge/openclawClient.ts +22 -9
- package/src/knowledge/openclawGatewayClient.ts +20 -13
- package/src/lib/alertContract.ts +0 -3
- package/src/lib/ecosystemContract.ts +0 -3
- package/src/lib/export-logs.ts +1 -4
- package/src/lib/safeFetch.ts +100 -0
- package/src/lib/ssrfGuard.ts +133 -0
- package/src/lib/stopReason.ts +1 -4
- package/src/lib/unknown.ts +27 -0
- package/src/lib/useProviders.ts +1 -1
- package/src/mcp/mode.ts +3 -9
- package/src/mcp/server.ts +5 -14
- package/src/mcp/toolHandlers.ts +45 -54
- package/src/proxy/constants.ts +17 -0
- package/src/proxy/formats/anthropic/handler.ts +5 -1
- package/src/proxy/formats/anthropic/schemas.ts +1 -7
- package/src/proxy/formats/index.ts +1 -1
- package/src/proxy/formats/openai/schemas.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -3
- package/src/proxy/handler.ts +91 -12
- package/src/proxy/identityProxy.ts +74 -1
- package/src/proxy/logImporter.ts +82 -12
- package/src/proxy/logIndex.ts +1 -4
- package/src/proxy/providerSecretStore.ts +181 -0
- package/src/proxy/providers.ts +84 -11
- package/src/proxy/schemas.ts +1 -8
- package/src/proxy/sqliteLogIndex.ts +0 -16
- package/src/proxy/store.ts +1 -5
- package/src/proxy/toolSchemaWarnings.ts +1 -9
- package/src/routes/api/logs.$id.replay.ts +17 -5
- package/src/routes/api/providers.$providerId.model-metadata.ts +11 -1
- package/src/routes/api/providers.$providerId.ts +18 -5
- package/src/routes/api/providers.ts +18 -7
- package/styles/globals.css +0 -61
- package/.output/public/assets/ProxyViewerContainer-DcqFqo1m.js +0 -127
- package/.output/public/assets/index-BMYuDL5X.css +0 -1
package/.output/server/_ssr/{ProxyViewerContainer-By-I0tJv.mjs → ProxyViewerContainer-CbyBpCAj.mjs}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as reactExports, j as jsxRuntimeExports, R as React } from "../_libs/react.mjs";
|
|
2
|
-
import { C as CapturedLogSchema,
|
|
2
|
+
import { C as CapturedLogSchema, B as DeleteInspectorGroupsResponseSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, g as readStoredUiScalePreference, h as persistUiScalePreference, j as applyUiScalePreference, x as extractStopReason, p as packageJson, P as ProviderConfigSchema, U as UI_SCALE_STORAGE_KEY, k as normalizeUiScalePreference, t as apiFormatForPath, E as GroupEvidenceExportResultSchema, F as DeleteInspectorGroupResponseSchema, $ as PiAgentResponseSchema, w as isTurnBoundary, r as readStoredThemeMode, e as applyThemeMode, f as persistThemeMode, a2 as formatUiScale, y as AlertSummarySchema, z as AlertListResponseSchema, I as InspectorGroupsListResponseSchema, G as GroupEvidenceReadResponseSchema, V as createPendingProviderTestResults, W as ProviderTestResultsSchema, X as createFailedProviderTestResults, Y as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, Z as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, _ as TimeDisplayFormatSchema, i as isRecord, T as THEME_MODE_STORAGE_KEY, n as normalizeThemeMode, M as PATH_V1_MESSAGES, N as PATH_V1_CHAT_COMPLETIONS, Q as PATH_V1_RESPONSES, H as providerHasContextMetadata, m as parseOpenAIResponse, o as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, q as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, l as AnthropicRequestSchema, s as safeGetOwnProperty, a0 as resolveProviderContextWindow, u as getSessionPath, J as findProviderModelMetadata, S as previewUpstreamUrl, K as KnowledgeCandidateSchema, a1 as isPlainRecord, v as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-Dgx6-ot3.mjs";
|
|
3
3
|
import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
|
|
4
4
|
import { J as JSZip } from "../_libs/jszip.mjs";
|
|
5
5
|
import { c as clsx } from "../_libs/clsx.mjs";
|
|
@@ -7,7 +7,9 @@ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
|
|
|
7
7
|
import { c as cva } from "../_libs/class-variance-authority.mjs";
|
|
8
8
|
import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
|
|
9
9
|
import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
|
|
10
|
-
import {
|
|
10
|
+
import { r as reactDomExports } from "../_libs/react-dom.mjs";
|
|
11
|
+
import { W as WebGLRenderer, S as SRGBColorSpace, A as ACESFilmicToneMapping, a as Scene, O as OrthographicCamera, M as MeshPhysicalMaterial, C as Color, G as Group, b as Mesh, c as CircleGeometry, d as MeshBasicMaterial, e as SphereGeometry, f as AmbientLight, H as HemisphereLight, D as DirectionalLight, P as PointLight, V as Vector3, g as CylinderGeometry } from "../_libs/three.mjs";
|
|
12
|
+
import { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, P as PanelRightOpen, c as PanelRightClose, S as Search, U as Upload, D as Download, F as FileBraces, R as RefreshCw, d as Trash2, H as Heart, e as Pause, f as Play, g as Plus, B as BrainCircuit, L as LoaderCircle, h as Send, A as ArrowLeft, i as ChevronUp, j as Copy, k as ChevronsLeft, l as ChevronLeft, m as ChevronRight, n as ChevronsRight, o as Clapperboard, p as Flag, q as Siren, r as Timer, G as Gauge, s as Sun, E as Eye, M as Moon, t as ALargeSmall, u as Layers, v as FlaskConical, w as Sparkles, x as Activity, y as Beaker, N as Network, z as Terminal, J as Settings, K as Monitor, O as Rocket, Q as PackageCheck, V as History, W as Scan, Y as CircleAlert, Z as ArrowUpRight, _ as ArrowDownRight, $ as Cpu, a0 as FolderOpen, a1 as Bell, a2 as Clock3, a3 as ShieldAlert, a4 as EyeOff, a5 as ExternalLink, a6 as Clock, a7 as RotateCw, a8 as CircleCheckBig, a9 as Pencil, aa as MessageSquare, ab as Zap, ac as Wrench, ad as Brain, ae as Minus, af as CircleX, ag as ShieldCheck, ah as Save, ai as FileSearch, aj as CloudUpload, ak as CircleQuestionMark, al as Server, am as Lock, an as Wifi, ao as WifiOff, ap as OctagonAlert, aq as Radio, ar as Globe, as as FileTerminal, at as ChevronsUp, au as ChevronsDown, av as FileDiff, aw as RotateCcw, ax as GitCompareArrows, ay as ArrowUp, az as ArrowDown, aA as Ellipsis, aB as CodeXml, aC as Blocks, aD as MousePointerClick, aE as FileText, aF as Rows3, aG as Columns2 } from "../_libs/lucide-react.mjs";
|
|
11
13
|
import { u as union, o as object, e as array, l as literal, n as number, d as boolean, b as string, _ as _enum } from "../_libs/zod.mjs";
|
|
12
14
|
import { S as Select$1, a as SelectTrigger$1, b as SelectIcon, c as SelectValue$1, d as SelectPortal, e as SelectContent$1, f as SelectViewport, g as SelectItem$1, h as SelectItemIndicator, i as SelectItemText, j as SelectScrollUpButton$1, k as SelectScrollDownButton$1 } from "../_libs/radix-ui__react-select.mjs";
|
|
13
15
|
import { D as Dialog$1, a as DialogContent$1, b as DialogClose, c as DialogTitle$1, d as DialogDescription$1, e as DialogPortal$1, f as DialogOverlay$1, g as DialogTrigger$1 } from "../_libs/radix-ui__react-dialog.mjs";
|
|
@@ -389,9 +391,6 @@ async function fetchStreamingChunks(logId, signal) {
|
|
|
389
391
|
return null;
|
|
390
392
|
}
|
|
391
393
|
}
|
|
392
|
-
function isRecord(value) {
|
|
393
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
394
|
-
}
|
|
395
394
|
function redactValue(value) {
|
|
396
395
|
if (Array.isArray(value)) return value.map((item) => redactValue(item));
|
|
397
396
|
if (!isRecord(value)) return value;
|
|
@@ -688,7 +687,7 @@ async function fetcher(url) {
|
|
|
688
687
|
}
|
|
689
688
|
function useProviders() {
|
|
690
689
|
const response = useSWR(
|
|
691
|
-
"/api/providers
|
|
690
|
+
"/api/providers",
|
|
692
691
|
fetcher,
|
|
693
692
|
{
|
|
694
693
|
revalidateOnFocus: false,
|
|
@@ -2525,27 +2524,27 @@ function useCopyFeedback(text) {
|
|
|
2525
2524
|
return { copied, copy };
|
|
2526
2525
|
}
|
|
2527
2526
|
const LazyCompareDrawer = reactExports.lazy(
|
|
2528
|
-
() => import("./CompareDrawer-
|
|
2527
|
+
() => import("./CompareDrawer-K2k9G7r1.mjs").then((m) => ({ default: m.CompareDrawer }))
|
|
2529
2528
|
);
|
|
2530
2529
|
const LazyReplayDialog = reactExports.lazy(
|
|
2531
|
-
() => import("./ReplayDialog-
|
|
2530
|
+
() => import("./ReplayDialog-DacI2Iv0.mjs").then((m) => ({ default: m.ReplayDialog }))
|
|
2532
2531
|
);
|
|
2533
2532
|
const LazyRequestAnatomy = reactExports.lazy(
|
|
2534
|
-
() => import("./RequestAnatomy-
|
|
2533
|
+
() => import("./RequestAnatomy-gh-pAdkO.mjs").then((m) => ({ default: m.RequestAnatomy }))
|
|
2535
2534
|
);
|
|
2536
2535
|
const LazyResponseView = reactExports.lazy(
|
|
2537
|
-
() => import("./ResponseView-
|
|
2536
|
+
() => import("./ResponseView-BRJKN50-.mjs").then((m) => ({ default: m.ResponseView }))
|
|
2538
2537
|
);
|
|
2539
2538
|
const LazyStreamingChunkSequence = reactExports.lazy(
|
|
2540
|
-
() => import("./StreamingChunkSequence-
|
|
2539
|
+
() => import("./StreamingChunkSequence-BQLVEuWY.mjs").then((m) => ({
|
|
2541
2540
|
default: m.StreamingChunkSequence
|
|
2542
2541
|
}))
|
|
2543
2542
|
);
|
|
2544
2543
|
const LazyJsonViewer = reactExports.lazy(
|
|
2545
|
-
() => import("./json-viewer-
|
|
2544
|
+
() => import("./json-viewer-DNs1ZjKI.mjs").then((m) => ({ default: m.JsonViewer }))
|
|
2546
2545
|
);
|
|
2547
2546
|
const LazyJsonViewerFromString = reactExports.lazy(
|
|
2548
|
-
() => import("./json-viewer-
|
|
2547
|
+
() => import("./json-viewer-DNs1ZjKI.mjs").then((m) => ({ default: m.JsonViewerFromString }))
|
|
2549
2548
|
);
|
|
2550
2549
|
const HIGHLIGHT_DURATION_MS = 1200;
|
|
2551
2550
|
const MAX_HIGHLIGHT_ATTEMPTS = 12;
|
|
@@ -8935,7 +8934,7 @@ function ProviderTestPill({
|
|
|
8935
8934
|
testResults,
|
|
8936
8935
|
isTesting
|
|
8937
8936
|
}) {
|
|
8938
|
-
const
|
|
8937
|
+
const hasEndpoint2 = providerHasEndpoint$1(provider.anthropicBaseUrl) || providerHasEndpoint$1(provider.openaiBaseUrl) || providerHasEndpoint$1(provider.openaiResponsesBaseUrl);
|
|
8939
8938
|
const summary = summarizeProviderTestResults(provider, testResults);
|
|
8940
8939
|
if (isTesting) {
|
|
8941
8940
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100", children: [
|
|
@@ -8943,7 +8942,7 @@ function ProviderTestPill({
|
|
|
8943
8942
|
"Testing"
|
|
8944
8943
|
] });
|
|
8945
8944
|
}
|
|
8946
|
-
if (!
|
|
8945
|
+
if (!hasEndpoint2) {
|
|
8947
8946
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground", children: "No endpoint" });
|
|
8948
8947
|
}
|
|
8949
8948
|
if (testResults === void 0 || summary.total === 0) {
|
|
@@ -9065,16 +9064,7 @@ function ProviderCard({
|
|
|
9065
9064
|
onRefreshMetadata,
|
|
9066
9065
|
isRefreshingMetadata = false
|
|
9067
9066
|
}) {
|
|
9068
|
-
const [showApiKey, setShowApiKey] = reactExports.useState(false);
|
|
9069
|
-
const [copied, setCopied] = reactExports.useState(false);
|
|
9070
9067
|
const [showDetails, setShowDetails] = reactExports.useState(false);
|
|
9071
|
-
function handleCopy() {
|
|
9072
|
-
void copyTextToClipboard(provider.apiKey).then((success) => {
|
|
9073
|
-
if (!success) return;
|
|
9074
|
-
setCopied(true);
|
|
9075
|
-
setTimeout(() => setCopied(false), 2e3);
|
|
9076
|
-
});
|
|
9077
|
-
}
|
|
9078
9068
|
const docsUrl = provider.apiDocsUrl ?? Object.entries(KNOWN_PROVIDER_DOCS).find(
|
|
9079
9069
|
([keyword]) => provider.name.toLowerCase().includes(keyword)
|
|
9080
9070
|
)?.[1];
|
|
@@ -9161,28 +9151,9 @@ function ProviderCard({
|
|
|
9161
9151
|
] })
|
|
9162
9152
|
] }),
|
|
9163
9153
|
showDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-3 border-t border-border/60 pt-3", children: [
|
|
9164
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9165
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9166
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9167
|
-
"button",
|
|
9168
|
-
{
|
|
9169
|
-
type: "button",
|
|
9170
|
-
onClick: () => setShowApiKey((s) => !s),
|
|
9171
|
-
className: "p-1 text-muted-foreground transition-colors hover:text-foreground",
|
|
9172
|
-
"aria-label": showApiKey ? "Hide API key" : "Show API key",
|
|
9173
|
-
children: showApiKey ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-4" })
|
|
9174
|
-
}
|
|
9175
|
-
),
|
|
9176
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9177
|
-
"button",
|
|
9178
|
-
{
|
|
9179
|
-
type: "button",
|
|
9180
|
-
onClick: handleCopy,
|
|
9181
|
-
className: "p-1 text-muted-foreground transition-colors hover:text-foreground",
|
|
9182
|
-
"aria-label": "Copy API key",
|
|
9183
|
-
children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4 text-cyan-300" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-4" })
|
|
9184
|
-
}
|
|
9185
|
-
)
|
|
9154
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
9155
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3.5 text-emerald-500" }),
|
|
9156
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "API key configured" })
|
|
9186
9157
|
] }),
|
|
9187
9158
|
provider.models !== void 0 && provider.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1", children: provider.models.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsx(ModelChip, { provider, model }, model)) }),
|
|
9188
9159
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-2", children: [
|
|
@@ -9254,6 +9225,25 @@ function ProviderCard({
|
|
|
9254
9225
|
}
|
|
9255
9226
|
);
|
|
9256
9227
|
}
|
|
9228
|
+
function hasEndpoint(value) {
|
|
9229
|
+
return value !== void 0 && value.trim() !== "";
|
|
9230
|
+
}
|
|
9231
|
+
function deriveProviderCapabilities(provider) {
|
|
9232
|
+
const anthropic = hasEndpoint(provider?.anthropicBaseUrl);
|
|
9233
|
+
const openaiChat = hasEndpoint(provider?.openaiBaseUrl);
|
|
9234
|
+
const openaiResponses = hasEndpoint(provider?.openaiResponsesBaseUrl);
|
|
9235
|
+
return {
|
|
9236
|
+
anthropic,
|
|
9237
|
+
openai: openaiChat || openaiResponses,
|
|
9238
|
+
openaiChat,
|
|
9239
|
+
openaiResponses
|
|
9240
|
+
};
|
|
9241
|
+
}
|
|
9242
|
+
function endpointForCapability(enabled, value, clearWhenDisabled) {
|
|
9243
|
+
if (!enabled) return clearWhenDisabled ? "" : void 0;
|
|
9244
|
+
const trimmed = value.trim();
|
|
9245
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
9246
|
+
}
|
|
9257
9247
|
const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
|
|
9258
9248
|
const ZHIPU_CODING_PROVIDER_KEYWORDS = [
|
|
9259
9249
|
"zhipu coding",
|
|
@@ -9477,8 +9467,9 @@ function EndpointUrlPreview({
|
|
|
9477
9467
|
] });
|
|
9478
9468
|
}
|
|
9479
9469
|
function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
9470
|
+
const initialCapabilities = deriveProviderCapabilities(provider);
|
|
9480
9471
|
const [name, setName] = reactExports.useState(provider?.name ?? "");
|
|
9481
|
-
const [apiKey, setApiKey] = reactExports.useState(
|
|
9472
|
+
const [apiKey, setApiKey] = reactExports.useState("");
|
|
9482
9473
|
const [showApiKey, setShowApiKey] = reactExports.useState(false);
|
|
9483
9474
|
const [copied, setCopied] = reactExports.useState(false);
|
|
9484
9475
|
const initialModels = provider?.models;
|
|
@@ -9491,7 +9482,12 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9491
9482
|
initialModels !== void 0 && initialModels.length > 0 ? initialModels : [""]
|
|
9492
9483
|
)
|
|
9493
9484
|
);
|
|
9494
|
-
const [
|
|
9485
|
+
const [supportsAnthropic, setSupportsAnthropic] = reactExports.useState(initialCapabilities.anthropic);
|
|
9486
|
+
const [supportsOpenai, setSupportsOpenai] = reactExports.useState(initialCapabilities.openai);
|
|
9487
|
+
const [supportsOpenaiChat, setSupportsOpenaiChat] = reactExports.useState(initialCapabilities.openaiChat);
|
|
9488
|
+
const [supportsOpenaiResponses, setSupportsOpenaiResponses] = reactExports.useState(
|
|
9489
|
+
initialCapabilities.openaiResponses
|
|
9490
|
+
);
|
|
9495
9491
|
const [anthropicBaseUrl, setAnthropicBaseUrl] = reactExports.useState(
|
|
9496
9492
|
stripEndpointSuffix(provider?.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
|
|
9497
9493
|
);
|
|
@@ -9541,10 +9537,16 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9541
9537
|
reactExports.useEffect(() => {
|
|
9542
9538
|
if (provider) {
|
|
9543
9539
|
setName(provider.name);
|
|
9544
|
-
setApiKey(
|
|
9540
|
+
setApiKey("");
|
|
9541
|
+
setShowApiKey(false);
|
|
9545
9542
|
const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
|
|
9546
9543
|
setModels(nextModels);
|
|
9547
9544
|
setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
|
|
9545
|
+
const nextCapabilities = deriveProviderCapabilities(provider);
|
|
9546
|
+
setSupportsAnthropic(nextCapabilities.anthropic);
|
|
9547
|
+
setSupportsOpenai(nextCapabilities.openai);
|
|
9548
|
+
setSupportsOpenaiChat(nextCapabilities.openaiChat);
|
|
9549
|
+
setSupportsOpenaiResponses(nextCapabilities.openaiResponses);
|
|
9548
9550
|
setAnthropicBaseUrl(
|
|
9549
9551
|
stripEndpointSuffix(provider.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT)
|
|
9550
9552
|
);
|
|
@@ -9596,20 +9598,38 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9596
9598
|
if (!name.trim()) {
|
|
9597
9599
|
newErrors.name = "Name is required";
|
|
9598
9600
|
}
|
|
9599
|
-
if (!apiKey.trim()) {
|
|
9601
|
+
if (provider === void 0 && !apiKey.trim()) {
|
|
9600
9602
|
newErrors.apiKey = "API key is required";
|
|
9601
9603
|
}
|
|
9602
9604
|
if (models.length === 0 || models.every((m) => !m.trim())) {
|
|
9603
9605
|
newErrors.models = "At least one model is required";
|
|
9604
9606
|
}
|
|
9605
|
-
if (
|
|
9606
|
-
newErrors.
|
|
9607
|
+
if (!supportsAnthropic && !supportsOpenai) {
|
|
9608
|
+
newErrors.capabilities = "Select at least one API capability";
|
|
9607
9609
|
}
|
|
9608
|
-
if (
|
|
9609
|
-
newErrors.
|
|
9610
|
+
if (supportsOpenai && !supportsOpenaiChat && !supportsOpenaiResponses) {
|
|
9611
|
+
newErrors.openaiCapabilities = "Select Chat Completions, Responses, or both";
|
|
9610
9612
|
}
|
|
9611
|
-
if (
|
|
9612
|
-
|
|
9613
|
+
if (supportsAnthropic) {
|
|
9614
|
+
if (anthropicBaseUrl.trim() === "") {
|
|
9615
|
+
newErrors.anthropicBaseUrl = "Anthropic base URL is required";
|
|
9616
|
+
} else if (!isValidUrl(anthropicBaseUrl.trim())) {
|
|
9617
|
+
newErrors.anthropicBaseUrl = "Invalid URL format";
|
|
9618
|
+
}
|
|
9619
|
+
}
|
|
9620
|
+
if (supportsOpenai && supportsOpenaiChat) {
|
|
9621
|
+
if (openaiBaseUrl.trim() === "") {
|
|
9622
|
+
newErrors.openaiBaseUrl = "OpenAI Chat base URL is required";
|
|
9623
|
+
} else if (!isValidUrl(openaiBaseUrl.trim())) {
|
|
9624
|
+
newErrors.openaiBaseUrl = "Invalid URL format";
|
|
9625
|
+
}
|
|
9626
|
+
}
|
|
9627
|
+
if (supportsOpenai && supportsOpenaiResponses) {
|
|
9628
|
+
if (openaiResponsesBaseUrl.trim() === "") {
|
|
9629
|
+
newErrors.openaiResponsesBaseUrl = "OpenAI Responses base URL is required";
|
|
9630
|
+
} else if (!isValidUrl(openaiResponsesBaseUrl.trim())) {
|
|
9631
|
+
newErrors.openaiResponsesBaseUrl = "Invalid URL format";
|
|
9632
|
+
}
|
|
9613
9633
|
}
|
|
9614
9634
|
if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
|
|
9615
9635
|
newErrors.modelMetadataUrl = "Invalid URL format";
|
|
@@ -9627,9 +9647,6 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9627
9647
|
newErrors[`modelMetadata.${index}.outputLimit`] = "Output limit should not exceed context window";
|
|
9628
9648
|
}
|
|
9629
9649
|
});
|
|
9630
|
-
if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim() && !openaiResponsesBaseUrl.trim()) {
|
|
9631
|
-
newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
|
|
9632
|
-
}
|
|
9633
9650
|
setErrors(newErrors);
|
|
9634
9651
|
return Object.keys(newErrors).length === 0;
|
|
9635
9652
|
}
|
|
@@ -9736,14 +9753,27 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9736
9753
|
e.preventDefault();
|
|
9737
9754
|
if (!validate()) return;
|
|
9738
9755
|
setIsSubmitting(true);
|
|
9756
|
+
const clearDisabledEndpoints = provider !== void 0;
|
|
9739
9757
|
try {
|
|
9740
9758
|
onSubmit({
|
|
9741
9759
|
name: name.trim(),
|
|
9742
9760
|
apiKey: apiKey.trim(),
|
|
9743
9761
|
models: models.map((m) => m.trim()).filter((m) => m !== ""),
|
|
9744
|
-
anthropicBaseUrl:
|
|
9745
|
-
|
|
9746
|
-
|
|
9762
|
+
anthropicBaseUrl: endpointForCapability(
|
|
9763
|
+
supportsAnthropic,
|
|
9764
|
+
anthropicBaseUrl,
|
|
9765
|
+
clearDisabledEndpoints
|
|
9766
|
+
),
|
|
9767
|
+
openaiBaseUrl: endpointForCapability(
|
|
9768
|
+
supportsOpenai && supportsOpenaiChat,
|
|
9769
|
+
openaiBaseUrl,
|
|
9770
|
+
clearDisabledEndpoints
|
|
9771
|
+
),
|
|
9772
|
+
openaiResponsesBaseUrl: endpointForCapability(
|
|
9773
|
+
supportsOpenai && supportsOpenaiResponses,
|
|
9774
|
+
openaiResponsesBaseUrl,
|
|
9775
|
+
clearDisabledEndpoints
|
|
9776
|
+
),
|
|
9747
9777
|
apiDocsUrl: apiDocsUrl.trim() || void 0,
|
|
9748
9778
|
modelMetadataUrl: modelMetadataUrl.trim() || void 0,
|
|
9749
9779
|
modelMetadata: buildModelMetadata(),
|
|
@@ -9772,6 +9802,205 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9772
9802
|
),
|
|
9773
9803
|
errors.name !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.name })
|
|
9774
9804
|
] }),
|
|
9805
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("fieldset", { className: "space-y-3", children: [
|
|
9806
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("legend", { className: "text-sm font-medium", children: [
|
|
9807
|
+
"Capabilities ",
|
|
9808
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
9809
|
+
] }),
|
|
9810
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "divide-y divide-border overflow-hidden rounded-md border border-border", children: [
|
|
9811
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: supportsOpenai ? "bg-primary/[0.04] px-3 py-3" : "px-3 py-3", children: [
|
|
9812
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9813
|
+
"label",
|
|
9814
|
+
{
|
|
9815
|
+
htmlFor: "provider-capability-openai",
|
|
9816
|
+
className: "flex cursor-pointer items-start gap-3",
|
|
9817
|
+
children: [
|
|
9818
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9819
|
+
"input",
|
|
9820
|
+
{
|
|
9821
|
+
id: "provider-capability-openai",
|
|
9822
|
+
type: "checkbox",
|
|
9823
|
+
checked: supportsOpenai,
|
|
9824
|
+
onChange: (event) => setSupportsOpenai(event.target.checked),
|
|
9825
|
+
className: "mt-0.5 size-4 shrink-0 accent-primary"
|
|
9826
|
+
}
|
|
9827
|
+
),
|
|
9828
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
|
|
9829
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-sm font-medium", children: "OpenAI" }),
|
|
9830
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-xs text-muted-foreground", children: "Chat Completions and Responses" })
|
|
9831
|
+
] })
|
|
9832
|
+
]
|
|
9833
|
+
}
|
|
9834
|
+
),
|
|
9835
|
+
supportsOpenai && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-7 mt-3 flex flex-wrap gap-x-5 gap-y-2 border-t border-border/70 pt-3", children: [
|
|
9836
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9837
|
+
"label",
|
|
9838
|
+
{
|
|
9839
|
+
htmlFor: "provider-capability-openai-chat",
|
|
9840
|
+
className: "flex cursor-pointer items-center gap-2 text-sm",
|
|
9841
|
+
children: [
|
|
9842
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9843
|
+
"input",
|
|
9844
|
+
{
|
|
9845
|
+
id: "provider-capability-openai-chat",
|
|
9846
|
+
type: "checkbox",
|
|
9847
|
+
checked: supportsOpenaiChat,
|
|
9848
|
+
onChange: (event) => setSupportsOpenaiChat(event.target.checked),
|
|
9849
|
+
className: "size-4 shrink-0 accent-primary"
|
|
9850
|
+
}
|
|
9851
|
+
),
|
|
9852
|
+
"Chat Completions"
|
|
9853
|
+
]
|
|
9854
|
+
}
|
|
9855
|
+
),
|
|
9856
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9857
|
+
"label",
|
|
9858
|
+
{
|
|
9859
|
+
htmlFor: "provider-capability-openai-responses",
|
|
9860
|
+
className: "flex cursor-pointer items-center gap-2 text-sm",
|
|
9861
|
+
children: [
|
|
9862
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9863
|
+
"input",
|
|
9864
|
+
{
|
|
9865
|
+
id: "provider-capability-openai-responses",
|
|
9866
|
+
type: "checkbox",
|
|
9867
|
+
checked: supportsOpenaiResponses,
|
|
9868
|
+
onChange: (event) => setSupportsOpenaiResponses(event.target.checked),
|
|
9869
|
+
className: "size-4 shrink-0 accent-primary"
|
|
9870
|
+
}
|
|
9871
|
+
),
|
|
9872
|
+
"Responses"
|
|
9873
|
+
]
|
|
9874
|
+
}
|
|
9875
|
+
)
|
|
9876
|
+
] }),
|
|
9877
|
+
errors.openaiCapabilities !== void 0 && supportsOpenai && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ml-7 mt-2 text-xs text-destructive", children: errors.openaiCapabilities })
|
|
9878
|
+
] }),
|
|
9879
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9880
|
+
"label",
|
|
9881
|
+
{
|
|
9882
|
+
htmlFor: "provider-capability-anthropic",
|
|
9883
|
+
className: `flex cursor-pointer items-start gap-3 px-3 py-3 ${supportsAnthropic ? "bg-primary/[0.04]" : ""}`,
|
|
9884
|
+
children: [
|
|
9885
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9886
|
+
"input",
|
|
9887
|
+
{
|
|
9888
|
+
id: "provider-capability-anthropic",
|
|
9889
|
+
type: "checkbox",
|
|
9890
|
+
checked: supportsAnthropic,
|
|
9891
|
+
onChange: (event) => setSupportsAnthropic(event.target.checked),
|
|
9892
|
+
className: "mt-0.5 size-4 shrink-0 accent-primary"
|
|
9893
|
+
}
|
|
9894
|
+
),
|
|
9895
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "min-w-0", children: [
|
|
9896
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-sm font-medium", children: "Anthropic" }),
|
|
9897
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block text-xs text-muted-foreground", children: "Messages" })
|
|
9898
|
+
] })
|
|
9899
|
+
]
|
|
9900
|
+
}
|
|
9901
|
+
)
|
|
9902
|
+
] }),
|
|
9903
|
+
errors.capabilities !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.capabilities })
|
|
9904
|
+
] }),
|
|
9905
|
+
(supportsAnthropic || supportsOpenai && (supportsOpenaiChat || supportsOpenaiResponses)) && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-5 border-l border-border pl-4", "aria-label": "API endpoints", children: [
|
|
9906
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-sm font-medium", children: "Endpoint Configuration" }),
|
|
9907
|
+
supportsAnthropic && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
9908
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-anthropic-base-url", className: "text-sm font-medium", children: [
|
|
9909
|
+
"Anthropic Messages Base URL ",
|
|
9910
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
9911
|
+
] }),
|
|
9912
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
9913
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9914
|
+
"input",
|
|
9915
|
+
{
|
|
9916
|
+
id: "provider-anthropic-base-url",
|
|
9917
|
+
type: "text",
|
|
9918
|
+
value: anthropicBaseUrl,
|
|
9919
|
+
onChange: (event) => {
|
|
9920
|
+
setManualAnthropicUrlOverride(true);
|
|
9921
|
+
setAnthropicBaseUrl(event.target.value);
|
|
9922
|
+
},
|
|
9923
|
+
placeholder: "https://api.anthropic.com",
|
|
9924
|
+
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
9925
|
+
}
|
|
9926
|
+
),
|
|
9927
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: ANTHROPIC_MESSAGES_ENDPOINT })
|
|
9928
|
+
] }),
|
|
9929
|
+
errors.anthropicBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.anthropicBaseUrl }),
|
|
9930
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9931
|
+
EndpointUrlPreview,
|
|
9932
|
+
{
|
|
9933
|
+
baseUrl: anthropicBaseUrl,
|
|
9934
|
+
endpoint: ANTHROPIC_MESSAGES_ENDPOINT,
|
|
9935
|
+
providerName: name
|
|
9936
|
+
}
|
|
9937
|
+
)
|
|
9938
|
+
] }),
|
|
9939
|
+
supportsOpenai && supportsOpenaiChat && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
9940
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: [
|
|
9941
|
+
"OpenAI Chat Base URL ",
|
|
9942
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
9943
|
+
] }),
|
|
9944
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
9945
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9946
|
+
"input",
|
|
9947
|
+
{
|
|
9948
|
+
id: "provider-openai-base-url",
|
|
9949
|
+
type: "text",
|
|
9950
|
+
value: openaiBaseUrl,
|
|
9951
|
+
onChange: (event) => {
|
|
9952
|
+
setManualOpenaiUrlOverride(true);
|
|
9953
|
+
setOpenaiBaseUrl(event.target.value);
|
|
9954
|
+
},
|
|
9955
|
+
placeholder: "https://api.openai.com",
|
|
9956
|
+
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
9957
|
+
}
|
|
9958
|
+
),
|
|
9959
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_CHAT_COMPLETIONS_ENDPOINT })
|
|
9960
|
+
] }),
|
|
9961
|
+
errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
|
|
9962
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9963
|
+
EndpointUrlPreview,
|
|
9964
|
+
{
|
|
9965
|
+
baseUrl: openaiBaseUrl,
|
|
9966
|
+
endpoint: OPENAI_CHAT_COMPLETIONS_ENDPOINT,
|
|
9967
|
+
providerName: name
|
|
9968
|
+
}
|
|
9969
|
+
)
|
|
9970
|
+
] }),
|
|
9971
|
+
supportsOpenai && supportsOpenaiResponses && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
9972
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-openai-responses-base-url", className: "text-sm font-medium", children: [
|
|
9973
|
+
"OpenAI Responses Base URL ",
|
|
9974
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
9975
|
+
] }),
|
|
9976
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
9977
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9978
|
+
"input",
|
|
9979
|
+
{
|
|
9980
|
+
id: "provider-openai-responses-base-url",
|
|
9981
|
+
type: "text",
|
|
9982
|
+
value: openaiResponsesBaseUrl,
|
|
9983
|
+
onChange: (event) => {
|
|
9984
|
+
setManualOpenaiResponsesUrlOverride(true);
|
|
9985
|
+
setOpenaiResponsesBaseUrl(event.target.value);
|
|
9986
|
+
},
|
|
9987
|
+
placeholder: "https://api.openai.com",
|
|
9988
|
+
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
9989
|
+
}
|
|
9990
|
+
),
|
|
9991
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_RESPONSES_ENDPOINT })
|
|
9992
|
+
] }),
|
|
9993
|
+
errors.openaiResponsesBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiResponsesBaseUrl }),
|
|
9994
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9995
|
+
EndpointUrlPreview,
|
|
9996
|
+
{
|
|
9997
|
+
baseUrl: openaiResponsesBaseUrl,
|
|
9998
|
+
endpoint: OPENAI_RESPONSES_ENDPOINT,
|
|
9999
|
+
providerName: name
|
|
10000
|
+
}
|
|
10001
|
+
)
|
|
10002
|
+
] })
|
|
10003
|
+
] }),
|
|
9775
10004
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
9776
10005
|
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-source", className: "text-sm font-medium", children: "Token Source" }),
|
|
9777
10006
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
@@ -9813,21 +10042,17 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9813
10042
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
9814
10043
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("label", { htmlFor: "provider-apikey", className: "text-sm font-medium", children: [
|
|
9815
10044
|
"API Key ",
|
|
9816
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
10045
|
+
provider === void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-destructive", children: "*" })
|
|
9817
10046
|
] }),
|
|
9818
10047
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9819
10048
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9820
10049
|
"input",
|
|
9821
10050
|
{
|
|
9822
10051
|
id: "provider-apikey",
|
|
9823
|
-
type: "text",
|
|
9824
|
-
value:
|
|
10052
|
+
type: showApiKey ? "text" : "password",
|
|
10053
|
+
value: apiKey,
|
|
9825
10054
|
onChange: (e) => setApiKey(e.target.value),
|
|
9826
|
-
|
|
9827
|
-
if (!showApiKey && apiKey.length > 0) setShowApiKey(true);
|
|
9828
|
-
},
|
|
9829
|
-
placeholder: "sk-ant-...",
|
|
9830
|
-
readOnly: !showApiKey && apiKey.length > 0,
|
|
10055
|
+
placeholder: provider === void 0 ? "sk-ant-..." : "Leave blank to keep current key",
|
|
9831
10056
|
className: "flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
9832
10057
|
}
|
|
9833
10058
|
),
|
|
@@ -9852,6 +10077,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
9852
10077
|
}
|
|
9853
10078
|
)
|
|
9854
10079
|
] }),
|
|
10080
|
+
provider !== void 0 && apiKey.trim() === "" && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "A key is configured. Enter a new value only to rotate it." }),
|
|
9855
10081
|
errors.apiKey !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.apiKey })
|
|
9856
10082
|
] }),
|
|
9857
10083
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
@@ -10025,127 +10251,6 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
10025
10251
|
),
|
|
10026
10252
|
errors.models !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.models })
|
|
10027
10253
|
] }),
|
|
10028
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
10029
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-border bg-muted/40 flex gap-1 rounded-[8px] p-1", children: [
|
|
10030
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
10031
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10032
|
-
"button",
|
|
10033
|
-
{
|
|
10034
|
-
type: "button",
|
|
10035
|
-
onClick: () => setActiveTab("anthropic"),
|
|
10036
|
-
className: `px-3 py-2 text-sm font-medium border-b-2 transition-colors ${activeTab === "anthropic" ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
|
|
10037
|
-
children: "Anthropic Format"
|
|
10038
|
-
}
|
|
10039
|
-
) }),
|
|
10040
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Anthropic Messages API format" })
|
|
10041
|
-
] }) }),
|
|
10042
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
|
|
10043
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10044
|
-
"button",
|
|
10045
|
-
{
|
|
10046
|
-
type: "button",
|
|
10047
|
-
onClick: () => setActiveTab("openai"),
|
|
10048
|
-
className: `px-3 py-2 text-sm font-medium border-b-2 transition-colors ${activeTab === "openai" ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
|
|
10049
|
-
children: "OpenAI Format"
|
|
10050
|
-
}
|
|
10051
|
-
) }),
|
|
10052
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "OpenAI Chat Completions API format" })
|
|
10053
|
-
] }) })
|
|
10054
|
-
] }),
|
|
10055
|
-
errors.format !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.format })
|
|
10056
|
-
] }),
|
|
10057
|
-
activeTab === "anthropic" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
10058
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-anthropic-base-url", className: "text-sm font-medium", children: "Anthropic Base URL" }),
|
|
10059
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
10060
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10061
|
-
"input",
|
|
10062
|
-
{
|
|
10063
|
-
id: "provider-anthropic-base-url",
|
|
10064
|
-
type: "text",
|
|
10065
|
-
value: anthropicBaseUrl,
|
|
10066
|
-
onChange: (e) => {
|
|
10067
|
-
setManualAnthropicUrlOverride(true);
|
|
10068
|
-
setAnthropicBaseUrl(e.target.value);
|
|
10069
|
-
},
|
|
10070
|
-
placeholder: "https://api.anthropic.com",
|
|
10071
|
-
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
10072
|
-
}
|
|
10073
|
-
),
|
|
10074
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: ANTHROPIC_MESSAGES_ENDPOINT })
|
|
10075
|
-
] }),
|
|
10076
|
-
errors.anthropicBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.anthropicBaseUrl }),
|
|
10077
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10078
|
-
EndpointUrlPreview,
|
|
10079
|
-
{
|
|
10080
|
-
baseUrl: anthropicBaseUrl,
|
|
10081
|
-
endpoint: ANTHROPIC_MESSAGES_ENDPOINT,
|
|
10082
|
-
providerName: name
|
|
10083
|
-
}
|
|
10084
|
-
),
|
|
10085
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Anthropic-compatible base URL. Leave empty if this provider does not support Anthropic format." })
|
|
10086
|
-
] }),
|
|
10087
|
-
activeTab === "openai" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
|
|
10088
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
10089
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-base-url", className: "text-sm font-medium", children: "OpenAI Chat Base URL" }),
|
|
10090
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
10091
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10092
|
-
"input",
|
|
10093
|
-
{
|
|
10094
|
-
id: "provider-openai-base-url",
|
|
10095
|
-
type: "text",
|
|
10096
|
-
value: openaiBaseUrl,
|
|
10097
|
-
onChange: (e) => {
|
|
10098
|
-
setManualOpenaiUrlOverride(true);
|
|
10099
|
-
setOpenaiBaseUrl(e.target.value);
|
|
10100
|
-
},
|
|
10101
|
-
placeholder: "https://api.openai.com",
|
|
10102
|
-
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
10103
|
-
}
|
|
10104
|
-
),
|
|
10105
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_CHAT_COMPLETIONS_ENDPOINT })
|
|
10106
|
-
] }),
|
|
10107
|
-
errors.openaiBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiBaseUrl }),
|
|
10108
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10109
|
-
EndpointUrlPreview,
|
|
10110
|
-
{
|
|
10111
|
-
baseUrl: openaiBaseUrl,
|
|
10112
|
-
endpoint: OPENAI_CHAT_COMPLETIONS_ENDPOINT,
|
|
10113
|
-
providerName: name
|
|
10114
|
-
}
|
|
10115
|
-
),
|
|
10116
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "OpenAI Chat Completions-compatible base URL. Leave empty if this provider only supports the Responses API." })
|
|
10117
|
-
] }),
|
|
10118
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
10119
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-openai-responses-base-url", className: "text-sm font-medium", children: "OpenAI Responses Base URL" }),
|
|
10120
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]", children: [
|
|
10121
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10122
|
-
"input",
|
|
10123
|
-
{
|
|
10124
|
-
id: "provider-openai-responses-base-url",
|
|
10125
|
-
type: "text",
|
|
10126
|
-
value: openaiResponsesBaseUrl,
|
|
10127
|
-
onChange: (e) => {
|
|
10128
|
-
setManualOpenaiResponsesUrlOverride(true);
|
|
10129
|
-
setOpenaiResponsesBaseUrl(e.target.value);
|
|
10130
|
-
},
|
|
10131
|
-
placeholder: "https://api.openai.com",
|
|
10132
|
-
className: "min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
10133
|
-
}
|
|
10134
|
-
),
|
|
10135
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground", children: OPENAI_RESPONSES_ENDPOINT })
|
|
10136
|
-
] }),
|
|
10137
|
-
errors.openaiResponsesBaseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.openaiResponsesBaseUrl }),
|
|
10138
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
10139
|
-
EndpointUrlPreview,
|
|
10140
|
-
{
|
|
10141
|
-
baseUrl: openaiResponsesBaseUrl,
|
|
10142
|
-
endpoint: OPENAI_RESPONSES_ENDPOINT,
|
|
10143
|
-
providerName: name
|
|
10144
|
-
}
|
|
10145
|
-
),
|
|
10146
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Optional Responses-compatible base URL. Set only when the provider supports the Responses API." })
|
|
10147
|
-
] })
|
|
10148
|
-
] }),
|
|
10149
10254
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
10150
10255
|
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-api-docs-url", className: "text-sm font-medium", children: "API Docs URL" }),
|
|
10151
10256
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -10191,14 +10296,21 @@ const PROVIDER_DELETE_TIMEOUT_MS = 1e4;
|
|
|
10191
10296
|
const PROVIDER_EXPORT_TIMEOUT_MS = 2e4;
|
|
10192
10297
|
const PROVIDER_IMPORT_TIMEOUT_MS = 2e4;
|
|
10193
10298
|
const PROVIDER_METADATA_TIMEOUT_MS = 3e4;
|
|
10194
|
-
function
|
|
10299
|
+
function endpointPayloadValue(value, preserveEmptyEndpoints) {
|
|
10300
|
+
if ((value?.length ?? 0) > 0) return value;
|
|
10301
|
+
return preserveEmptyEndpoints && value !== void 0 ? "" : void 0;
|
|
10302
|
+
}
|
|
10303
|
+
function createProviderPayload(data, preserveEmptyEndpoints = false) {
|
|
10195
10304
|
return {
|
|
10196
10305
|
name: data.name,
|
|
10197
|
-
apiKey: data.apiKey,
|
|
10306
|
+
...data.apiKey.trim() === "" ? {} : { apiKey: data.apiKey },
|
|
10198
10307
|
models: data.models,
|
|
10199
|
-
anthropicBaseUrl: (data.anthropicBaseUrl
|
|
10200
|
-
openaiBaseUrl: (data.openaiBaseUrl
|
|
10201
|
-
openaiResponsesBaseUrl: (
|
|
10308
|
+
anthropicBaseUrl: endpointPayloadValue(data.anthropicBaseUrl, preserveEmptyEndpoints),
|
|
10309
|
+
openaiBaseUrl: endpointPayloadValue(data.openaiBaseUrl, preserveEmptyEndpoints),
|
|
10310
|
+
openaiResponsesBaseUrl: endpointPayloadValue(
|
|
10311
|
+
data.openaiResponsesBaseUrl,
|
|
10312
|
+
preserveEmptyEndpoints
|
|
10313
|
+
),
|
|
10202
10314
|
apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : void 0,
|
|
10203
10315
|
modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : void 0,
|
|
10204
10316
|
modelMetadata: data.modelMetadata,
|
|
@@ -10481,7 +10593,7 @@ function ProvidersPanel({
|
|
|
10481
10593
|
{
|
|
10482
10594
|
method: "PUT",
|
|
10483
10595
|
headers: { "Content-Type": "application/json" },
|
|
10484
|
-
body: JSON.stringify(createProviderPayload(data))
|
|
10596
|
+
body: JSON.stringify(createProviderPayload(data, true))
|
|
10485
10597
|
},
|
|
10486
10598
|
() => "Failed to update provider"
|
|
10487
10599
|
);
|
|
@@ -10491,7 +10603,7 @@ function ProvidersPanel({
|
|
|
10491
10603
|
await refreshProviderMetadata(updated.id, data.modelMetadataUrl, false);
|
|
10492
10604
|
}
|
|
10493
10605
|
refreshProviders();
|
|
10494
|
-
const criticalFieldsChanged =
|
|
10606
|
+
const criticalFieldsChanged = data.apiKey.trim() !== "" || JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) || (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") || (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "") || (data.openaiResponsesBaseUrl ?? "") !== (editingProvider.openaiResponsesBaseUrl ?? "");
|
|
10495
10607
|
if (criticalFieldsChanged) {
|
|
10496
10608
|
await runTest(updated.id);
|
|
10497
10609
|
}
|
|
@@ -11138,9 +11250,9 @@ function McpSettingsTab() {
|
|
|
11138
11250
|
value: `agent-inspector-mcp doctor --url ${endpoint}`
|
|
11139
11251
|
},
|
|
11140
11252
|
{
|
|
11141
|
-
id: "
|
|
11142
|
-
label: "
|
|
11143
|
-
value: "
|
|
11253
|
+
id: "writes",
|
|
11254
|
+
label: "Enable write tools",
|
|
11255
|
+
value: "AGENT_INSPECTOR_MCP_WRITES=1 agent-inspector"
|
|
11144
11256
|
},
|
|
11145
11257
|
{
|
|
11146
11258
|
id: "capabilities",
|
|
@@ -12134,11 +12246,11 @@ function AgentLabDialog({ currentSessionId, logCount }) {
|
|
|
12134
12246
|
] })
|
|
12135
12247
|
] });
|
|
12136
12248
|
}
|
|
12137
|
-
const INSPECTOR_PET_SIZE =
|
|
12249
|
+
const INSPECTOR_PET_SIZE = 112;
|
|
12138
12250
|
const INSPECTOR_PET_MARGIN = 14;
|
|
12139
|
-
const
|
|
12140
|
-
const INSPECTOR_PET_MIN_STEP =
|
|
12141
|
-
const INSPECTOR_PET_MAX_STEP =
|
|
12251
|
+
const INSPECTOR_PET_EDGE_GAP = 20;
|
|
12252
|
+
const INSPECTOR_PET_MIN_STEP = 36;
|
|
12253
|
+
const INSPECTOR_PET_MAX_STEP = 92;
|
|
12142
12254
|
const INSPECTOR_PET_SLEEP_AFTER_MS = 45e3;
|
|
12143
12255
|
const INSPECTOR_PET_PANEL_WIDTH = 192;
|
|
12144
12256
|
const INSPECTOR_PET_PANEL_HEIGHT = 68;
|
|
@@ -12155,33 +12267,27 @@ function clampUnit(value) {
|
|
|
12155
12267
|
return Math.min(Math.max(value, 0), 1);
|
|
12156
12268
|
}
|
|
12157
12269
|
function maximumPetX(viewport) {
|
|
12158
|
-
const
|
|
12159
|
-
return Math.max(
|
|
12160
|
-
-edgePeek,
|
|
12161
|
-
viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
|
|
12162
|
-
);
|
|
12270
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12271
|
+
return Math.max(edgeGap, viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
|
|
12163
12272
|
}
|
|
12164
12273
|
function maximumPetY(viewport) {
|
|
12165
|
-
const
|
|
12166
|
-
return Math.max(
|
|
12167
|
-
-edgePeek,
|
|
12168
|
-
viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
|
|
12169
|
-
);
|
|
12274
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12275
|
+
return Math.max(edgeGap, viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) - edgeGap);
|
|
12170
12276
|
}
|
|
12171
12277
|
function clampInspectorPetPosition(position, viewport) {
|
|
12172
|
-
const
|
|
12278
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12173
12279
|
return {
|
|
12174
|
-
x: Math.min(Math.max(position.x,
|
|
12175
|
-
y: Math.min(Math.max(position.y,
|
|
12280
|
+
x: Math.min(Math.max(position.x, edgeGap), maximumPetX(viewport)),
|
|
12281
|
+
y: Math.min(Math.max(position.y, edgeGap), maximumPetY(viewport))
|
|
12176
12282
|
};
|
|
12177
12283
|
}
|
|
12178
12284
|
function nearestInspectorPetEdge(position, viewport) {
|
|
12179
12285
|
const clamped = clampInspectorPetPosition(position, viewport);
|
|
12180
|
-
const
|
|
12286
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12181
12287
|
const distances = [
|
|
12182
|
-
{ edge: "left", distance: clamped.x
|
|
12288
|
+
{ edge: "left", distance: clamped.x - edgeGap },
|
|
12183
12289
|
{ edge: "right", distance: maximumPetX(viewport) - clamped.x },
|
|
12184
|
-
{ edge: "top", distance: clamped.y
|
|
12290
|
+
{ edge: "top", distance: clamped.y - edgeGap },
|
|
12185
12291
|
{ edge: "bottom", distance: maximumPetY(viewport) - clamped.y }
|
|
12186
12292
|
];
|
|
12187
12293
|
return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
|
|
@@ -12189,33 +12295,26 @@ function nearestInspectorPetEdge(position, viewport) {
|
|
|
12189
12295
|
function snapInspectorPetToEdge(position, viewport) {
|
|
12190
12296
|
const clamped = clampInspectorPetPosition(position, viewport);
|
|
12191
12297
|
const edge = nearestInspectorPetEdge(clamped, viewport);
|
|
12192
|
-
const
|
|
12298
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12193
12299
|
switch (edge) {
|
|
12194
12300
|
case "left":
|
|
12195
|
-
return { x:
|
|
12301
|
+
return { x: edgeGap, y: clamped.y };
|
|
12196
12302
|
case "right":
|
|
12197
12303
|
return { x: maximumPetX(viewport), y: clamped.y };
|
|
12198
12304
|
case "top":
|
|
12199
|
-
return { x: clamped.x, y:
|
|
12305
|
+
return { x: clamped.x, y: edgeGap };
|
|
12200
12306
|
case "bottom":
|
|
12201
12307
|
return { x: clamped.x, y: maximumPetY(viewport) };
|
|
12202
12308
|
}
|
|
12203
12309
|
}
|
|
12204
|
-
function randomInspectorPetEdgePosition(viewport,
|
|
12310
|
+
function randomInspectorPetEdgePosition(viewport, positionSample) {
|
|
12205
12311
|
const maxX = maximumPetX(viewport);
|
|
12206
12312
|
const maxY = maximumPetY(viewport);
|
|
12207
|
-
const
|
|
12208
|
-
|
|
12209
|
-
|
|
12210
|
-
|
|
12211
|
-
|
|
12212
|
-
case 0:
|
|
12213
|
-
return { x: -edgePeek, y: randomY };
|
|
12214
|
-
case 1:
|
|
12215
|
-
return { x: maxX, y: randomY };
|
|
12216
|
-
default:
|
|
12217
|
-
return { x: maxX, y: maxY };
|
|
12218
|
-
}
|
|
12313
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12314
|
+
return {
|
|
12315
|
+
x: edgeGap + clampUnit(positionSample) * (maxX - edgeGap),
|
|
12316
|
+
y: maxY
|
|
12317
|
+
};
|
|
12219
12318
|
}
|
|
12220
12319
|
function nextInspectorPetWalkingPosition(position, viewport, stepSample, directionSample) {
|
|
12221
12320
|
const clamped = snapInspectorPetToEdge(position, viewport);
|
|
@@ -12224,12 +12323,12 @@ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directi
|
|
|
12224
12323
|
const edge = nearestInspectorPetEdge(clamped, viewport);
|
|
12225
12324
|
const maxX = maximumPetX(viewport);
|
|
12226
12325
|
const maxY = maximumPetY(viewport);
|
|
12227
|
-
const
|
|
12326
|
+
const edgeGap = scaledMetric(INSPECTOR_PET_EDGE_GAP, viewport);
|
|
12228
12327
|
const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
|
|
12229
12328
|
switch (edge) {
|
|
12230
12329
|
case "left":
|
|
12231
12330
|
return {
|
|
12232
|
-
x:
|
|
12331
|
+
x: edgeGap,
|
|
12233
12332
|
y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
|
|
12234
12333
|
};
|
|
12235
12334
|
case "right":
|
|
@@ -12239,12 +12338,12 @@ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directi
|
|
|
12239
12338
|
};
|
|
12240
12339
|
case "top":
|
|
12241
12340
|
return {
|
|
12242
|
-
x: Math.min(Math.max(clamped.x + step * direction,
|
|
12243
|
-
y:
|
|
12341
|
+
x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
|
|
12342
|
+
y: edgeGap
|
|
12244
12343
|
};
|
|
12245
12344
|
case "bottom":
|
|
12246
12345
|
return {
|
|
12247
|
-
x: Math.min(Math.max(clamped.x + step * direction,
|
|
12346
|
+
x: Math.min(Math.max(clamped.x + step * direction, edgeGap), maxX),
|
|
12248
12347
|
y: maxY
|
|
12249
12348
|
};
|
|
12250
12349
|
}
|
|
@@ -12333,12 +12432,344 @@ function resolveInspectorPetStatus(logs, nowMs) {
|
|
|
12333
12432
|
capturedCount
|
|
12334
12433
|
};
|
|
12335
12434
|
}
|
|
12435
|
+
function createRod(start, end, radius, material) {
|
|
12436
|
+
const direction = end.clone().sub(start);
|
|
12437
|
+
const length = direction.length();
|
|
12438
|
+
const rod = new Mesh(new CylinderGeometry(radius, radius * 0.9, length, 12), material);
|
|
12439
|
+
rod.position.copy(start.clone().add(end).multiplyScalar(0.5));
|
|
12440
|
+
rod.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), direction.normalize());
|
|
12441
|
+
return rod;
|
|
12442
|
+
}
|
|
12443
|
+
function addClaw(root, side, shellMaterial, accentMaterial) {
|
|
12444
|
+
const clawScale = side < 0 ? 1.5 : 0.68;
|
|
12445
|
+
const clawGroup = new Group();
|
|
12446
|
+
const pincers = [];
|
|
12447
|
+
root.add(clawGroup);
|
|
12448
|
+
const shoulder = new Vector3(side * 1.2, 0.25, -0.05);
|
|
12449
|
+
const wrist = new Vector3(side * (1.65 + clawScale * 0.18), 0.5, 0);
|
|
12450
|
+
const arm = createRod(shoulder, wrist, 0.16 * clawScale, accentMaterial);
|
|
12451
|
+
clawGroup.add(arm);
|
|
12452
|
+
const palm = new Mesh(new SphereGeometry(0.55, 24, 16), shellMaterial);
|
|
12453
|
+
palm.position.set(side * (1.95 + clawScale * 0.16), 0.62, 0.05);
|
|
12454
|
+
palm.scale.set(1.05 * clawScale, 0.72 * clawScale, 0.55 * clawScale);
|
|
12455
|
+
clawGroup.add(palm);
|
|
12456
|
+
for (const pincerDirection of [-1, 1]) {
|
|
12457
|
+
const pincer = new Mesh(new SphereGeometry(0.36, 20, 14), accentMaterial);
|
|
12458
|
+
pincer.position.set(
|
|
12459
|
+
side * (2.22 + clawScale * 0.26),
|
|
12460
|
+
0.62 + pincerDirection * 0.24 * clawScale,
|
|
12461
|
+
0.08
|
|
12462
|
+
);
|
|
12463
|
+
pincer.scale.set(1.05 * clawScale, 0.34 * clawScale, 0.42 * clawScale);
|
|
12464
|
+
pincer.rotation.z = pincerDirection * side * 0.42;
|
|
12465
|
+
clawGroup.add(pincer);
|
|
12466
|
+
pincers.push({
|
|
12467
|
+
direction: pincerDirection,
|
|
12468
|
+
mesh: pincer,
|
|
12469
|
+
restRotationZ: pincer.rotation.z,
|
|
12470
|
+
restY: pincer.position.y
|
|
12471
|
+
});
|
|
12472
|
+
}
|
|
12473
|
+
return { group: clawGroup, pincers, scale: clawScale };
|
|
12474
|
+
}
|
|
12475
|
+
function pinchPulse(cycleTime, start, duration) {
|
|
12476
|
+
const progress = (cycleTime - start) / duration;
|
|
12477
|
+
if (progress <= 0 || progress >= 1) return 0;
|
|
12478
|
+
return Math.sin(progress * Math.PI);
|
|
12479
|
+
}
|
|
12480
|
+
function dominantClawGrip(elapsed) {
|
|
12481
|
+
const cycleTime = elapsed % 6.8;
|
|
12482
|
+
return Math.max(pinchPulse(cycleTime, 0.72, 0.82), pinchPulse(cycleTime, 1.72, 0.68));
|
|
12483
|
+
}
|
|
12484
|
+
function addEyes(root, shellMaterial, eyeMaterial) {
|
|
12485
|
+
const eyes = [];
|
|
12486
|
+
for (const side of [-1, 1]) {
|
|
12487
|
+
const stalkStart = new Vector3(side * 0.48, 0.68, 0.05);
|
|
12488
|
+
const stalkEnd = new Vector3(side * 0.1, 0.82, 0.07);
|
|
12489
|
+
const eyeGroup = new Group();
|
|
12490
|
+
eyeGroup.position.copy(stalkStart);
|
|
12491
|
+
eyeGroup.add(createRod(new Vector3(0, 0, 0), stalkEnd, 0.1, shellMaterial));
|
|
12492
|
+
const eye = new Mesh(new SphereGeometry(0.24, 20, 16), eyeMaterial);
|
|
12493
|
+
eye.position.copy(stalkEnd);
|
|
12494
|
+
eye.scale.set(1, 1, 0.85);
|
|
12495
|
+
eyeGroup.add(eye);
|
|
12496
|
+
const pupil = new Mesh(
|
|
12497
|
+
new SphereGeometry(0.105, 16, 12),
|
|
12498
|
+
new MeshPhysicalMaterial({ color: 1120295, roughness: 0.25 })
|
|
12499
|
+
);
|
|
12500
|
+
const pupilRest = new Vector3(stalkEnd.x, stalkEnd.y, stalkEnd.z + 0.2);
|
|
12501
|
+
pupil.position.copy(pupilRest);
|
|
12502
|
+
eyeGroup.add(pupil);
|
|
12503
|
+
root.add(eyeGroup);
|
|
12504
|
+
eyes.push({
|
|
12505
|
+
group: eyeGroup,
|
|
12506
|
+
phase: side < 0 ? 0 : Math.PI / 3,
|
|
12507
|
+
pupil,
|
|
12508
|
+
pupilRest
|
|
12509
|
+
});
|
|
12510
|
+
}
|
|
12511
|
+
return eyes;
|
|
12512
|
+
}
|
|
12513
|
+
function addLegs(root, legMaterial) {
|
|
12514
|
+
const legs = [];
|
|
12515
|
+
for (const side of [-1, 1]) {
|
|
12516
|
+
for (let index = 0; index < 4; index += 1) {
|
|
12517
|
+
const leg = new Group();
|
|
12518
|
+
const baseY = -0.3 - index * 0.22;
|
|
12519
|
+
leg.position.set(side * (0.8 + index * 0.12), baseY, -0.18);
|
|
12520
|
+
leg.add(
|
|
12521
|
+
createRod(
|
|
12522
|
+
new Vector3(0, 0, 0),
|
|
12523
|
+
new Vector3(side * (0.62 + index * 0.06), -0.34, 0),
|
|
12524
|
+
0.095,
|
|
12525
|
+
legMaterial
|
|
12526
|
+
)
|
|
12527
|
+
);
|
|
12528
|
+
leg.add(
|
|
12529
|
+
createRod(
|
|
12530
|
+
new Vector3(side * (0.58 + index * 0.06), -0.31, 0),
|
|
12531
|
+
new Vector3(side * (1.02 + index * 0.08), -0.88 + index * 0.07, 0.04),
|
|
12532
|
+
0.075,
|
|
12533
|
+
legMaterial
|
|
12534
|
+
)
|
|
12535
|
+
);
|
|
12536
|
+
root.add(leg);
|
|
12537
|
+
legs.push({ group: leg, phase: index * Math.PI + (side > 0 ? Math.PI / 2 : 0), side });
|
|
12538
|
+
}
|
|
12539
|
+
}
|
|
12540
|
+
return legs;
|
|
12541
|
+
}
|
|
12542
|
+
function createCrabScene(scene) {
|
|
12543
|
+
const shellMaterial = new MeshPhysicalMaterial({
|
|
12544
|
+
color: 16096779,
|
|
12545
|
+
roughness: 0.34,
|
|
12546
|
+
metalness: 0.04,
|
|
12547
|
+
clearcoat: 0.78,
|
|
12548
|
+
clearcoatRoughness: 0.18,
|
|
12549
|
+
emissive: new Color(0),
|
|
12550
|
+
emissiveIntensity: 0
|
|
12551
|
+
});
|
|
12552
|
+
const accentMaterial = new MeshPhysicalMaterial({
|
|
12553
|
+
color: 16486972,
|
|
12554
|
+
roughness: 0.42,
|
|
12555
|
+
metalness: 0.02,
|
|
12556
|
+
clearcoat: 0.52,
|
|
12557
|
+
clearcoatRoughness: 0.28
|
|
12558
|
+
});
|
|
12559
|
+
const legMaterial = new MeshPhysicalMaterial({
|
|
12560
|
+
color: 14251782,
|
|
12561
|
+
roughness: 0.48,
|
|
12562
|
+
clearcoat: 0.35,
|
|
12563
|
+
clearcoatRoughness: 0.35
|
|
12564
|
+
});
|
|
12565
|
+
const eyeMaterial = new MeshPhysicalMaterial({
|
|
12566
|
+
color: 16776171,
|
|
12567
|
+
roughness: 0.22,
|
|
12568
|
+
clearcoat: 0.8,
|
|
12569
|
+
clearcoatRoughness: 0.12
|
|
12570
|
+
});
|
|
12571
|
+
const root = new Group();
|
|
12572
|
+
root.rotation.x = -0.12;
|
|
12573
|
+
root.rotation.y = -0.08;
|
|
12574
|
+
root.scale.setScalar(1.08);
|
|
12575
|
+
scene.add(root);
|
|
12576
|
+
const shadow = new Mesh(
|
|
12577
|
+
new CircleGeometry(1.65, 40),
|
|
12578
|
+
new MeshBasicMaterial({ color: 1120295, transparent: true, opacity: 0.16, depthWrite: false })
|
|
12579
|
+
);
|
|
12580
|
+
shadow.position.set(0, -1.33, -0.65);
|
|
12581
|
+
shadow.scale.set(1.4, 0.34, 1);
|
|
12582
|
+
root.add(shadow);
|
|
12583
|
+
const body = new Mesh(new SphereGeometry(1, 36, 24), shellMaterial);
|
|
12584
|
+
body.position.set(0, 0.02, 0);
|
|
12585
|
+
body.scale.set(1.58, 0.92, 0.68);
|
|
12586
|
+
root.add(body);
|
|
12587
|
+
const lowerShell = new Mesh(new SphereGeometry(0.92, 28, 20), accentMaterial);
|
|
12588
|
+
lowerShell.position.set(0, -0.48, 0.22);
|
|
12589
|
+
lowerShell.scale.set(1.28, 0.42, 0.56);
|
|
12590
|
+
root.add(lowerShell);
|
|
12591
|
+
const dominantClaw = addClaw(root, -1, shellMaterial, accentMaterial);
|
|
12592
|
+
addClaw(root, 1, shellMaterial, accentMaterial);
|
|
12593
|
+
const eyes = addEyes(root, shellMaterial, eyeMaterial);
|
|
12594
|
+
const legs = addLegs(root, legMaterial);
|
|
12595
|
+
scene.add(new AmbientLight(16775149, 1.65));
|
|
12596
|
+
scene.add(new HemisphereLight(16775149, 1515571, 2.15));
|
|
12597
|
+
const keyLight = new DirectionalLight(16777215, 3.6);
|
|
12598
|
+
keyLight.position.set(3.5, 4.5, 6);
|
|
12599
|
+
scene.add(keyLight);
|
|
12600
|
+
const fillLight = new DirectionalLight(16498468, 1.35);
|
|
12601
|
+
fillLight.position.set(-4, 1.5, 4);
|
|
12602
|
+
scene.add(fillLight);
|
|
12603
|
+
const rimLight = new PointLight(16096779, 1.8, 12, 2);
|
|
12604
|
+
rimLight.position.set(0, 1.8, 4.5);
|
|
12605
|
+
scene.add(rimLight);
|
|
12606
|
+
return { bodyMaterial: shellMaterial, dominantClaw, eyes, legs, rimLight, root };
|
|
12607
|
+
}
|
|
12608
|
+
function moodColor(mood) {
|
|
12609
|
+
switch (mood) {
|
|
12610
|
+
case "active":
|
|
12611
|
+
return 2282478;
|
|
12612
|
+
case "attention":
|
|
12613
|
+
return 15680580;
|
|
12614
|
+
case "idle":
|
|
12615
|
+
return 16096779;
|
|
12616
|
+
case "sleeping":
|
|
12617
|
+
return 9741240;
|
|
12618
|
+
}
|
|
12619
|
+
}
|
|
12620
|
+
function isDisposableMesh(object2) {
|
|
12621
|
+
return object2 instanceof Mesh;
|
|
12622
|
+
}
|
|
12623
|
+
function disposeScene(scene) {
|
|
12624
|
+
const materials = /* @__PURE__ */ new Set();
|
|
12625
|
+
scene.traverse((object2) => {
|
|
12626
|
+
if (!isDisposableMesh(object2)) return;
|
|
12627
|
+
object2.geometry.dispose();
|
|
12628
|
+
const material = object2.material;
|
|
12629
|
+
if (Array.isArray(material)) {
|
|
12630
|
+
for (const item of material) materials.add(item);
|
|
12631
|
+
return;
|
|
12632
|
+
}
|
|
12633
|
+
materials.add(material);
|
|
12634
|
+
});
|
|
12635
|
+
for (const material of materials) material.dispose();
|
|
12636
|
+
}
|
|
12637
|
+
function InspectorPetCrab3D({
|
|
12638
|
+
mood,
|
|
12639
|
+
moving,
|
|
12640
|
+
reduceMotion
|
|
12641
|
+
}) {
|
|
12642
|
+
const canvasRef = reactExports.useRef(null);
|
|
12643
|
+
const moodRef = reactExports.useRef(mood);
|
|
12644
|
+
const movingRef = reactExports.useRef(moving);
|
|
12645
|
+
const reduceMotionRef = reactExports.useRef(reduceMotion);
|
|
12646
|
+
const [renderFailed, setRenderFailed] = reactExports.useState(false);
|
|
12647
|
+
reactExports.useEffect(() => {
|
|
12648
|
+
moodRef.current = mood;
|
|
12649
|
+
}, [mood]);
|
|
12650
|
+
reactExports.useEffect(() => {
|
|
12651
|
+
movingRef.current = moving;
|
|
12652
|
+
}, [moving]);
|
|
12653
|
+
reactExports.useEffect(() => {
|
|
12654
|
+
reduceMotionRef.current = reduceMotion;
|
|
12655
|
+
}, [reduceMotion]);
|
|
12656
|
+
reactExports.useEffect(() => {
|
|
12657
|
+
const canvas = canvasRef.current;
|
|
12658
|
+
if (canvas === null) return void 0;
|
|
12659
|
+
let renderer;
|
|
12660
|
+
try {
|
|
12661
|
+
renderer = new WebGLRenderer({
|
|
12662
|
+
alpha: true,
|
|
12663
|
+
antialias: true,
|
|
12664
|
+
canvas,
|
|
12665
|
+
powerPreference: "low-power",
|
|
12666
|
+
preserveDrawingBuffer: true
|
|
12667
|
+
});
|
|
12668
|
+
} catch {
|
|
12669
|
+
setRenderFailed(true);
|
|
12670
|
+
return void 0;
|
|
12671
|
+
}
|
|
12672
|
+
renderer.setClearColor(0, 0);
|
|
12673
|
+
renderer.outputColorSpace = SRGBColorSpace;
|
|
12674
|
+
renderer.toneMapping = ACESFilmicToneMapping;
|
|
12675
|
+
renderer.toneMappingExposure = 1.18;
|
|
12676
|
+
const scene = new Scene();
|
|
12677
|
+
const camera = new OrthographicCamera(-3.65, 3.65, 3.65, -3.65, 0.1, 20);
|
|
12678
|
+
camera.position.set(0, 0.05, 8);
|
|
12679
|
+
camera.lookAt(0, 0, 0);
|
|
12680
|
+
const parts = createCrabScene(scene);
|
|
12681
|
+
const startedAt = window.performance.now();
|
|
12682
|
+
let lastFrameAt = 0;
|
|
12683
|
+
const renderFrame = (timestamp) => {
|
|
12684
|
+
if (timestamp - lastFrameAt < 32) return;
|
|
12685
|
+
lastFrameAt = timestamp;
|
|
12686
|
+
const elapsed = (timestamp - startedAt) / 1e3;
|
|
12687
|
+
const motionAllowed = !reduceMotionRef.current;
|
|
12688
|
+
const gait = movingRef.current ? 1 : moodRef.current === "active" ? 0.42 : 0;
|
|
12689
|
+
const idleMotion = motionAllowed ? Math.sin(elapsed * 0.85) : 0;
|
|
12690
|
+
const stepMotion = motionAllowed ? Math.sin(elapsed * 3.4) : 0;
|
|
12691
|
+
const clawGrip = motionAllowed ? dominantClawGrip(elapsed) : 0;
|
|
12692
|
+
parts.root.position.y = idleMotion * 0.035 + Math.abs(stepMotion) * 0.055 * gait;
|
|
12693
|
+
parts.root.rotation.y = -0.08 + idleMotion * 0.055;
|
|
12694
|
+
parts.root.rotation.z = stepMotion * 0.028 * gait;
|
|
12695
|
+
parts.dominantClaw.group.rotation.z = -clawGrip * 0.035;
|
|
12696
|
+
for (const pincer of parts.dominantClaw.pincers) {
|
|
12697
|
+
pincer.mesh.position.y = pincer.restY - pincer.direction * parts.dominantClaw.scale * 0.16 * clawGrip;
|
|
12698
|
+
pincer.mesh.rotation.z = pincer.restRotationZ * (1 - clawGrip * 0.72);
|
|
12699
|
+
}
|
|
12700
|
+
for (const leg of parts.legs) {
|
|
12701
|
+
leg.group.rotation.z = motionAllowed && gait > 0 ? Math.sin(elapsed * 4.25 + leg.phase) * 0.2 * leg.side * gait : 0;
|
|
12702
|
+
leg.group.rotation.x = motionAllowed && gait > 0 ? stepMotion * 0.045 * gait : 0;
|
|
12703
|
+
}
|
|
12704
|
+
for (const eye of parts.eyes) {
|
|
12705
|
+
const gaze = motionAllowed ? Math.sin(elapsed * 0.72 + eye.phase) : 0;
|
|
12706
|
+
const lift = motionAllowed ? Math.cos(elapsed * 0.9 + eye.phase) : 0;
|
|
12707
|
+
eye.group.rotation.z = gaze * 0.075;
|
|
12708
|
+
eye.group.rotation.x = lift * 0.025;
|
|
12709
|
+
eye.pupil.position.set(
|
|
12710
|
+
eye.pupilRest.x + gaze * 0.055,
|
|
12711
|
+
eye.pupilRest.y + lift * 0.025,
|
|
12712
|
+
eye.pupilRest.z
|
|
12713
|
+
);
|
|
12714
|
+
}
|
|
12715
|
+
const color = moodColor(moodRef.current);
|
|
12716
|
+
parts.rimLight.color.setHex(color);
|
|
12717
|
+
parts.rimLight.intensity = moodRef.current === "attention" ? 3.2 : 1.8;
|
|
12718
|
+
parts.bodyMaterial.emissive.setHex(color);
|
|
12719
|
+
parts.bodyMaterial.emissiveIntensity = moodRef.current === "active" ? 0.12 : 0.035;
|
|
12720
|
+
renderer.render(scene, camera);
|
|
12721
|
+
};
|
|
12722
|
+
const resize = () => {
|
|
12723
|
+
const width = Math.max(canvas.clientWidth, 1);
|
|
12724
|
+
const height = Math.max(canvas.clientHeight, 1);
|
|
12725
|
+
renderer.setDrawingBufferSize(width, height, Math.min(window.devicePixelRatio, 2));
|
|
12726
|
+
renderer.render(scene, camera);
|
|
12727
|
+
};
|
|
12728
|
+
const resizeObserver = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(resize);
|
|
12729
|
+
if (resizeObserver === null) {
|
|
12730
|
+
window.addEventListener("resize", resize);
|
|
12731
|
+
} else {
|
|
12732
|
+
resizeObserver.observe(canvas);
|
|
12733
|
+
}
|
|
12734
|
+
resize();
|
|
12735
|
+
const handleContextLost = (event) => {
|
|
12736
|
+
event.preventDefault();
|
|
12737
|
+
setRenderFailed(true);
|
|
12738
|
+
};
|
|
12739
|
+
canvas.addEventListener("webglcontextlost", handleContextLost);
|
|
12740
|
+
renderer.setAnimationLoop(renderFrame);
|
|
12741
|
+
return () => {
|
|
12742
|
+
renderer.setAnimationLoop(null);
|
|
12743
|
+
if (resizeObserver === null) {
|
|
12744
|
+
window.removeEventListener("resize", resize);
|
|
12745
|
+
} else {
|
|
12746
|
+
resizeObserver.disconnect();
|
|
12747
|
+
}
|
|
12748
|
+
canvas.removeEventListener("webglcontextlost", handleContextLost);
|
|
12749
|
+
disposeScene(scene);
|
|
12750
|
+
renderer.dispose();
|
|
12751
|
+
};
|
|
12752
|
+
}, []);
|
|
12753
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "relative flex size-24 items-center justify-center", "aria-hidden": "true", children: [
|
|
12754
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
12755
|
+
"canvas",
|
|
12756
|
+
{
|
|
12757
|
+
ref: canvasRef,
|
|
12758
|
+
className: renderFailed ? "hidden" : "block size-full bg-transparent",
|
|
12759
|
+
"data-inspector-pet-canvas": "true"
|
|
12760
|
+
}
|
|
12761
|
+
),
|
|
12762
|
+
renderFailed && /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-22 text-amber-500 drop-shadow-[0_0_18px_rgba(245,158,11,0.28)]" })
|
|
12763
|
+
] });
|
|
12764
|
+
}
|
|
12336
12765
|
const INSPECTOR_PET_ROAMING_STORAGE_KEY = "agent-inspector.pet.roaming";
|
|
12337
|
-
const
|
|
12338
|
-
const
|
|
12766
|
+
const INSPECTOR_PET_MIN_PAUSE_MS = 11e3;
|
|
12767
|
+
const INSPECTOR_PET_PAUSE_WINDOW_MS = 9e3;
|
|
12768
|
+
const INSPECTOR_PET_SCUTTLE_MS = 7200;
|
|
12339
12769
|
const INSPECTOR_PET_PANEL_DISMISS_MS = 6e3;
|
|
12340
12770
|
const INSPECTOR_PET_REACTION_MS = 760;
|
|
12341
12771
|
const INSPECTOR_PET_DRAG_THRESHOLD = 4;
|
|
12772
|
+
const INSPECTOR_PET_TOP_LAYER_Z_INDEX = 2147483647;
|
|
12342
12773
|
function readViewport() {
|
|
12343
12774
|
if (typeof window === "undefined") return { width: 1280, height: 720, scale: 1 };
|
|
12344
12775
|
const rawScale = window.getComputedStyle(document.documentElement).getPropertyValue("--inspector-ui-scale");
|
|
@@ -12383,13 +12814,14 @@ function moodMotionClass(mood) {
|
|
|
12383
12814
|
}
|
|
12384
12815
|
}
|
|
12385
12816
|
function InspectorPet({ logs }) {
|
|
12817
|
+
const [portalHost, setPortalHost] = reactExports.useState(null);
|
|
12386
12818
|
const [viewport, setViewport] = reactExports.useState(readViewport);
|
|
12387
12819
|
const [position, setPosition] = reactExports.useState({
|
|
12388
12820
|
x: INSPECTOR_PET_MARGIN,
|
|
12389
12821
|
y: INSPECTOR_PET_MARGIN
|
|
12390
12822
|
});
|
|
12391
12823
|
const [clockMs, setClockMs] = reactExports.useState(() => Date.now());
|
|
12392
|
-
const [
|
|
12824
|
+
const [scuttling, setScuttling] = reactExports.useState(false);
|
|
12393
12825
|
const [panelTick, setPanelTick] = reactExports.useState(0);
|
|
12394
12826
|
const [panelVisible, setPanelVisible] = reactExports.useState(false);
|
|
12395
12827
|
const [reactionTick, setReactionTick] = reactExports.useState(0);
|
|
@@ -12404,10 +12836,11 @@ function InspectorPet({ logs }) {
|
|
|
12404
12836
|
() => inspectorPetPanelPosition(position, viewport),
|
|
12405
12837
|
[position, viewport]
|
|
12406
12838
|
);
|
|
12839
|
+
reactExports.useEffect(() => setPortalHost(document.body), []);
|
|
12407
12840
|
reactExports.useEffect(() => {
|
|
12408
12841
|
const nextViewport = readViewport();
|
|
12409
12842
|
setViewport(nextViewport);
|
|
12410
|
-
setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random()
|
|
12843
|
+
setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random()));
|
|
12411
12844
|
}, []);
|
|
12412
12845
|
reactExports.useEffect(() => {
|
|
12413
12846
|
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
@@ -12428,25 +12861,31 @@ function InspectorPet({ logs }) {
|
|
|
12428
12861
|
}, [roaming]);
|
|
12429
12862
|
reactExports.useEffect(() => {
|
|
12430
12863
|
if (!roaming || dragging || reduceMotion) return void 0;
|
|
12431
|
-
let
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12864
|
+
let pauseTimeoutId = null;
|
|
12865
|
+
let scuttleTimeoutId = null;
|
|
12866
|
+
const scheduleScuttle = () => {
|
|
12867
|
+
const delay = INSPECTOR_PET_MIN_PAUSE_MS + Math.random() * INSPECTOR_PET_PAUSE_WINDOW_MS;
|
|
12868
|
+
pauseTimeoutId = window.setTimeout(() => {
|
|
12869
|
+
setScuttling(true);
|
|
12436
12870
|
setPosition(
|
|
12437
12871
|
(current) => nextInspectorPetWalkingPosition(current, viewport, Math.random(), Math.random())
|
|
12438
12872
|
);
|
|
12439
|
-
|
|
12873
|
+
scuttleTimeoutId = window.setTimeout(() => {
|
|
12874
|
+
setScuttling(false);
|
|
12875
|
+
scheduleScuttle();
|
|
12876
|
+
}, INSPECTOR_PET_SCUTTLE_MS);
|
|
12440
12877
|
}, delay);
|
|
12441
12878
|
};
|
|
12442
|
-
|
|
12879
|
+
scheduleScuttle();
|
|
12443
12880
|
return () => {
|
|
12444
|
-
if (
|
|
12881
|
+
if (pauseTimeoutId !== null) window.clearTimeout(pauseTimeoutId);
|
|
12882
|
+
if (scuttleTimeoutId !== null) window.clearTimeout(scuttleTimeoutId);
|
|
12445
12883
|
};
|
|
12446
12884
|
}, [dragging, reduceMotion, roaming, viewport]);
|
|
12447
12885
|
reactExports.useEffect(() => {
|
|
12448
12886
|
const handleResize = () => {
|
|
12449
12887
|
const nextViewport = readViewport();
|
|
12888
|
+
setScuttling(false);
|
|
12450
12889
|
setViewport(nextViewport);
|
|
12451
12890
|
setPosition((current) => snapInspectorPetToEdge(current, nextViewport));
|
|
12452
12891
|
};
|
|
@@ -12489,6 +12928,7 @@ function InspectorPet({ logs }) {
|
|
|
12489
12928
|
moved: false
|
|
12490
12929
|
};
|
|
12491
12930
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
12931
|
+
setScuttling(false);
|
|
12492
12932
|
setPanelVisible(false);
|
|
12493
12933
|
setDragging(true);
|
|
12494
12934
|
}, []);
|
|
@@ -12533,135 +12973,136 @@ function InspectorPet({ logs }) {
|
|
|
12533
12973
|
dragOccurredRef.current = false;
|
|
12534
12974
|
setDragging(false);
|
|
12535
12975
|
}, []);
|
|
12976
|
+
const handleToggleRoaming = reactExports.useCallback(() => {
|
|
12977
|
+
setScuttling(false);
|
|
12978
|
+
setRoaming((current) => !current);
|
|
12979
|
+
}, []);
|
|
12536
12980
|
const petStyle = reactExports.useMemo(
|
|
12537
12981
|
() => ({
|
|
12538
|
-
transform: `translate3d(${String(position.x)}px, ${String(position.y)}px, 0)
|
|
12539
|
-
transition: dragging ? "none" :
|
|
12982
|
+
transform: `translate3d(${String(position.x)}px, ${String(position.y)}px, 0) scale(${dragging ? "1.04" : "1"})`,
|
|
12983
|
+
transition: dragging ? "none" : scuttling ? `transform ${String(INSPECTOR_PET_SCUTTLE_MS)}ms cubic-bezier(0.22, 0.72, 0.32, 1)` : "transform 900ms cubic-bezier(0.22, 0.72, 0.32, 1)",
|
|
12540
12984
|
touchAction: "none"
|
|
12541
12985
|
}),
|
|
12542
|
-
[dragging,
|
|
12986
|
+
[dragging, position.x, position.y, scuttling]
|
|
12543
12987
|
);
|
|
12544
12988
|
const panelStyle = reactExports.useMemo(
|
|
12545
12989
|
() => ({ left: panelPosition.x, top: panelPosition.y }),
|
|
12546
12990
|
[panelPosition.x, panelPosition.y]
|
|
12547
12991
|
);
|
|
12548
12992
|
const roamingLabel = roaming ? "Pause pet roaming" : "Resume pet roaming";
|
|
12549
|
-
return /* @__PURE__ */ jsxRuntimeExports.
|
|
12550
|
-
|
|
12551
|
-
|
|
12993
|
+
if (portalHost === null) return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {});
|
|
12994
|
+
return reactDomExports.createPortal(
|
|
12995
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
12996
|
+
"div",
|
|
12552
12997
|
{
|
|
12553
|
-
|
|
12554
|
-
|
|
12555
|
-
|
|
12556
|
-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
|
|
12561
|
-
|
|
12562
|
-
|
|
12563
|
-
|
|
12564
|
-
|
|
12565
|
-
|
|
12566
|
-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
|
|
12571
|
-
|
|
12572
|
-
|
|
12573
|
-
|
|
12574
|
-
|
|
12575
|
-
|
|
12576
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
12998
|
+
className: "pointer-events-none fixed inset-0 isolate",
|
|
12999
|
+
style: { zIndex: INSPECTOR_PET_TOP_LAYER_Z_INDEX },
|
|
13000
|
+
"data-inspector-pet-layer": "true",
|
|
13001
|
+
children: [
|
|
13002
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13003
|
+
"button",
|
|
13004
|
+
{
|
|
13005
|
+
type: "button",
|
|
13006
|
+
className: cn(
|
|
13007
|
+
"pointer-events-auto fixed left-0 top-0 z-10 flex size-28 items-center justify-center bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_28px_rgba(245,158,11,0.32)] focus-visible:drop-shadow-[0_0_14px_rgba(251,191,36,0.72)]",
|
|
13008
|
+
dragging ? "cursor-grabbing" : "cursor-grab"
|
|
13009
|
+
),
|
|
13010
|
+
style: petStyle,
|
|
13011
|
+
"data-pet-mood": status.mood,
|
|
13012
|
+
"data-pet-moving": scuttling ? "true" : "false",
|
|
13013
|
+
onClick: handlePet,
|
|
13014
|
+
onPointerDown: handlePointerDown,
|
|
13015
|
+
onPointerMove: handlePointerMove,
|
|
13016
|
+
onPointerUp: handlePointerUp,
|
|
13017
|
+
onPointerCancel: handlePointerCancel,
|
|
13018
|
+
"aria-label": `Inspector pet: ${status.label}`,
|
|
13019
|
+
title: status.label,
|
|
13020
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
12577
13021
|
"span",
|
|
12578
13022
|
{
|
|
12579
13023
|
className: cn(
|
|
12580
|
-
"relative flex size-
|
|
12581
|
-
|
|
13024
|
+
"relative z-10 flex size-24 items-center justify-center",
|
|
13025
|
+
reactionVisible && "inspector-pet-patted"
|
|
12582
13026
|
),
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
|
|
12591
|
-
|
|
12592
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "
|
|
12593
|
-
|
|
13027
|
+
"aria-hidden": "true",
|
|
13028
|
+
children: [
|
|
13029
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13030
|
+
"span",
|
|
13031
|
+
{
|
|
13032
|
+
className: cn(
|
|
13033
|
+
"relative flex size-24 items-center justify-center",
|
|
13034
|
+
moodMotionClass(status.mood)
|
|
13035
|
+
),
|
|
13036
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "relative flex size-24 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13037
|
+
InspectorPetCrab3D,
|
|
12594
13038
|
{
|
|
12595
|
-
|
|
12596
|
-
|
|
12597
|
-
|
|
12598
|
-
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12602
|
-
|
|
12603
|
-
|
|
12604
|
-
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
13039
|
+
mood: status.mood,
|
|
13040
|
+
moving: scuttling && !dragging,
|
|
13041
|
+
reduceMotion
|
|
13042
|
+
}
|
|
13043
|
+
) })
|
|
13044
|
+
}
|
|
13045
|
+
),
|
|
13046
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13047
|
+
"span",
|
|
13048
|
+
{
|
|
13049
|
+
className: cn(
|
|
13050
|
+
"absolute right-0 top-2 size-3 rounded-full border border-background/90",
|
|
13051
|
+
moodDotClass(status.mood),
|
|
13052
|
+
status.mood === "active" && !reduceMotion && "animate-pulse"
|
|
13053
|
+
)
|
|
13054
|
+
}
|
|
13055
|
+
),
|
|
13056
|
+
reactionVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13057
|
+
Heart,
|
|
13058
|
+
{
|
|
13059
|
+
className: "inspector-pet-heart absolute -right-2 -top-3 size-5 fill-amber-400 text-amber-400"
|
|
13060
|
+
},
|
|
13061
|
+
reactionTick
|
|
13062
|
+
)
|
|
13063
|
+
]
|
|
12617
13064
|
}
|
|
12618
|
-
),
|
|
12619
|
-
reactionVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
12620
|
-
Heart,
|
|
12621
|
-
{
|
|
12622
|
-
className: "inspector-pet-heart absolute -right-1 -top-2 size-3.5 fill-amber-400 text-amber-400"
|
|
12623
|
-
},
|
|
12624
|
-
reactionTick
|
|
12625
13065
|
)
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
13066
|
+
}
|
|
13067
|
+
),
|
|
13068
|
+
panelVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
13069
|
+
"div",
|
|
13070
|
+
{
|
|
13071
|
+
className: "pointer-events-auto fixed z-20 w-48 rounded-md border border-border/80 bg-popover/95 px-3 py-2 text-popover-foreground shadow-[0_14px_34px_rgba(0,0,0,0.28)] backdrop-blur",
|
|
13072
|
+
style: panelStyle,
|
|
13073
|
+
role: "status",
|
|
13074
|
+
"aria-live": "polite",
|
|
13075
|
+
children: [
|
|
13076
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
13077
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("size-2 shrink-0 rounded-full", moodDotClass(status.mood)) }),
|
|
13078
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: status.label }),
|
|
13079
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
13080
|
+
"button",
|
|
13081
|
+
{
|
|
13082
|
+
type: "button",
|
|
13083
|
+
className: "inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
|
|
13084
|
+
onClick: handleToggleRoaming,
|
|
13085
|
+
"aria-label": roamingLabel,
|
|
13086
|
+
title: roamingLabel,
|
|
13087
|
+
children: roaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { className: "size-3.5" })
|
|
13088
|
+
}
|
|
13089
|
+
)
|
|
13090
|
+
] }),
|
|
13091
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex items-center justify-between gap-3 font-mono text-[10px] text-muted-foreground", children: [
|
|
13092
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
13093
|
+
String(status.capturedCount),
|
|
13094
|
+
" captured"
|
|
13095
|
+
] }),
|
|
13096
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: status.pendingCount > 0 ? `${String(status.pendingCount)} active` : scuttling ? "Scuttling" : roaming ? "Resting" : "Paused" })
|
|
13097
|
+
] })
|
|
13098
|
+
]
|
|
13099
|
+
}
|
|
13100
|
+
)
|
|
12661
13101
|
]
|
|
12662
13102
|
}
|
|
12663
|
-
)
|
|
12664
|
-
|
|
13103
|
+
),
|
|
13104
|
+
portalHost
|
|
13105
|
+
);
|
|
12665
13106
|
}
|
|
12666
13107
|
const DEFAULT_QUESTION = "Analyze the current Inspector session. What looks abnormal, slow, or likely to hang?";
|
|
12667
13108
|
function providerSupportsPiAgent(provider) {
|