@tonyclaw/agent-inspector 3.1.32 → 4.0.2
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/backend/nitro.json +1 -1
- package/.output/cli.js +241 -120
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-oXUk3MBR.mjs → router-BBY_zWRK.mjs} +91 -3
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-BtfPSpkV.js → CompareDrawer-rtNElN-B.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D9DPPQ3n.js → InspectorPet-QaAiuF3d.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-Buu8vkCK.js → ProxyViewerContainer-6twNZHwl.js} +4 -4
- package/.output/ui/assets/{ReplayDialog-BER-QzpD.js → ReplayDialog-B7KlziO2.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-C8Zwxrr7.js → RequestAnatomy-2XVFBrW4.js} +1 -1
- package/.output/ui/assets/{ResponseView-DDpKdYk6.js → ResponseView-BULgzLVB.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-sMPGG7Sw.js → StreamingChunkSequence-DSkvkbns.js} +1 -1
- package/.output/ui/assets/{_sessionId-e4IheHnP.js → _sessionId-D2dmJi3G.js} +1 -1
- package/.output/ui/assets/{_sessionId-C5Uv72Fx.js → _sessionId-DcS0s1Mr.js} +1 -1
- package/.output/ui/assets/{index-B__qT0Yd.js → index-B0TN1KKO.js} +2 -2
- package/.output/ui/assets/{index-Be3bTCzi.js → index-Bva1zHEs.js} +1 -1
- package/.output/ui/assets/{index-C9q__p8J.js → index-BzBExOxu.js} +1 -1
- package/.output/ui/assets/{index-CtB0deSZ.js → index-k6sRQz55.js} +1 -1
- package/.output/ui/assets/{json-viewer-Db0AFp-7.js → json-viewer-CWM4tY-j.js} +1 -1
- package/.output/ui/assets/{jszip.min-DkY0Ym_q.js → jszip.min-BkWvRFlo.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/package.json +8 -13
- package/src/assets/IDE/claudecode-color.svg +0 -1
- package/src/assets/IDE/codex-color.svg +0 -1
- package/src/assets/IDE/opencode.svg +0 -1
- package/src/assets/IDE/xiaomimimo.svg +0 -1
- package/src/assets/favicon.svg +0 -21
- package/src/assets/logos/alibaba.svg +0 -1
- package/src/assets/logos/anthropic.svg +0 -1
- package/src/assets/logos/claude-code.svg +0 -4
- package/src/assets/logos/deepseek.svg +0 -1
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +0 -1
- package/src/assets/logos/opencode.svg +0 -4
- package/src/assets/logos/qwen.webp +0 -0
- package/src/assets/logos/zhipuai.svg +0 -219
- package/src/backend/router.tsx +0 -36
- package/src/backend/routes/$.ts +0 -26
- package/src/backend/routes/__root.ts +0 -4
- package/src/backend/routes/api/-instances.ts +0 -7
- package/src/backend/routes/api/alerts.summary.ts +0 -28
- package/src/backend/routes/api/alerts.ts +0 -70
- package/src/backend/routes/api/config.paths.ts +0 -14
- package/src/backend/routes/api/config.ts +0 -73
- package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.ts +0 -21
- package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
- package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
- package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
- package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
- package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
- package/src/backend/routes/api/groups.$groupId.ts +0 -93
- package/src/backend/routes/api/groups.ts +0 -79
- package/src/backend/routes/api/health.ts +0 -22
- package/src/backend/routes/api/instances.$name.connection.ts +0 -12
- package/src/backend/routes/api/instances.$name.restart.ts +0 -23
- package/src/backend/routes/api/instances.$name.start.ts +0 -18
- package/src/backend/routes/api/instances.$name.stop.ts +0 -23
- package/src/backend/routes/api/instances.$name.ts +0 -12
- package/src/backend/routes/api/instances.ts +0 -16
- package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
- package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
- package/src/backend/routes/api/knowledge.candidates.ts +0 -10
- package/src/backend/routes/api/knowledge.project-context.ts +0 -29
- package/src/backend/routes/api/knowledge.search.ts +0 -62
- package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
- package/src/backend/routes/api/logs.$id.body.ts +0 -58
- package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
- package/src/backend/routes/api/logs.$id.replay.ts +0 -297
- package/src/backend/routes/api/logs.$id.ts +0 -23
- package/src/backend/routes/api/logs.import.ts +0 -39
- package/src/backend/routes/api/logs.stream.ts +0 -174
- package/src/backend/routes/api/logs.ts +0 -214
- package/src/backend/routes/api/mcp.ts +0 -25
- package/src/backend/routes/api/models.ts +0 -10
- package/src/backend/routes/api/pi-agent.ts +0 -29
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
- package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
- package/src/backend/routes/api/providers.$providerId.ts +0 -111
- package/src/backend/routes/api/providers.export.ts +0 -44
- package/src/backend/routes/api/providers.import.ts +0 -81
- package/src/backend/routes/api/providers.scan.ts +0 -28
- package/src/backend/routes/api/providers.ts +0 -102
- package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
- package/src/backend/routes/api/runs.$runId.ts +0 -50
- package/src/backend/routes/api/runs.ts +0 -62
- package/src/backend/routes/api/runtime.ts +0 -46
- package/src/backend/routes/api/sessions.ts +0 -51
- package/src/backend/routes/api/storage.ts +0 -56
- package/src/backend/routes/livez.ts +0 -13
- package/src/backend/routes/metrics.ts +0 -14
- package/src/backend/routes/proxy/$.ts +0 -15
- package/src/backend/routes/readyz.ts +0 -18
- package/src/backend/start.ts +0 -10
- package/src/cli/alias.ts +0 -431
- package/src/cli/detect-tools.ts +0 -123
- package/src/cli/doctor.ts +0 -836
- package/src/cli/instance.ts +0 -1071
- package/src/cli/instanceApi.ts +0 -213
- package/src/cli/instanceArgs.ts +0 -301
- package/src/cli/instanceControl.ts +0 -191
- package/src/cli/instanceModel.ts +0 -276
- package/src/cli/instanceRegistry.ts +0 -731
- package/src/cli/networkHints.ts +0 -170
- package/src/cli/onboard.ts +0 -1668
- package/src/cli/onboardTarget.ts +0 -228
- package/src/cli/rustBackendPackage.ts +0 -458
- package/src/cli/startupOutput.ts +0 -16
- package/src/cli/templates/codex-skill-onboard.ts +0 -320
- package/src/cli/templates/command-onboard.ts +0 -25
- package/src/cli/templates/skill-onboard.ts +0 -625
- package/src/cli.ts +0 -1397
- package/src/components/OnboardingBanner.tsx +0 -115
- package/src/components/ProxyViewer.tsx +0 -2259
- package/src/components/ProxyViewerContainer.tsx +0 -1076
- package/src/components/alerts/AlertsDialog.tsx +0 -615
- package/src/components/clients/ClientLogo.tsx +0 -175
- package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
- package/src/components/errors/SafeErrorBoundary.tsx +0 -201
- package/src/components/groups/GroupsDialog.tsx +0 -1304
- package/src/components/inspector-pet/InspectorPet.tsx +0 -398
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
- package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
- package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
- package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
- package/src/components/providers/ImportWizardDialog.tsx +0 -281
- package/src/components/providers/ProviderCard.tsx +0 -347
- package/src/components/providers/ProviderForm.tsx +0 -1183
- package/src/components/providers/ProviderLogo.tsx +0 -122
- package/src/components/providers/ProviderTestResultsView.tsx +0 -734
- package/src/components/providers/ProvidersPanel.tsx +0 -714
- package/src/components/providers/SettingsDialog.tsx +0 -1042
- package/src/components/providers/providerFormCapabilities.ts +0 -36
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
- package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
- package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
- package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
- package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
- package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
- package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
- package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
- package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
- package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
- package/src/components/proxy-viewer/ResponseView.tsx +0 -202
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
- package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
- package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
- package/src/components/proxy-viewer/accessibility.ts +0 -8
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
- package/src/components/proxy-viewer/anatomy/types.ts +0 -41
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
- package/src/components/proxy-viewer/bodyHydration.ts +0 -123
- package/src/components/proxy-viewer/cacheTrend.ts +0 -50
- package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
- package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
- package/src/components/proxy-viewer/diff/index.ts +0 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
- package/src/components/proxy-viewer/formats/index.tsx +0 -47
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
- package/src/components/proxy-viewer/index.ts +0 -9
- package/src/components/proxy-viewer/lazy.ts +0 -41
- package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
- package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
- package/src/components/proxy-viewer/log-formats/index.ts +0 -23
- package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
- package/src/components/proxy-viewer/log-formats/types.ts +0 -40
- package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
- package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
- package/src/components/proxy-viewer/logFocus.ts +0 -54
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
- package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
- package/src/components/proxy-viewer/replayComparison.ts +0 -131
- package/src/components/proxy-viewer/requestDiff.ts +0 -277
- package/src/components/proxy-viewer/requestTools.ts +0 -220
- package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
- package/src/components/proxy-viewer/viewerState.ts +0 -176
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -47
- package/src/components/ui/collapsible.tsx +0 -21
- package/src/components/ui/confirm-dialog.tsx +0 -51
- package/src/components/ui/crab-logo.tsx +0 -45
- package/src/components/ui/crab-variants.tsx +0 -467
- package/src/components/ui/dialog.tsx +0 -130
- package/src/components/ui/icon-trigger.ts +0 -2
- package/src/components/ui/json-viewer-bulk.ts +0 -132
- package/src/components/ui/json-viewer.tsx +0 -508
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/select.tsx +0 -178
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -88
- package/src/components/ui/tooltip.tsx +0 -51
- package/src/components/ui/transient-toast.tsx +0 -105
- package/src/contracts/anthropic.ts +0 -230
- package/src/contracts/index.ts +0 -59
- package/src/contracts/json.ts +0 -37
- package/src/contracts/log.ts +0 -139
- package/src/contracts/openai.ts +0 -267
- package/src/index.css +0 -11
- package/src/knowledge/candidateStore.ts +0 -94
- package/src/knowledge/distiller.ts +0 -98
- package/src/knowledge/openclawClient.ts +0 -161
- package/src/knowledge/openclawGatewayClient.ts +0 -249
- package/src/knowledge/openclawMarkdown.ts +0 -146
- package/src/knowledge/redactor.ts +0 -80
- package/src/knowledge/types.ts +0 -83
- package/src/lib/alertContract.ts +0 -76
- package/src/lib/apiClient.ts +0 -166
- package/src/lib/apiFormat.ts +0 -29
- package/src/lib/backendImplementation.ts +0 -16
- package/src/lib/basePath.ts +0 -107
- package/src/lib/browserRuntimeContract.ts +0 -124
- package/src/lib/browserRuntimeResolver.ts +0 -333
- package/src/lib/chunkLoadRecovery.ts +0 -83
- package/src/lib/claudeCodeStrip.ts +0 -98
- package/src/lib/clipboard.ts +0 -67
- package/src/lib/codingAgentEndpoints.ts +0 -54
- package/src/lib/ecosystemContract.ts +0 -130
- package/src/lib/export-logs.ts +0 -287
- package/src/lib/groupContract.ts +0 -166
- package/src/lib/instanceContract.ts +0 -239
- package/src/lib/jsonFile.ts +0 -46
- package/src/lib/logImportContract.ts +0 -12
- package/src/lib/managedInstance.ts +0 -112
- package/src/lib/mask.ts +0 -4
- package/src/lib/objectUtils.ts +0 -22
- package/src/lib/piAgentContract.ts +0 -102
- package/src/lib/providerContract.ts +0 -125
- package/src/lib/providerImportContract.ts +0 -27
- package/src/lib/providerModelMetadata.ts +0 -458
- package/src/lib/providerTestContract.ts +0 -170
- package/src/lib/providerTestPrompt.ts +0 -100
- package/src/lib/publicBasePathContext.ts +0 -137
- package/src/lib/resourceLimits.ts +0 -345
- package/src/lib/runContract.ts +0 -164
- package/src/lib/runtimeConfig.ts +0 -43
- package/src/lib/safeDiagnostic.ts +0 -38
- package/src/lib/safeFetch.ts +0 -508
- package/src/lib/serverPort.ts +0 -47
- package/src/lib/sessionInfoContract.ts +0 -78
- package/src/lib/sessionUrl.ts +0 -46
- package/src/lib/ssrfGuard.ts +0 -133
- package/src/lib/stopReason.ts +0 -130
- package/src/lib/themeMode.ts +0 -41
- package/src/lib/timeDisplay.ts +0 -22
- package/src/lib/toolTrace.ts +0 -290
- package/src/lib/uiScale.ts +0 -117
- package/src/lib/unknown.ts +0 -27
- package/src/lib/upstreamUrl.ts +0 -150
- package/src/lib/useAlerts.ts +0 -82
- package/src/lib/useGroupEvidence.ts +0 -37
- package/src/lib/useGroups.ts +0 -32
- package/src/lib/useOnboarding.ts +0 -93
- package/src/lib/useProviders.ts +0 -30
- package/src/lib/useStripConfig.ts +0 -154
- package/src/lib/useThemeMode.ts +0 -40
- package/src/lib/useUiScale.ts +0 -52
- package/src/lib/utils.ts +0 -78
- package/src/mcp/currentContext.ts +0 -65
- package/src/mcp/instanceHandlers.ts +0 -151
- package/src/mcp/loopback.ts +0 -88
- package/src/mcp/mode.ts +0 -75
- package/src/mcp/previewExtractor.ts +0 -233
- package/src/mcp/server.ts +0 -2274
- package/src/mcp/toolHandlers.ts +0 -1533
- package/src/proxy/alerts.ts +0 -664
- package/src/proxy/chunkStorage.ts +0 -123
- package/src/proxy/config.ts +0 -218
- package/src/proxy/constants.ts +0 -66
- package/src/proxy/dataDir.ts +0 -147
- package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
- package/src/proxy/ecosystemExecutionRoute.ts +0 -116
- package/src/proxy/ecosystemRegistry.ts +0 -296
- package/src/proxy/ecosystemTasks.ts +0 -415
- package/src/proxy/evidenceAnalysis.ts +0 -526
- package/src/proxy/evidenceExporter.ts +0 -235
- package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
- package/src/proxy/formats/anthropic/handler.ts +0 -75
- package/src/proxy/formats/anthropic/index.ts +0 -13
- package/src/proxy/formats/anthropic/register.ts +0 -4
- package/src/proxy/formats/anthropic/schemas.ts +0 -6
- package/src/proxy/formats/anthropic/stream.ts +0 -205
- package/src/proxy/formats/handler.ts +0 -46
- package/src/proxy/formats/index.ts +0 -13
- package/src/proxy/formats/jsonSchema.ts +0 -2
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
- package/src/proxy/formats/openai/codexMetadata.ts +0 -61
- package/src/proxy/formats/openai/handler.ts +0 -133
- package/src/proxy/formats/openai/index.ts +0 -33
- package/src/proxy/formats/openai/provider.ts +0 -50
- package/src/proxy/formats/openai/register.ts +0 -4
- package/src/proxy/formats/openai/schemas.ts +0 -25
- package/src/proxy/formats/openai/stream.ts +0 -600
- package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
- package/src/proxy/formats/providerAdapter.ts +0 -50
- package/src/proxy/formats/providerRegistry.ts +0 -51
- package/src/proxy/formats/providers/index.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -65
- package/src/proxy/groupEvidenceExporter.ts +0 -371
- package/src/proxy/groupStore.ts +0 -336
- package/src/proxy/handler.ts +0 -729
- package/src/proxy/identityProxy.ts +0 -1842
- package/src/proxy/jsonlRecovery.ts +0 -554
- package/src/proxy/logBodyChunks.ts +0 -91
- package/src/proxy/logFinalizer.ts +0 -777
- package/src/proxy/logFinalizer.worker.ts +0 -24
- package/src/proxy/logImportUpload.ts +0 -128
- package/src/proxy/logImporter.ts +0 -813
- package/src/proxy/logIndex.ts +0 -663
- package/src/proxy/logSearch.ts +0 -122
- package/src/proxy/logger.ts +0 -537
- package/src/proxy/openaiOrphanToolStrip.ts +0 -142
- package/src/proxy/platformCommands.ts +0 -253
- package/src/proxy/privateDataPath.ts +0 -419
- package/src/proxy/providerImporters.ts +0 -578
- package/src/proxy/providerScanStore.ts +0 -87
- package/src/proxy/providerSecretStore.ts +0 -216
- package/src/proxy/providers.ts +0 -998
- package/src/proxy/rawStreamCapture.ts +0 -342
- package/src/proxy/runFailures.ts +0 -94
- package/src/proxy/runStore.ts +0 -214
- package/src/proxy/runtimeAdmission.ts +0 -55
- package/src/proxy/runtimeHealth.ts +0 -221
- package/src/proxy/runtimeMetrics.ts +0 -149
- package/src/proxy/runtimeShutdown.ts +0 -157
- package/src/proxy/schemas.ts +0 -112
- package/src/proxy/sessionArchive.ts +0 -479
- package/src/proxy/sessionInfo.ts +0 -266
- package/src/proxy/sessionProcess.ts +0 -194
- package/src/proxy/sessionRuntime.ts +0 -121
- package/src/proxy/sessionSupervisor.ts +0 -305
- package/src/proxy/sessionWorkerEntry.ts +0 -26
- package/src/proxy/shutdownCoordinator.ts +0 -90
- package/src/proxy/socketTracker.ts +0 -639
- package/src/proxy/sqliteLogIndex.ts +0 -733
- package/src/proxy/storageLifecycle.ts +0 -432
- package/src/proxy/store.ts +0 -1773
- package/src/proxy/streamTiming.ts +0 -31
- package/src/proxy/toolSchemaWarnings.ts +0 -529
- package/src/proxy/uiAssetServer.ts +0 -106
- package/src/proxy/upstream.ts +0 -127
- package/src/services/alerts.ts +0 -12
- package/src/services/groups.ts +0 -119
- package/src/services/piAgent.ts +0 -421
- package/src/services/piAgentRetrieval.ts +0 -1180
- package/src/services/result.ts +0 -17
- package/src/services/runs.ts +0 -76
- package/src/ui/bootstrap.ts +0 -167
- package/src/ui/index.html +0 -38
- package/src/ui/logsStreamClient.ts +0 -20
- package/src/ui/main.tsx +0 -13
- package/src/ui/public/runtime-config.js +0 -2
- package/src/ui/router.tsx +0 -13
- package/src/ui/routes/__root.tsx +0 -69
- package/src/ui/routes/index.tsx +0 -35
- package/src/ui/routes/session/$sessionId.tsx +0 -50
- package/src/ui/runtimeDiscovery.ts +0 -50
package/src/proxy/logImporter.ts
DELETED
|
@@ -1,813 +0,0 @@
|
|
|
1
|
-
import { basename } from "node:path";
|
|
2
|
-
import { Buffer } from "node:buffer";
|
|
3
|
-
import JSZip from "jszip";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
import {
|
|
6
|
-
CapturedLogSchema,
|
|
7
|
-
LogIdSchema,
|
|
8
|
-
StreamingChunkSchema,
|
|
9
|
-
type ApiFormat,
|
|
10
|
-
type CapturedLog,
|
|
11
|
-
type StreamingChunk,
|
|
12
|
-
} from "../contracts";
|
|
13
|
-
import packageJson from "../../package.json";
|
|
14
|
-
import { apiFormatForPath } from "./formats";
|
|
15
|
-
import { analyzeToolSchemaWarnings } from "./toolSchemaWarnings";
|
|
16
|
-
|
|
17
|
-
export type ImportedLogDraft = Omit<CapturedLog, "id">;
|
|
18
|
-
|
|
19
|
-
export type LogImportLimits = {
|
|
20
|
-
maxCompressedBytes: number;
|
|
21
|
-
maxEntries: number;
|
|
22
|
-
maxEntryUncompressedBytes: number;
|
|
23
|
-
maxTotalUncompressedBytes: number;
|
|
24
|
-
maxExpansionRatio: number;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const DEFAULT_LOG_IMPORT_LIMITS: Readonly<LogImportLimits> = {
|
|
28
|
-
maxCompressedBytes: 200 * 1024 * 1024,
|
|
29
|
-
maxEntries: 10_000,
|
|
30
|
-
maxEntryUncompressedBytes: 128 * 1024 * 1024,
|
|
31
|
-
maxTotalUncompressedBytes: 500 * 1024 * 1024,
|
|
32
|
-
maxExpansionRatio: 100,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50;
|
|
36
|
-
const ZIP_CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;
|
|
37
|
-
const ZIP_END_OF_CENTRAL_DIRECTORY_BYTES = 22;
|
|
38
|
-
const ZIP_CENTRAL_DIRECTORY_HEADER_BYTES = 46;
|
|
39
|
-
const ZIP_MAX_COMMENT_BYTES = 0xffff;
|
|
40
|
-
const ZIP_16_BIT_SENTINEL = 0xffff;
|
|
41
|
-
const ZIP_32_BIT_SENTINEL = 0xffffffff;
|
|
42
|
-
|
|
43
|
-
export type ParsedLogImport =
|
|
44
|
-
| {
|
|
45
|
-
ok: true;
|
|
46
|
-
logs: ImportedLogDraft[];
|
|
47
|
-
skipped: number;
|
|
48
|
-
warnings: string[];
|
|
49
|
-
}
|
|
50
|
-
| {
|
|
51
|
-
ok: false;
|
|
52
|
-
message: string;
|
|
53
|
-
warnings: string[];
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const ManifestSchema = z
|
|
57
|
-
.object({
|
|
58
|
-
version: z.string().optional(),
|
|
59
|
-
exportedAt: z.string().optional(),
|
|
60
|
-
mode: z.string().optional(),
|
|
61
|
-
redacted: z.boolean().optional(),
|
|
62
|
-
logIds: z.array(LogIdSchema).optional(),
|
|
63
|
-
})
|
|
64
|
-
.passthrough();
|
|
65
|
-
|
|
66
|
-
const LogsArraySchema = z.array(CapturedLogSchema);
|
|
67
|
-
const LogsEnvelopeSchema = z.object({ logs: z.array(CapturedLogSchema) }).passthrough();
|
|
68
|
-
const StreamingChunksImportSchema = z.object({
|
|
69
|
-
chunks: z.array(StreamingChunkSchema),
|
|
70
|
-
truncated: z.boolean().optional().default(false),
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
type ParsedManifest = z.infer<typeof ManifestSchema>;
|
|
74
|
-
|
|
75
|
-
type ZipBudget = {
|
|
76
|
-
remainingBytes: number;
|
|
77
|
-
decompressedBytes: number;
|
|
78
|
-
compressedBytes: number;
|
|
79
|
-
limits: Readonly<LogImportLimits>;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
type ZipTextResult = { ok: true; text: string | null } | { ok: false; message: string };
|
|
83
|
-
|
|
84
|
-
type ZipDirectoryInspection = { ok: true } | { ok: false; message: string };
|
|
85
|
-
|
|
86
|
-
const SEMVER_MAJOR_PATTERN = /^([0-9]+)\.[0-9]+\.[0-9]+(?:[-+].*)?$/;
|
|
87
|
-
|
|
88
|
-
function readRecordProperty(value: unknown, name: string): unknown {
|
|
89
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
90
|
-
const desc = Object.getOwnPropertyDescriptor(value, name);
|
|
91
|
-
return desc === undefined ? undefined : desc.value;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function parseJson(text: string | null): unknown {
|
|
95
|
-
if (text === null) return null;
|
|
96
|
-
try {
|
|
97
|
-
const parsed: unknown = JSON.parse(text);
|
|
98
|
-
return parsed;
|
|
99
|
-
} catch {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function parseJsonLogs(text: string): CapturedLog[] | null {
|
|
105
|
-
const parsed = parseJson(text);
|
|
106
|
-
const direct = LogsArraySchema.safeParse(parsed);
|
|
107
|
-
if (direct.success) return direct.data;
|
|
108
|
-
|
|
109
|
-
const envelope = LogsEnvelopeSchema.safeParse(parsed);
|
|
110
|
-
if (envelope.success) return envelope.data.logs;
|
|
111
|
-
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function parseJsonlLogs(text: string): { logs: CapturedLog[]; skipped: number } {
|
|
116
|
-
const logs: CapturedLog[] = [];
|
|
117
|
-
let skipped = 0;
|
|
118
|
-
for (const line of text.split(/\r?\n/)) {
|
|
119
|
-
if (line.trim() === "") continue;
|
|
120
|
-
const parsed = CapturedLogSchema.safeParse(parseJson(line));
|
|
121
|
-
if (parsed.success) {
|
|
122
|
-
logs.push(parsed.data);
|
|
123
|
-
} else {
|
|
124
|
-
skipped += 1;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return { logs, skipped };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function readStringProperty(value: unknown, name: string): string | null {
|
|
131
|
-
const property = readRecordProperty(value, name);
|
|
132
|
-
return typeof property === "string" ? property : null;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function hasProperty(value: unknown, name: string): boolean {
|
|
136
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
137
|
-
return Object.getOwnPropertyDescriptor(value, name) !== undefined;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function modelFromRequestText(requestText: string | null): string | null {
|
|
141
|
-
return readStringProperty(parseJson(requestText), "model");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function inferApiFormatFromBody(
|
|
145
|
-
path: string,
|
|
146
|
-
model: string | null,
|
|
147
|
-
requestText: string | null,
|
|
148
|
-
): ApiFormat {
|
|
149
|
-
const pathFormat = apiFormatForPath(path);
|
|
150
|
-
if (pathFormat !== "unknown") return pathFormat;
|
|
151
|
-
|
|
152
|
-
const normalizedModel = model?.toLowerCase() ?? "";
|
|
153
|
-
if (normalizedModel.startsWith("claude-")) return "anthropic";
|
|
154
|
-
if (
|
|
155
|
-
normalizedModel.startsWith("gpt-") ||
|
|
156
|
-
normalizedModel.startsWith("o1-") ||
|
|
157
|
-
normalizedModel.startsWith("o3-") ||
|
|
158
|
-
normalizedModel.startsWith("o4-") ||
|
|
159
|
-
normalizedModel.startsWith("deepseek-") ||
|
|
160
|
-
normalizedModel.startsWith("glm-") ||
|
|
161
|
-
normalizedModel.startsWith("qwen")
|
|
162
|
-
) {
|
|
163
|
-
return "openai";
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const parsedRequest = parseJson(requestText);
|
|
167
|
-
if (hasProperty(parsedRequest, "system") || hasProperty(parsedRequest, "max_tokens")) {
|
|
168
|
-
return "anthropic";
|
|
169
|
-
}
|
|
170
|
-
return "openai";
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function defaultPathForApiFormat(apiFormat: ApiFormat): string {
|
|
174
|
-
switch (apiFormat) {
|
|
175
|
-
case "anthropic":
|
|
176
|
-
return "/v1/messages";
|
|
177
|
-
case "openai":
|
|
178
|
-
return "/v1/chat/completions";
|
|
179
|
-
case "unknown":
|
|
180
|
-
return "/v1/chat/completions";
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function normalizeTimestamp(value: string, fallback: string): string {
|
|
185
|
-
return Number.isNaN(new Date(value).getTime()) ? fallback : value;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function responseStatusFromText(responseText: string | null): number | null {
|
|
189
|
-
if (responseText === null) return null;
|
|
190
|
-
const parsed = parseJson(responseText);
|
|
191
|
-
return hasProperty(parsed, "error") ? 500 : 200;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function mergeWarnings(
|
|
195
|
-
existing: readonly string[] | undefined,
|
|
196
|
-
generated: readonly string[],
|
|
197
|
-
): string[] | undefined {
|
|
198
|
-
const values: string[] = [];
|
|
199
|
-
for (const warning of existing ?? []) {
|
|
200
|
-
if (!values.includes(warning)) values.push(warning);
|
|
201
|
-
}
|
|
202
|
-
for (const warning of generated) {
|
|
203
|
-
if (!values.includes(warning)) values.push(warning);
|
|
204
|
-
}
|
|
205
|
-
return values.length === 0 ? undefined : values;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function withToolWarnings(log: ImportedLogDraft): ImportedLogDraft {
|
|
209
|
-
const warnings = analyzeToolSchemaWarnings({
|
|
210
|
-
rawRequestBody: log.rawRequestBody,
|
|
211
|
-
responseText: log.responseText,
|
|
212
|
-
apiFormat: log.apiFormat,
|
|
213
|
-
});
|
|
214
|
-
return {
|
|
215
|
-
...log,
|
|
216
|
-
warnings: mergeWarnings(log.warnings, warnings),
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function streamingChunksFromText(text: string | null):
|
|
221
|
-
| {
|
|
222
|
-
chunks: StreamingChunk[];
|
|
223
|
-
truncated: boolean;
|
|
224
|
-
}
|
|
225
|
-
| undefined {
|
|
226
|
-
const parsed = StreamingChunksImportSchema.safeParse(parseJson(text));
|
|
227
|
-
return parsed.success ? parsed.data : undefined;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function draftFromCapturedLog(
|
|
231
|
-
log: CapturedLog,
|
|
232
|
-
requestText: string | null,
|
|
233
|
-
responseText: string | null,
|
|
234
|
-
streamingChunks: { chunks: StreamingChunk[]; truncated: boolean } | undefined,
|
|
235
|
-
): ImportedLogDraft {
|
|
236
|
-
const rawRequestBody = requestText ?? log.rawRequestBody;
|
|
237
|
-
const response = responseText ?? log.responseText;
|
|
238
|
-
const model = log.model ?? modelFromRequestText(rawRequestBody);
|
|
239
|
-
const apiFormat =
|
|
240
|
-
log.apiFormat === "unknown"
|
|
241
|
-
? inferApiFormatFromBody(log.path, model, rawRequestBody)
|
|
242
|
-
: log.apiFormat;
|
|
243
|
-
const path = log.path.trim() === "" ? defaultPathForApiFormat(apiFormat) : log.path;
|
|
244
|
-
|
|
245
|
-
return withToolWarnings({
|
|
246
|
-
timestamp: normalizeTimestamp(log.timestamp, new Date().toISOString()),
|
|
247
|
-
method: log.method.trim() === "" ? "POST" : log.method,
|
|
248
|
-
path,
|
|
249
|
-
model,
|
|
250
|
-
sessionId: null,
|
|
251
|
-
rawRequestBody,
|
|
252
|
-
responseStatus: log.responseStatus ?? responseStatusFromText(response),
|
|
253
|
-
responseText: response,
|
|
254
|
-
inputTokens: log.inputTokens,
|
|
255
|
-
outputTokens: log.outputTokens,
|
|
256
|
-
cacheCreationInputTokens: log.cacheCreationInputTokens,
|
|
257
|
-
cacheReadInputTokens: log.cacheReadInputTokens,
|
|
258
|
-
elapsedMs: log.elapsedMs,
|
|
259
|
-
firstChunkMs: log.firstChunkMs ?? null,
|
|
260
|
-
totalStreamMs: log.totalStreamMs ?? null,
|
|
261
|
-
tokensPerSecond: log.tokensPerSecond ?? null,
|
|
262
|
-
streaming: log.streaming || streamingChunks !== undefined,
|
|
263
|
-
userAgent: log.userAgent,
|
|
264
|
-
origin: log.origin,
|
|
265
|
-
rawHeaders: log.rawHeaders,
|
|
266
|
-
headers: log.headers,
|
|
267
|
-
apiFormat,
|
|
268
|
-
isTest: false,
|
|
269
|
-
replayOfLogId: null,
|
|
270
|
-
providerName: log.providerName ?? null,
|
|
271
|
-
clientPort: log.clientPort ?? null,
|
|
272
|
-
clientPid: log.clientPid ?? null,
|
|
273
|
-
clientCwd: log.clientCwd ?? null,
|
|
274
|
-
clientProjectFolder: log.clientProjectFolder ?? null,
|
|
275
|
-
streamingChunks,
|
|
276
|
-
streamingChunksPath: null,
|
|
277
|
-
rawRequestBodyBytes: log.rawRequestBodyBytes,
|
|
278
|
-
responseTextBytes: log.responseTextBytes,
|
|
279
|
-
bodyContentMode: "full",
|
|
280
|
-
warnings: log.warnings,
|
|
281
|
-
error: log.error ?? null,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function legacyDraftFromParts(input: {
|
|
286
|
-
exportedAt: string;
|
|
287
|
-
requestText: string | null;
|
|
288
|
-
responseText: string | null;
|
|
289
|
-
streamingChunks: { chunks: StreamingChunk[]; truncated: boolean } | undefined;
|
|
290
|
-
}): ImportedLogDraft {
|
|
291
|
-
const model = modelFromRequestText(input.requestText);
|
|
292
|
-
const apiFormat = inferApiFormatFromBody("", model, input.requestText);
|
|
293
|
-
return withToolWarnings({
|
|
294
|
-
timestamp: input.exportedAt,
|
|
295
|
-
method: "POST",
|
|
296
|
-
path: defaultPathForApiFormat(apiFormat),
|
|
297
|
-
model,
|
|
298
|
-
sessionId: null,
|
|
299
|
-
rawRequestBody: input.requestText,
|
|
300
|
-
responseStatus: responseStatusFromText(input.responseText),
|
|
301
|
-
responseText: input.responseText,
|
|
302
|
-
inputTokens: null,
|
|
303
|
-
outputTokens: null,
|
|
304
|
-
cacheCreationInputTokens: null,
|
|
305
|
-
cacheReadInputTokens: null,
|
|
306
|
-
elapsedMs: null,
|
|
307
|
-
firstChunkMs: null,
|
|
308
|
-
totalStreamMs: null,
|
|
309
|
-
tokensPerSecond: null,
|
|
310
|
-
streaming: input.streamingChunks !== undefined,
|
|
311
|
-
userAgent: "agent-inspector-import",
|
|
312
|
-
origin: null,
|
|
313
|
-
apiFormat,
|
|
314
|
-
isTest: false,
|
|
315
|
-
replayOfLogId: null,
|
|
316
|
-
providerName: null,
|
|
317
|
-
clientPort: null,
|
|
318
|
-
clientPid: null,
|
|
319
|
-
clientCwd: null,
|
|
320
|
-
clientProjectFolder: null,
|
|
321
|
-
streamingChunks: input.streamingChunks,
|
|
322
|
-
streamingChunksPath: null,
|
|
323
|
-
bodyContentMode: "full",
|
|
324
|
-
error: null,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function zipEntryLimitMessage(path: string, maxBytes: number): string {
|
|
329
|
-
return `Archive entry "${path}" exceeds maximum uncompressed size of ${String(maxBytes)} bytes.`;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
function zipTotalLimitMessage(maxBytes: number): string {
|
|
333
|
-
return `Archive exceeds maximum total uncompressed size of ${String(maxBytes)} bytes.`;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
function zipExpansionLimitMessage(maxRatio: number): string {
|
|
337
|
-
return `Archive exceeds maximum expansion ratio of ${String(maxRatio)}:1.`;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function exceedsExpansionRatio(
|
|
341
|
-
uncompressedBytes: number,
|
|
342
|
-
compressedBytes: number,
|
|
343
|
-
maxRatio: number,
|
|
344
|
-
): boolean {
|
|
345
|
-
if (uncompressedBytes === 0) return false;
|
|
346
|
-
if (compressedBytes === 0) return true;
|
|
347
|
-
return uncompressedBytes > compressedBytes * maxRatio;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function findEndOfCentralDirectory(data: Uint8Array, view: DataView): number | null {
|
|
351
|
-
const firstCandidate = Math.max(
|
|
352
|
-
0,
|
|
353
|
-
data.byteLength - ZIP_END_OF_CENTRAL_DIRECTORY_BYTES - ZIP_MAX_COMMENT_BYTES,
|
|
354
|
-
);
|
|
355
|
-
for (
|
|
356
|
-
let offset = data.byteLength - ZIP_END_OF_CENTRAL_DIRECTORY_BYTES;
|
|
357
|
-
offset >= firstCandidate;
|
|
358
|
-
offset -= 1
|
|
359
|
-
) {
|
|
360
|
-
if (view.getUint32(offset, true) !== ZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE) continue;
|
|
361
|
-
const commentBytes = view.getUint16(offset + 20, true);
|
|
362
|
-
if (offset + ZIP_END_OF_CENTRAL_DIRECTORY_BYTES + commentBytes === data.byteLength) {
|
|
363
|
-
return offset;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return null;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* JSZip does not expose declared entry sizes. Inspect the central directory so
|
|
371
|
-
* hostile archives are rejected before an inflater or JSON parser runs.
|
|
372
|
-
*/
|
|
373
|
-
function inspectZipDirectory(
|
|
374
|
-
data: Uint8Array,
|
|
375
|
-
limits: Readonly<LogImportLimits>,
|
|
376
|
-
): ZipDirectoryInspection {
|
|
377
|
-
if (data.byteLength < ZIP_END_OF_CENTRAL_DIRECTORY_BYTES) {
|
|
378
|
-
return { ok: false, message: "Invalid ZIP archive: central directory was not found." };
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
382
|
-
const endOffset = findEndOfCentralDirectory(data, view);
|
|
383
|
-
if (endOffset === null) {
|
|
384
|
-
return { ok: false, message: "Invalid ZIP archive: central directory was not found." };
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const diskNumber = view.getUint16(endOffset + 4, true);
|
|
388
|
-
const centralDirectoryDisk = view.getUint16(endOffset + 6, true);
|
|
389
|
-
const entriesOnDisk = view.getUint16(endOffset + 8, true);
|
|
390
|
-
const entryCount = view.getUint16(endOffset + 10, true);
|
|
391
|
-
const directoryBytes = view.getUint32(endOffset + 12, true);
|
|
392
|
-
const directoryOffset = view.getUint32(endOffset + 16, true);
|
|
393
|
-
|
|
394
|
-
if (
|
|
395
|
-
diskNumber !== 0 ||
|
|
396
|
-
centralDirectoryDisk !== 0 ||
|
|
397
|
-
entriesOnDisk !== entryCount ||
|
|
398
|
-
entryCount === ZIP_16_BIT_SENTINEL ||
|
|
399
|
-
directoryBytes === ZIP_32_BIT_SENTINEL ||
|
|
400
|
-
directoryOffset === ZIP_32_BIT_SENTINEL
|
|
401
|
-
) {
|
|
402
|
-
return { ok: false, message: "ZIP64 and split archives are not supported for log import." };
|
|
403
|
-
}
|
|
404
|
-
if (entryCount > limits.maxEntries) {
|
|
405
|
-
return {
|
|
406
|
-
ok: false,
|
|
407
|
-
message: `Archive contains more than ${String(limits.maxEntries)} entries.`,
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const directoryEnd = directoryOffset + directoryBytes;
|
|
412
|
-
if (directoryEnd > endOffset || directoryEnd > data.byteLength) {
|
|
413
|
-
return { ok: false, message: "Invalid ZIP archive: central directory is out of bounds." };
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
let offset = directoryOffset;
|
|
417
|
-
let totalCompressedBytes = 0;
|
|
418
|
-
let totalUncompressedBytes = 0;
|
|
419
|
-
for (let index = 0; index < entryCount; index += 1) {
|
|
420
|
-
if (
|
|
421
|
-
offset + ZIP_CENTRAL_DIRECTORY_HEADER_BYTES > directoryEnd ||
|
|
422
|
-
view.getUint32(offset, true) !== ZIP_CENTRAL_DIRECTORY_SIGNATURE
|
|
423
|
-
) {
|
|
424
|
-
return { ok: false, message: "Invalid ZIP archive: malformed central directory entry." };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const flags = view.getUint16(offset + 8, true);
|
|
428
|
-
const compressedBytes = view.getUint32(offset + 20, true);
|
|
429
|
-
const uncompressedBytes = view.getUint32(offset + 24, true);
|
|
430
|
-
const fileNameBytes = view.getUint16(offset + 28, true);
|
|
431
|
-
const extraBytes = view.getUint16(offset + 30, true);
|
|
432
|
-
const commentBytes = view.getUint16(offset + 32, true);
|
|
433
|
-
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
434
|
-
const entryEnd =
|
|
435
|
-
offset + ZIP_CENTRAL_DIRECTORY_HEADER_BYTES + fileNameBytes + extraBytes + commentBytes;
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
compressedBytes === ZIP_32_BIT_SENTINEL ||
|
|
439
|
-
uncompressedBytes === ZIP_32_BIT_SENTINEL ||
|
|
440
|
-
localHeaderOffset === ZIP_32_BIT_SENTINEL
|
|
441
|
-
) {
|
|
442
|
-
return { ok: false, message: "ZIP64 archives are not supported for log import." };
|
|
443
|
-
}
|
|
444
|
-
if ((flags & 0x1) !== 0) {
|
|
445
|
-
return { ok: false, message: "Encrypted ZIP entries are not supported for log import." };
|
|
446
|
-
}
|
|
447
|
-
if (entryEnd > directoryEnd || localHeaderOffset >= directoryOffset) {
|
|
448
|
-
return { ok: false, message: "Invalid ZIP archive: entry metadata is out of bounds." };
|
|
449
|
-
}
|
|
450
|
-
if (uncompressedBytes > limits.maxEntryUncompressedBytes) {
|
|
451
|
-
return {
|
|
452
|
-
ok: false,
|
|
453
|
-
message: zipEntryLimitMessage(
|
|
454
|
-
`entry ${String(index + 1)}`,
|
|
455
|
-
limits.maxEntryUncompressedBytes,
|
|
456
|
-
),
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
if (exceedsExpansionRatio(uncompressedBytes, compressedBytes, limits.maxExpansionRatio)) {
|
|
460
|
-
return { ok: false, message: zipExpansionLimitMessage(limits.maxExpansionRatio) };
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
totalCompressedBytes += compressedBytes;
|
|
464
|
-
totalUncompressedBytes += uncompressedBytes;
|
|
465
|
-
if (totalUncompressedBytes > limits.maxTotalUncompressedBytes) {
|
|
466
|
-
return { ok: false, message: zipTotalLimitMessage(limits.maxTotalUncompressedBytes) };
|
|
467
|
-
}
|
|
468
|
-
if (
|
|
469
|
-
exceedsExpansionRatio(totalUncompressedBytes, totalCompressedBytes, limits.maxExpansionRatio)
|
|
470
|
-
) {
|
|
471
|
-
return { ok: false, message: zipExpansionLimitMessage(limits.maxExpansionRatio) };
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
offset = entryEnd;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (offset !== directoryEnd) {
|
|
478
|
-
return { ok: false, message: "Invalid ZIP archive: central directory size is inconsistent." };
|
|
479
|
-
}
|
|
480
|
-
return { ok: true };
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function bufferFromZipChunk(chunk: unknown): Buffer | null {
|
|
484
|
-
if (Buffer.isBuffer(chunk)) return chunk;
|
|
485
|
-
if (chunk instanceof Uint8Array) return Buffer.from(chunk);
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
async function readZipText(zip: JSZip, path: string, budget: ZipBudget): Promise<ZipTextResult> {
|
|
490
|
-
const file = zip.file(path);
|
|
491
|
-
if (file === null) return { ok: true, text: null };
|
|
492
|
-
|
|
493
|
-
return await new Promise((resolve) => {
|
|
494
|
-
const chunks: Buffer[] = [];
|
|
495
|
-
const stream = file.nodeStream("nodebuffer");
|
|
496
|
-
let entryBytes = 0;
|
|
497
|
-
let settled = false;
|
|
498
|
-
|
|
499
|
-
const settle = (result: ZipTextResult): void => {
|
|
500
|
-
if (settled) return;
|
|
501
|
-
settled = true;
|
|
502
|
-
resolve(result);
|
|
503
|
-
};
|
|
504
|
-
|
|
505
|
-
stream.on("data", (chunk: unknown) => {
|
|
506
|
-
if (settled) return;
|
|
507
|
-
const buffer = bufferFromZipChunk(chunk);
|
|
508
|
-
if (buffer === null) {
|
|
509
|
-
stream.pause();
|
|
510
|
-
settle({ ok: false, message: `Archive entry "${path}" produced invalid data.` });
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
entryBytes += buffer.byteLength;
|
|
515
|
-
if (entryBytes > budget.limits.maxEntryUncompressedBytes) {
|
|
516
|
-
stream.pause();
|
|
517
|
-
settle({
|
|
518
|
-
ok: false,
|
|
519
|
-
message: zipEntryLimitMessage(path, budget.limits.maxEntryUncompressedBytes),
|
|
520
|
-
});
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
if (buffer.byteLength > budget.remainingBytes) {
|
|
524
|
-
stream.pause();
|
|
525
|
-
settle({
|
|
526
|
-
ok: false,
|
|
527
|
-
message: zipTotalLimitMessage(budget.limits.maxTotalUncompressedBytes),
|
|
528
|
-
});
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
const decompressedBytes = budget.decompressedBytes + buffer.byteLength;
|
|
532
|
-
if (
|
|
533
|
-
exceedsExpansionRatio(
|
|
534
|
-
decompressedBytes,
|
|
535
|
-
budget.compressedBytes,
|
|
536
|
-
budget.limits.maxExpansionRatio,
|
|
537
|
-
)
|
|
538
|
-
) {
|
|
539
|
-
stream.pause();
|
|
540
|
-
settle({
|
|
541
|
-
ok: false,
|
|
542
|
-
message: zipExpansionLimitMessage(budget.limits.maxExpansionRatio),
|
|
543
|
-
});
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
budget.remainingBytes -= buffer.byteLength;
|
|
548
|
-
budget.decompressedBytes = decompressedBytes;
|
|
549
|
-
chunks.push(buffer);
|
|
550
|
-
});
|
|
551
|
-
stream.on("error", (error: unknown) => {
|
|
552
|
-
settle({
|
|
553
|
-
ok: false,
|
|
554
|
-
message: error instanceof Error ? error.message : `Failed to decompress "${path}".`,
|
|
555
|
-
});
|
|
556
|
-
});
|
|
557
|
-
stream.on("end", () => {
|
|
558
|
-
settle({ ok: true, text: Buffer.concat(chunks, entryBytes).toString("utf8") });
|
|
559
|
-
});
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
async function readManifest(
|
|
564
|
-
zip: JSZip,
|
|
565
|
-
budget: ZipBudget,
|
|
566
|
-
): Promise<{ ok: true; manifest: ParsedManifest | null } | { ok: false; message: string }> {
|
|
567
|
-
const result = await readZipText(zip, "manifest.json", budget);
|
|
568
|
-
if (!result.ok) return result;
|
|
569
|
-
const parsed = ManifestSchema.safeParse(parseJson(result.text));
|
|
570
|
-
return { ok: true, manifest: parsed.success ? parsed.data : null };
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function versionMajor(version: string): number | null {
|
|
574
|
-
const match = SEMVER_MAJOR_PATTERN.exec(version);
|
|
575
|
-
if (match === null) return null;
|
|
576
|
-
const major = match[1];
|
|
577
|
-
return major === undefined ? null : Number.parseInt(major, 10);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function validateManifestVersion(manifest: ParsedManifest | null):
|
|
581
|
-
| {
|
|
582
|
-
ok: true;
|
|
583
|
-
warning: string | null;
|
|
584
|
-
}
|
|
585
|
-
| {
|
|
586
|
-
ok: false;
|
|
587
|
-
message: string;
|
|
588
|
-
} {
|
|
589
|
-
const exportedVersion = manifest?.version;
|
|
590
|
-
if (exportedVersion === undefined) {
|
|
591
|
-
return {
|
|
592
|
-
ok: true,
|
|
593
|
-
warning:
|
|
594
|
-
"Imported legacy export without version metadata. Compatibility could not be verified.",
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const exportedMajor = versionMajor(exportedVersion);
|
|
599
|
-
if (exportedMajor === null) {
|
|
600
|
-
return {
|
|
601
|
-
ok: false,
|
|
602
|
-
message: `Unsupported export version "${exportedVersion}".`,
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
const currentMajor = versionMajor(packageJson.version);
|
|
607
|
-
if (currentMajor === null) {
|
|
608
|
-
return {
|
|
609
|
-
ok: true,
|
|
610
|
-
warning: `Current Agent Inspector version "${packageJson.version}" could not be verified against export version "${exportedVersion}".`,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
if (exportedMajor !== currentMajor) {
|
|
615
|
-
return {
|
|
616
|
-
ok: false,
|
|
617
|
-
message: `Export version mismatch: archive was created by Agent Inspector v${exportedVersion}, current version is v${packageJson.version}. Import with a matching major version.`,
|
|
618
|
-
};
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (exportedVersion !== packageJson.version) {
|
|
622
|
-
return {
|
|
623
|
-
ok: true,
|
|
624
|
-
warning: `Imported export from Agent Inspector v${exportedVersion}; current version is v${packageJson.version}.`,
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
return { ok: true, warning: null };
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
async function parseZipLogImport(
|
|
632
|
-
data: Uint8Array,
|
|
633
|
-
limits: Readonly<LogImportLimits>,
|
|
634
|
-
): Promise<ParsedLogImport> {
|
|
635
|
-
const warnings: string[] = [];
|
|
636
|
-
const inspection = inspectZipDirectory(data, limits);
|
|
637
|
-
if (!inspection.ok) return { ...inspection, warnings };
|
|
638
|
-
|
|
639
|
-
const zip = await JSZip.loadAsync(data);
|
|
640
|
-
const budget: ZipBudget = {
|
|
641
|
-
remainingBytes: limits.maxTotalUncompressedBytes,
|
|
642
|
-
decompressedBytes: 0,
|
|
643
|
-
compressedBytes: data.byteLength,
|
|
644
|
-
limits,
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
const manifestResult = await readManifest(zip, budget);
|
|
648
|
-
if (!manifestResult.ok) return { ...manifestResult, warnings };
|
|
649
|
-
const manifest = manifestResult.manifest;
|
|
650
|
-
const versionCheck = validateManifestVersion(manifest);
|
|
651
|
-
if (!versionCheck.ok) {
|
|
652
|
-
return {
|
|
653
|
-
ok: false,
|
|
654
|
-
message: versionCheck.message,
|
|
655
|
-
warnings,
|
|
656
|
-
};
|
|
657
|
-
}
|
|
658
|
-
if (versionCheck.warning !== null) {
|
|
659
|
-
warnings.push(versionCheck.warning);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const exportedAt = normalizeTimestamp(manifest?.exportedAt ?? "", new Date().toISOString());
|
|
663
|
-
if (manifest?.redacted === true) {
|
|
664
|
-
warnings.push("Imported redacted export. Secrets and some replay details may be unavailable.");
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
const metadataResult = await readZipText(zip, "logs.json", budget);
|
|
668
|
-
if (!metadataResult.ok) return { ...metadataResult, warnings };
|
|
669
|
-
const metadataLogs = metadataResult.text === null ? null : parseJsonLogs(metadataResult.text);
|
|
670
|
-
const logs: ImportedLogDraft[] = [];
|
|
671
|
-
let skipped = 0;
|
|
672
|
-
|
|
673
|
-
if (metadataLogs !== null) {
|
|
674
|
-
for (const log of metadataLogs) {
|
|
675
|
-
const requestResult = await readZipText(zip, `#${String(log.id)}.Request.json`, budget);
|
|
676
|
-
if (!requestResult.ok) return { ...requestResult, warnings };
|
|
677
|
-
const responseResult = await readZipText(zip, `#${String(log.id)}.Response.json`, budget);
|
|
678
|
-
if (!responseResult.ok) return { ...responseResult, warnings };
|
|
679
|
-
const streamingResult = await readZipText(
|
|
680
|
-
zip,
|
|
681
|
-
`#${String(log.id)}.SSE.Response.json`,
|
|
682
|
-
budget,
|
|
683
|
-
);
|
|
684
|
-
if (!streamingResult.ok) return { ...streamingResult, warnings };
|
|
685
|
-
logs.push(
|
|
686
|
-
draftFromCapturedLog(
|
|
687
|
-
log,
|
|
688
|
-
requestResult.text,
|
|
689
|
-
responseResult.text,
|
|
690
|
-
streamingChunksFromText(streamingResult.text),
|
|
691
|
-
),
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
return { ok: true, logs, skipped, warnings };
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
for (const originalId of manifest?.logIds ?? []) {
|
|
698
|
-
const requestResult = await readZipText(zip, `#${String(originalId)}.Request.json`, budget);
|
|
699
|
-
if (!requestResult.ok) return { ...requestResult, warnings };
|
|
700
|
-
const responseResult = await readZipText(zip, `#${String(originalId)}.Response.json`, budget);
|
|
701
|
-
if (!responseResult.ok) return { ...responseResult, warnings };
|
|
702
|
-
const streamingResult = await readZipText(
|
|
703
|
-
zip,
|
|
704
|
-
`#${String(originalId)}.SSE.Response.json`,
|
|
705
|
-
budget,
|
|
706
|
-
);
|
|
707
|
-
if (!streamingResult.ok) return { ...streamingResult, warnings };
|
|
708
|
-
if (
|
|
709
|
-
requestResult.text === null &&
|
|
710
|
-
responseResult.text === null &&
|
|
711
|
-
streamingResult.text === null
|
|
712
|
-
) {
|
|
713
|
-
skipped += 1;
|
|
714
|
-
continue;
|
|
715
|
-
}
|
|
716
|
-
logs.push(
|
|
717
|
-
legacyDraftFromParts({
|
|
718
|
-
exportedAt,
|
|
719
|
-
requestText: requestResult.text,
|
|
720
|
-
responseText: responseResult.text,
|
|
721
|
-
streamingChunks: streamingChunksFromText(streamingResult.text),
|
|
722
|
-
}),
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (logs.length === 0) {
|
|
727
|
-
return {
|
|
728
|
-
ok: false,
|
|
729
|
-
message: "No importable logs were found in the archive.",
|
|
730
|
-
warnings,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
return { ok: true, logs, skipped, warnings };
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
function looksLikeZip(data: Uint8Array, fileName: string): boolean {
|
|
738
|
-
if (fileName.toLowerCase().endsWith(".zip")) return true;
|
|
739
|
-
return data[0] === 0x50 && data[1] === 0x4b;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
function parseTextLogImport(text: string): ParsedLogImport {
|
|
743
|
-
const jsonLogs = parseJsonLogs(text);
|
|
744
|
-
if (jsonLogs !== null) {
|
|
745
|
-
return {
|
|
746
|
-
ok: true,
|
|
747
|
-
logs: jsonLogs.map((log) => draftFromCapturedLog(log, null, null, undefined)),
|
|
748
|
-
skipped: 0,
|
|
749
|
-
warnings: [],
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
const jsonl = parseJsonlLogs(text);
|
|
754
|
-
if (jsonl.logs.length > 0) {
|
|
755
|
-
return {
|
|
756
|
-
ok: true,
|
|
757
|
-
logs: jsonl.logs.map((log) => draftFromCapturedLog(log, null, null, undefined)),
|
|
758
|
-
skipped: jsonl.skipped,
|
|
759
|
-
warnings: [],
|
|
760
|
-
};
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
return {
|
|
764
|
-
ok: false,
|
|
765
|
-
message:
|
|
766
|
-
"Unsupported import file. Use an Agent Inspector export ZIP, logs.json, or JSONL file.",
|
|
767
|
-
warnings: [],
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
export async function parseImportedLogFile(input: {
|
|
772
|
-
fileName: string;
|
|
773
|
-
data: Uint8Array;
|
|
774
|
-
limits?: Readonly<LogImportLimits>;
|
|
775
|
-
}): Promise<ParsedLogImport> {
|
|
776
|
-
const limits = input.limits ?? DEFAULT_LOG_IMPORT_LIMITS;
|
|
777
|
-
try {
|
|
778
|
-
if (looksLikeZip(input.data, input.fileName)) {
|
|
779
|
-
if (input.data.byteLength > limits.maxCompressedBytes) {
|
|
780
|
-
return {
|
|
781
|
-
ok: false,
|
|
782
|
-
message: `Archive exceeds maximum compressed size of ${String(limits.maxCompressedBytes)} bytes.`,
|
|
783
|
-
warnings: [],
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
return await parseZipLogImport(input.data, limits);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (input.data.byteLength > limits.maxCompressedBytes) {
|
|
790
|
-
return {
|
|
791
|
-
ok: false,
|
|
792
|
-
message: `Import file exceeds maximum size of ${String(limits.maxCompressedBytes)} bytes.`,
|
|
793
|
-
warnings: [],
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
return parseTextLogImport(new TextDecoder("utf-8").decode(input.data));
|
|
798
|
-
} catch (err) {
|
|
799
|
-
return {
|
|
800
|
-
ok: false,
|
|
801
|
-
message: err instanceof Error ? err.message : "Failed to parse import file.",
|
|
802
|
-
warnings: [],
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
export function sessionNameFromImportFileName(fileName: string): string {
|
|
808
|
-
const normalized = fileName.replaceAll("\\", "/");
|
|
809
|
-
const base = basename(normalized)
|
|
810
|
-
.replace(/\.(zip|json|jsonl)$/i, "")
|
|
811
|
-
.trim();
|
|
812
|
-
return base === "" ? `import-${new Date().toISOString().slice(0, 10)}` : base;
|
|
813
|
-
}
|