@tonyclaw/agent-inspector 3.0.26 → 3.0.28

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.
Files changed (86) hide show
  1. package/.output/cli.js +80 -11
  2. package/.output/nitro.json +1 -1
  3. package/.output/public/assets/{CompareDrawer-SZRUDQc9.js → CompareDrawer-TghZr5t1.js} +1 -1
  4. package/.output/public/assets/{ProxyViewerContainer-BJvwOrSi.js → ProxyViewerContainer-fShBAZjj.js} +201 -201
  5. package/.output/public/assets/{ReplayDialog-DgfmqiM3.js → ReplayDialog-CZMApaF4.js} +1 -1
  6. package/.output/public/assets/{RequestAnatomy-CS6c7Qfn.js → RequestAnatomy-DNWccR7O.js} +1 -1
  7. package/.output/public/assets/{ResponseView-C0Ri_5gm.js → ResponseView-d25MdFhY.js} +2 -2
  8. package/.output/public/assets/{StreamingChunkSequence-7dYcHWyZ.js → StreamingChunkSequence-DIFZEnWe.js} +1 -1
  9. package/.output/public/assets/{_sessionId-D-V84cgw.js → _sessionId-DRfvuE2k.js} +1 -1
  10. package/.output/public/assets/{index-DgFWCxOs.js → index-B3aAuqWq.js} +1 -1
  11. package/.output/public/assets/index-BsxAGqMA.css +1 -0
  12. package/.output/public/assets/{index-DrVJsks2.js → index-D4HkS7Ct.js} +1 -1
  13. package/.output/public/assets/{index-CSIL7-MK.js → index-DzARL2Fi.js} +3 -3
  14. package/.output/public/assets/{json-viewer-C9Ji3gYN.js → json-viewer-wuJSKaed.js} +1 -1
  15. package/.output/server/_libs/lucide-react.mjs +39 -39
  16. package/.output/server/{_sessionId-BQ6YQOO1.mjs → _sessionId-BJTNiP_O.mjs} +4 -2
  17. package/.output/server/_ssr/{CompareDrawer-CqNzIeMR.mjs → CompareDrawer-BMRH3N97.mjs} +5 -3
  18. package/.output/server/_ssr/{ProxyViewerContainer-CMC7Vsbw.mjs → ProxyViewerContainer-AFwcHAIR.mjs} +330 -223
  19. package/.output/server/_ssr/{ReplayDialog-D9p_LaTr.mjs → ReplayDialog-CbWvRHiN.mjs} +6 -4
  20. package/.output/server/_ssr/{RequestAnatomy-B4ge3qNZ.mjs → RequestAnatomy-C4kcPyqN.mjs} +4 -2
  21. package/.output/server/_ssr/{ResponseView-BCqMKApI.mjs → ResponseView-D8VYt-gZ.mjs} +5 -3
  22. package/.output/server/_ssr/{StreamingChunkSequence-BA4zTLE4.mjs → StreamingChunkSequence-BUUo6vU0.mjs} +4 -2
  23. package/.output/server/_ssr/{index-CzxhDjlq.mjs → index-DTwMEEXO.mjs} +4 -2
  24. package/.output/server/_ssr/index.mjs +2 -2
  25. package/.output/server/_ssr/{json-viewer-B3qV6iEJ.mjs → json-viewer-BqF1pSQD.mjs} +5 -3
  26. package/.output/server/_ssr/{router-CZe_R31M.mjs → router-C8BOns7y.mjs} +839 -368
  27. package/.output/server/{_tanstack-start-manifest_v-B50VTLrH.mjs → _tanstack-start-manifest_v-BBf2nRhb.mjs} +1 -1
  28. package/.output/server/index.mjs +70 -70
  29. package/README.md +8 -0
  30. package/package.json +4 -5
  31. package/src/cli.ts +43 -9
  32. package/src/components/ProxyViewer.tsx +57 -1
  33. package/src/components/clients/ClientLogo.tsx +43 -4
  34. package/src/components/inspector-pet/InspectorPet.tsx +0 -7
  35. package/src/components/providers/ProviderCard.tsx +3 -36
  36. package/src/components/providers/ProviderForm.tsx +13 -12
  37. package/src/components/providers/ProvidersPanel.tsx +2 -2
  38. package/src/components/providers/SettingsDialog.tsx +3 -3
  39. package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
  40. package/src/components/proxy-viewer/ConversationGroupList.tsx +233 -152
  41. package/src/components/proxy-viewer/ConversationHeader.tsx +29 -13
  42. package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -3
  43. package/src/contracts/anthropic.ts +0 -5
  44. package/src/contracts/index.ts +0 -4
  45. package/src/contracts/log.ts +0 -1
  46. package/src/contracts/openai.ts +0 -3
  47. package/src/knowledge/openclawClient.ts +22 -9
  48. package/src/knowledge/openclawGatewayClient.ts +20 -13
  49. package/src/lib/alertContract.ts +0 -3
  50. package/src/lib/ecosystemContract.ts +0 -3
  51. package/src/lib/export-logs.ts +1 -4
  52. package/src/lib/jsonFile.ts +46 -0
  53. package/src/lib/safeFetch.ts +100 -0
  54. package/src/lib/ssrfGuard.ts +133 -0
  55. package/src/lib/stopReason.ts +1 -4
  56. package/src/lib/unknown.ts +27 -0
  57. package/src/lib/useProviders.ts +1 -1
  58. package/src/mcp/mode.ts +3 -9
  59. package/src/mcp/server.ts +5 -14
  60. package/src/mcp/toolHandlers.ts +45 -54
  61. package/src/proxy/constants.ts +17 -0
  62. package/src/proxy/evidenceAnalysis.ts +116 -117
  63. package/src/proxy/formats/anthropic/handler.ts +5 -1
  64. package/src/proxy/formats/anthropic/schemas.ts +1 -7
  65. package/src/proxy/formats/index.ts +1 -1
  66. package/src/proxy/formats/openai/schemas.ts +0 -3
  67. package/src/proxy/formats/registry.ts +0 -3
  68. package/src/proxy/groupEvidenceExporter.ts +4 -8
  69. package/src/proxy/groupStore.ts +3 -10
  70. package/src/proxy/handler.ts +91 -12
  71. package/src/proxy/identityProxy.ts +74 -1
  72. package/src/proxy/logImporter.ts +82 -12
  73. package/src/proxy/logIndex.ts +1 -4
  74. package/src/proxy/providerSecretStore.ts +177 -0
  75. package/src/proxy/providers.ts +84 -11
  76. package/src/proxy/runFailures.ts +4 -10
  77. package/src/proxy/runStore.ts +4 -11
  78. package/src/proxy/schemas.ts +1 -8
  79. package/src/proxy/sqliteLogIndex.ts +0 -16
  80. package/src/proxy/store.ts +1 -5
  81. package/src/proxy/toolSchemaWarnings.ts +1 -9
  82. package/src/routes/api/logs.$id.replay.ts +17 -5
  83. package/src/routes/api/providers.$providerId.model-metadata.ts +11 -1
  84. package/src/routes/api/providers.$providerId.ts +18 -5
  85. package/src/routes/api/providers.ts +18 -7
  86. package/.output/public/assets/index-CmeF5XXS.css +0 -1
@@ -2,22 +2,16 @@ import { type JSX, useState } from "react";
2
2
  import { Button } from "../ui/button";
3
3
  import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
4
4
  import {
5
- Eye,
6
- EyeOff,
7
5
  Pencil,
8
6
  Trash2,
9
7
  RotateCw,
10
8
  CheckCircle,
11
9
  ExternalLink,
12
10
  Clock,
13
- Copy,
14
- Check,
15
11
  ChevronDown,
16
12
  ChevronRight,
17
13
  } from "lucide-react";
18
14
  import type { ProviderConfig } from "../../proxy/providers";
19
- import { copyTextToClipboard } from "../../lib/clipboard";
20
- import { maskApiKey } from "../../lib/mask";
21
15
  import {
22
16
  findProviderModelMetadata,
23
17
  providerHasContextMetadata,
@@ -174,18 +168,8 @@ export function ProviderCard({
174
168
  onRefreshMetadata,
175
169
  isRefreshingMetadata = false,
176
170
  }: ProviderCardProps): JSX.Element {
177
- const [showApiKey, setShowApiKey] = useState(false);
178
- const [copied, setCopied] = useState(false);
179
171
  const [showDetails, setShowDetails] = useState(false);
180
172
 
181
- function handleCopy() {
182
- void copyTextToClipboard(provider.apiKey).then((success) => {
183
- if (!success) return;
184
- setCopied(true);
185
- setTimeout(() => setCopied(false), 2000);
186
- });
187
- }
188
-
189
173
  const docsUrl =
190
174
  provider.apiDocsUrl ??
191
175
  Object.entries(KNOWN_PROVIDER_DOCS).find(([keyword]) =>
@@ -285,26 +269,9 @@ export function ProviderCard({
285
269
 
286
270
  {showDetails && (
287
271
  <div className="space-y-3 border-t border-border/60 pt-3">
288
- <div className="flex items-center gap-2">
289
- <code className="flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground">
290
- {showApiKey ? provider.apiKey : maskApiKey(provider.apiKey)}
291
- </code>
292
- <button
293
- type="button"
294
- onClick={() => setShowApiKey((s) => !s)}
295
- className="p-1 text-muted-foreground transition-colors hover:text-foreground"
296
- aria-label={showApiKey ? "Hide API key" : "Show API key"}
297
- >
298
- {showApiKey ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
299
- </button>
300
- <button
301
- type="button"
302
- onClick={handleCopy}
303
- className="p-1 text-muted-foreground transition-colors hover:text-foreground"
304
- aria-label="Copy API key"
305
- >
306
- {copied ? <Check className="size-4 text-cyan-300" /> : <Copy className="size-4" />}
307
- </button>
272
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
273
+ <CheckCircle className="size-3.5 text-emerald-500" />
274
+ <span>API key configured</span>
308
275
  </div>
309
276
 
310
277
  {provider.models !== undefined && provider.models.length > 0 && (
@@ -4,7 +4,6 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ".
4
4
  import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
5
5
  import type { ProviderConfig, ProviderModelMetadata } from "../../lib/providerContract";
6
6
  import { copyTextToClipboard } from "../../lib/clipboard";
7
- import { maskApiKey } from "../../lib/mask";
8
7
  import {
9
8
  PATH_V1_CHAT_COMPLETIONS,
10
9
  PATH_V1_MESSAGES,
@@ -327,7 +326,7 @@ function EndpointUrlPreview({
327
326
  export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps): JSX.Element {
328
327
  const initialCapabilities = deriveProviderCapabilities(provider);
329
328
  const [name, setName] = useState(provider?.name ?? "");
330
- const [apiKey, setApiKey] = useState(provider?.apiKey ?? "");
329
+ const [apiKey, setApiKey] = useState("");
331
330
  const [showApiKey, setShowApiKey] = useState(false);
332
331
  const [copied, setCopied] = useState(false);
333
332
  const initialModels = provider?.models;
@@ -411,7 +410,8 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
411
410
  useEffect(() => {
412
411
  if (provider) {
413
412
  setName(provider.name);
414
- setApiKey(provider.apiKey);
413
+ setApiKey("");
414
+ setShowApiKey(false);
415
415
  const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
416
416
  setModels(nextModels);
417
417
  setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
@@ -482,7 +482,7 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
482
482
  if (!name.trim()) {
483
483
  newErrors.name = "Name is required";
484
484
  }
485
- if (!apiKey.trim()) {
485
+ if (provider === undefined && !apiKey.trim()) {
486
486
  newErrors.apiKey = "API key is required";
487
487
  }
488
488
  if (models.length === 0 || models.every((m) => !m.trim())) {
@@ -950,19 +950,15 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
950
950
 
951
951
  <div className="space-y-2">
952
952
  <label htmlFor="provider-apikey" className="text-sm font-medium">
953
- API Key <span className="text-destructive">*</span>
953
+ API Key {provider === undefined && <span className="text-destructive">*</span>}
954
954
  </label>
955
955
  <div className="flex items-center gap-2">
956
956
  <input
957
957
  id="provider-apikey"
958
- type="text"
959
- value={showApiKey || apiKey.length === 0 ? apiKey : maskApiKey(apiKey)}
958
+ type={showApiKey ? "text" : "password"}
959
+ value={apiKey}
960
960
  onChange={(e) => setApiKey(e.target.value)}
961
- onFocus={() => {
962
- if (!showApiKey && apiKey.length > 0) setShowApiKey(true);
963
- }}
964
- placeholder="sk-ant-..."
965
- readOnly={!showApiKey && apiKey.length > 0}
961
+ placeholder={provider === undefined ? "sk-ant-..." : "Leave blank to keep current key"}
966
962
  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"
967
963
  />
968
964
  <button
@@ -982,6 +978,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
982
978
  {copied ? <Check className="size-4 text-cyan-300" /> : <Copy className="size-4" />}
983
979
  </button>
984
980
  </div>
981
+ {provider !== undefined && apiKey.trim() === "" && (
982
+ <p className="text-xs text-muted-foreground">
983
+ A key is configured. Enter a new value only to rotate it.
984
+ </p>
985
+ )}
985
986
  {errors.apiKey !== undefined && <p className="text-xs text-destructive">{errors.apiKey}</p>}
986
987
  </div>
987
988
 
@@ -81,7 +81,7 @@ function endpointPayloadValue(
81
81
  function createProviderPayload(data: ProviderFormData, preserveEmptyEndpoints = false) {
82
82
  return {
83
83
  name: data.name,
84
- apiKey: data.apiKey,
84
+ ...(data.apiKey.trim() === "" ? {} : { apiKey: data.apiKey }),
85
85
  models: data.models,
86
86
  anthropicBaseUrl: endpointPayloadValue(data.anthropicBaseUrl, preserveEmptyEndpoints),
87
87
  openaiBaseUrl: endpointPayloadValue(data.openaiBaseUrl, preserveEmptyEndpoints),
@@ -430,7 +430,7 @@ export function ProvidersPanel({
430
430
  refreshProviders();
431
431
  // Only run connection test when critical fields (apiKey, model, base URLs) changed
432
432
  const criticalFieldsChanged =
433
- (data.apiKey ?? "") !== (editingProvider.apiKey ?? "") ||
433
+ data.apiKey.trim() !== "" ||
434
434
  JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) ||
435
435
  (data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") ||
436
436
  (data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "") ||
@@ -471,9 +471,9 @@ function McpSettingsTab(): JSX.Element {
471
471
  value: `agent-inspector-mcp doctor --url ${endpoint}`,
472
472
  },
473
473
  {
474
- id: "readonly",
475
- label: "Readonly mode",
476
- value: "AGENT_INSPECTOR_MCP_READONLY=1 agent-inspector",
474
+ id: "writes",
475
+ label: "Enable write tools",
476
+ value: "AGENT_INSPECTOR_MCP_WRITES=1 agent-inspector",
477
477
  },
478
478
  {
479
479
  id: "capabilities",
@@ -160,7 +160,7 @@ export const ConversationGroup = memo(function ({
160
160
  }, [group.logs, setExpanded]);
161
161
 
162
162
  return (
163
- <div className="mb-2">
163
+ <div className="mb-1.5">
164
164
  <ConversationHeader
165
165
  conversationId={group.conversationId}
166
166
  startTime={startTime}