@tonyclaw/agent-inspector 4.0.1 → 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 +192 -109
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-CTHqmroT.mjs → router-BBY_zWRK.mjs} +91 -3
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-L3n4J-Or.js → CompareDrawer-rtNElN-B.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D5o_3OZS.js → InspectorPet-QaAiuF3d.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-Ocagx75S.js → ProxyViewerContainer-6twNZHwl.js} +4 -4
- package/.output/ui/assets/{ReplayDialog-Bk7x1h-y.js → ReplayDialog-B7KlziO2.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-U4lzL7Sc.js → RequestAnatomy-2XVFBrW4.js} +1 -1
- package/.output/ui/assets/{ResponseView-LOWfvBLK.js → ResponseView-BULgzLVB.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DPfsrrXQ.js → StreamingChunkSequence-DSkvkbns.js} +1 -1
- package/.output/ui/assets/{_sessionId-OdX2Jvt1.js → _sessionId-D2dmJi3G.js} +1 -1
- package/.output/ui/assets/{_sessionId-0MlWS-CO.js → _sessionId-DcS0s1Mr.js} +1 -1
- package/.output/ui/assets/{index-Du4qNOOt.js → index-B0TN1KKO.js} +2 -2
- package/.output/ui/assets/{index-gvFj_9K6.js → index-Bva1zHEs.js} +1 -1
- package/.output/ui/assets/{index-Ea-c8hPg.js → index-BzBExOxu.js} +1 -1
- package/.output/ui/assets/{index-zVVS53Hd.js → index-k6sRQz55.js} +1 -1
- package/.output/ui/assets/{json-viewer-C1WAMzg3.js → json-viewer-CWM4tY-j.js} +1 -1
- package/.output/ui/assets/{jszip.min-B8AKu2zm.js → jszip.min-BkWvRFlo.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/package.json +5 -10
- 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 -1433
- 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 -258
- 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/cli/doctor.ts
DELETED
|
@@ -1,836 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { createConnection } from "node:net";
|
|
4
|
-
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath";
|
|
5
|
-
import { migrateLegacyDataDirs, resolveDataDir } from "../proxy/dataDir";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_PORT = 9527;
|
|
8
|
-
const PROBE_TIMEOUT_MS = 2000;
|
|
9
|
-
const EXTENSION_REQUIRED_FILES = [
|
|
10
|
-
"manifest.json",
|
|
11
|
-
"sidepanel.html",
|
|
12
|
-
"sidepanel.js",
|
|
13
|
-
"sidepanel.css",
|
|
14
|
-
"service-worker.js",
|
|
15
|
-
"icons/icon.svg",
|
|
16
|
-
"icons/icon-16.png",
|
|
17
|
-
"icons/icon-32.png",
|
|
18
|
-
"icons/icon-48.png",
|
|
19
|
-
"icons/icon-128.png",
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
export type DoctorSeverity = "pass" | "warn" | "fail";
|
|
23
|
-
|
|
24
|
-
export type DoctorCheck = {
|
|
25
|
-
name: string;
|
|
26
|
-
severity: DoctorSeverity;
|
|
27
|
-
message: string;
|
|
28
|
-
hint: string | null;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type DoctorOptions = {
|
|
32
|
-
port: number;
|
|
33
|
-
configDir: string | null;
|
|
34
|
-
providersJson: string | null;
|
|
35
|
-
chromeExtension: boolean;
|
|
36
|
-
fix: boolean;
|
|
37
|
-
json: boolean;
|
|
38
|
-
supportBundle: boolean;
|
|
39
|
-
verbose: boolean;
|
|
40
|
-
help: boolean;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type DoctorParseResult =
|
|
44
|
-
| { kind: "ok"; options: DoctorOptions }
|
|
45
|
-
| { kind: "error"; message: string };
|
|
46
|
-
|
|
47
|
-
export type HealthProbe = {
|
|
48
|
-
ok: boolean;
|
|
49
|
-
status: number | null;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type ReadinessProbe = {
|
|
53
|
-
ok: boolean;
|
|
54
|
-
status: number | null;
|
|
55
|
-
body: unknown;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type DoctorDeps = {
|
|
59
|
-
env: Readonly<Record<string, string | undefined>>;
|
|
60
|
-
platform: typeof process.platform;
|
|
61
|
-
cwd: string;
|
|
62
|
-
readText: (path: string) => string | null;
|
|
63
|
-
exists: (path: string) => boolean;
|
|
64
|
-
listDir: (path: string) => readonly string[] | null;
|
|
65
|
-
stat: (path: string) => { isDirectory: boolean; size: number } | null;
|
|
66
|
-
fetchHealth: (port: number, basePath: string) => Promise<HealthProbe>;
|
|
67
|
-
fetchReadiness: (port: number, basePath: string) => Promise<ReadinessProbe>;
|
|
68
|
-
isPortOpen: (port: number) => Promise<boolean>;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export type DoctorReport = {
|
|
72
|
-
checks: readonly DoctorCheck[];
|
|
73
|
-
readiness: ReadinessProbe | null;
|
|
74
|
-
passCount: number;
|
|
75
|
-
warnCount: number;
|
|
76
|
-
failCount: number;
|
|
77
|
-
exitCode: number;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export type DoctorSupportBundle = {
|
|
81
|
-
schema: "agent-inspector.support-bundle/v1";
|
|
82
|
-
createdAt: string;
|
|
83
|
-
report: DoctorReport;
|
|
84
|
-
environment: {
|
|
85
|
-
platform: typeof process.platform;
|
|
86
|
-
cwd: string;
|
|
87
|
-
basePath: string;
|
|
88
|
-
port: number;
|
|
89
|
-
hasControlToken: boolean;
|
|
90
|
-
hasProxyToken: boolean;
|
|
91
|
-
hasProviderJsonOverride: boolean;
|
|
92
|
-
};
|
|
93
|
-
storage: readonly DoctorCheck[];
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
function check(name: string, severity: DoctorSeverity, message: string, hint = ""): DoctorCheck {
|
|
97
|
-
return {
|
|
98
|
-
name,
|
|
99
|
-
severity,
|
|
100
|
-
message,
|
|
101
|
-
hint: hint === "" ? null : hint,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function parsePort(raw: string | undefined): number | null {
|
|
106
|
-
if (raw === undefined || raw.trim() === "") return null;
|
|
107
|
-
const port = Number(raw);
|
|
108
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65535) return null;
|
|
109
|
-
return port;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export function parseDoctorArgs(
|
|
113
|
-
argv: readonly string[],
|
|
114
|
-
env: Readonly<Record<string, string | undefined>> = process.env,
|
|
115
|
-
): DoctorParseResult {
|
|
116
|
-
const envPort = parsePort(env["PORT"]);
|
|
117
|
-
const options: DoctorOptions = {
|
|
118
|
-
port: envPort ?? DEFAULT_PORT,
|
|
119
|
-
configDir: null,
|
|
120
|
-
providersJson: null,
|
|
121
|
-
chromeExtension: false,
|
|
122
|
-
fix: false,
|
|
123
|
-
json: false,
|
|
124
|
-
supportBundle: false,
|
|
125
|
-
verbose: false,
|
|
126
|
-
help: false,
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
for (let i = 0; i < argv.length; i++) {
|
|
130
|
-
const arg = argv[i];
|
|
131
|
-
switch (arg) {
|
|
132
|
-
case undefined:
|
|
133
|
-
continue;
|
|
134
|
-
case "--help":
|
|
135
|
-
case "-h":
|
|
136
|
-
options.help = true;
|
|
137
|
-
break;
|
|
138
|
-
case "--port":
|
|
139
|
-
case "-p": {
|
|
140
|
-
const parsed = parsePort(argv[i + 1]);
|
|
141
|
-
if (parsed === null) {
|
|
142
|
-
return { kind: "error", message: "doctor: --port must be an integer from 1 to 65535" };
|
|
143
|
-
}
|
|
144
|
-
options.port = parsed;
|
|
145
|
-
i++;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case "--config-dir": {
|
|
149
|
-
const next = argv[i + 1];
|
|
150
|
-
if (next === undefined || next.trim() === "") {
|
|
151
|
-
return { kind: "error", message: "doctor: --config-dir requires a path argument" };
|
|
152
|
-
}
|
|
153
|
-
options.configDir = next;
|
|
154
|
-
i++;
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case "--providers": {
|
|
158
|
-
const next = argv[i + 1];
|
|
159
|
-
if (next === undefined || next.trim() === "") {
|
|
160
|
-
return { kind: "error", message: "doctor: --providers requires a JSON argument" };
|
|
161
|
-
}
|
|
162
|
-
options.providersJson = next;
|
|
163
|
-
i++;
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
case "--chrome-extension":
|
|
167
|
-
options.chromeExtension = true;
|
|
168
|
-
break;
|
|
169
|
-
case "--fix":
|
|
170
|
-
options.fix = true;
|
|
171
|
-
break;
|
|
172
|
-
case "--json":
|
|
173
|
-
options.json = true;
|
|
174
|
-
break;
|
|
175
|
-
case "--support-bundle":
|
|
176
|
-
options.supportBundle = true;
|
|
177
|
-
options.json = true;
|
|
178
|
-
break;
|
|
179
|
-
case "--verbose":
|
|
180
|
-
options.verbose = true;
|
|
181
|
-
break;
|
|
182
|
-
default:
|
|
183
|
-
return { kind: "error", message: `doctor: unknown option ${arg}` };
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return { kind: "ok", options };
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
191
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function providerCountFromParsed(value: unknown): number | null {
|
|
195
|
-
if (Array.isArray(value)) return value.length;
|
|
196
|
-
if (isObject(value)) {
|
|
197
|
-
const providers = value["providers"];
|
|
198
|
-
if (Array.isArray(providers)) return providers.length;
|
|
199
|
-
}
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function providerCountFromJson(raw: string): "invalid" | number | null {
|
|
204
|
-
try {
|
|
205
|
-
const parsed: unknown = JSON.parse(raw);
|
|
206
|
-
return providerCountFromParsed(parsed);
|
|
207
|
-
} catch {
|
|
208
|
-
return "invalid";
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function resolveMaybeRelative(base: string, value: string): string {
|
|
213
|
-
return isAbsolute(value) ? value : resolve(base, value);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function resolveDefaultDataDir(
|
|
217
|
-
env: Readonly<Record<string, string | undefined>>,
|
|
218
|
-
platform: typeof process.platform,
|
|
219
|
-
exists: (path: string) => boolean,
|
|
220
|
-
): string {
|
|
221
|
-
return resolveDataDir(exists, env, platform);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function providerConfigCandidates(options: DoctorOptions, deps: DoctorDeps): readonly string[] {
|
|
225
|
-
const explicitPath = deps.env["AGENT_INSPECTOR_CONFIG_PATH"];
|
|
226
|
-
if (explicitPath !== undefined && explicitPath !== "") {
|
|
227
|
-
return [resolveMaybeRelative(deps.cwd, explicitPath)];
|
|
228
|
-
}
|
|
229
|
-
if (options.configDir !== null) {
|
|
230
|
-
const dir = resolveMaybeRelative(deps.cwd, options.configDir);
|
|
231
|
-
return [join(dir, "providers.json"), join(dir, "config.json")];
|
|
232
|
-
}
|
|
233
|
-
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
234
|
-
return [join(dataDir, "providers.json"), join(dataDir, "config.json")];
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function checkProviderConfig(options: DoctorOptions, deps: DoctorDeps): DoctorCheck {
|
|
238
|
-
const providersJson = options.providersJson ?? deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] ?? null;
|
|
239
|
-
if (providersJson !== null) {
|
|
240
|
-
const count = providerCountFromJson(providersJson);
|
|
241
|
-
if (count === "invalid") {
|
|
242
|
-
return check(
|
|
243
|
-
"Provider config",
|
|
244
|
-
"fail",
|
|
245
|
-
"Provider JSON override is not valid JSON.",
|
|
246
|
-
"Fix --providers or AGENT_INSPECTOR_PROVIDERS_JSON; doctor never prints the raw value.",
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
if (count !== null) {
|
|
250
|
-
return check("Provider config", "pass", `${count} provider(s) found in JSON override.`);
|
|
251
|
-
}
|
|
252
|
-
return check(
|
|
253
|
-
"Provider config",
|
|
254
|
-
"fail",
|
|
255
|
-
"Provider JSON override does not look like a provider array or { providers } object.",
|
|
256
|
-
"Use an exported providers JSON payload or configure providers in the UI.",
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const candidates = providerConfigCandidates(options, deps);
|
|
261
|
-
for (const candidate of candidates) {
|
|
262
|
-
if (!deps.exists(candidate)) continue;
|
|
263
|
-
const text = deps.readText(candidate);
|
|
264
|
-
if (text === null) {
|
|
265
|
-
return check(
|
|
266
|
-
"Provider config",
|
|
267
|
-
"fail",
|
|
268
|
-
`Provider config exists but could not be read: ${candidate}`,
|
|
269
|
-
);
|
|
270
|
-
}
|
|
271
|
-
const count = providerCountFromJson(text);
|
|
272
|
-
if (count === "invalid") {
|
|
273
|
-
return check("Provider config", "fail", `Provider config is invalid JSON: ${candidate}`);
|
|
274
|
-
}
|
|
275
|
-
if (count !== null && count > 0) {
|
|
276
|
-
return check("Provider config", "pass", `${count} provider(s) configured.`);
|
|
277
|
-
}
|
|
278
|
-
return check(
|
|
279
|
-
"Provider config",
|
|
280
|
-
"warn",
|
|
281
|
-
`Provider config exists but no providers were found: ${candidate}`,
|
|
282
|
-
"Add a provider through the control API/MCP, or use Settings when a UI is available.",
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return check(
|
|
287
|
-
"Provider config",
|
|
288
|
-
"warn",
|
|
289
|
-
"No provider config was found.",
|
|
290
|
-
"Add a provider through the control API/MCP, use Settings when a UI is available, or pass --providers for this check.",
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function publicOriginForDoctor(port: number, env: DoctorDeps["env"]): string {
|
|
295
|
-
const configured = env["AGENT_INSPECTOR_PUBLIC_ORIGIN"];
|
|
296
|
-
if (configured !== undefined && configured.trim() === configured) {
|
|
297
|
-
try {
|
|
298
|
-
const parsed = new URL(configured);
|
|
299
|
-
if (
|
|
300
|
-
(parsed.protocol === "http:" || parsed.protocol === "https:") &&
|
|
301
|
-
parsed.username.length === 0 &&
|
|
302
|
-
parsed.password.length === 0 &&
|
|
303
|
-
parsed.pathname === "/" &&
|
|
304
|
-
parsed.search.length === 0 &&
|
|
305
|
-
parsed.hash.length === 0
|
|
306
|
-
) {
|
|
307
|
-
return parsed.origin;
|
|
308
|
-
}
|
|
309
|
-
} catch {
|
|
310
|
-
// Startup reports invalid public-origin configuration; doctor keeps a safe local hint.
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return `http://localhost:${String(port)}`;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
async function checkProxyHealth(port: number, deps: DoctorDeps): Promise<DoctorCheck> {
|
|
317
|
-
const basePath = getConfiguredBasePath(deps.env);
|
|
318
|
-
const health = await deps.fetchHealth(port, basePath);
|
|
319
|
-
const url = publicOriginForDoctor(port, deps.env);
|
|
320
|
-
const proxyUrl = appendBasePathToOrigin(url, "/proxy", basePath);
|
|
321
|
-
const healthPath = appendBasePathToOrigin("", "/api/health", basePath);
|
|
322
|
-
if (health.ok) {
|
|
323
|
-
return check(
|
|
324
|
-
"Backend health",
|
|
325
|
-
"pass",
|
|
326
|
-
`Agent Inspector backend is healthy at ${appendBasePathToOrigin(url, "/", basePath)}.`,
|
|
327
|
-
`Agent proxy URL: ${proxyUrl}. UI availability is not part of this backend check.`,
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (await deps.isPortOpen(port)) {
|
|
332
|
-
const suffix = health.status === null ? "" : ` (HTTP ${health.status})`;
|
|
333
|
-
return check(
|
|
334
|
-
"Backend health",
|
|
335
|
-
"fail",
|
|
336
|
-
`Port ${port} is accepting connections, but ${healthPath} is not healthy${suffix}.`,
|
|
337
|
-
"Stop that process, choose --port <n>, or start agent-inspector with --force-restart.",
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
return check(
|
|
342
|
-
"Backend health",
|
|
343
|
-
"warn",
|
|
344
|
-
`No process is listening on port ${port}.`,
|
|
345
|
-
"Start with `agent-inspector`, `agent-inspector --background`, or `bun run dev`.",
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
function readinessChecks(readiness: ReadinessProbe): readonly DoctorCheck[] {
|
|
350
|
-
if (!readiness.ok) {
|
|
351
|
-
const suffix = readiness.status === null ? "" : ` (HTTP ${readiness.status})`;
|
|
352
|
-
return [
|
|
353
|
-
check(
|
|
354
|
-
"Readiness",
|
|
355
|
-
"warn",
|
|
356
|
-
`Readiness probe is unavailable${suffix}.`,
|
|
357
|
-
"Use /readyz locally or run with --verbose for storage and worker hints.",
|
|
358
|
-
),
|
|
359
|
-
];
|
|
360
|
-
}
|
|
361
|
-
if (!isObject(readiness.body)) {
|
|
362
|
-
return [check("Readiness", "warn", "Readiness response was not an object.")];
|
|
363
|
-
}
|
|
364
|
-
const status = readiness.body["status"];
|
|
365
|
-
const checks = readiness.body["checks"];
|
|
366
|
-
if (typeof status !== "string" || !isObject(checks)) {
|
|
367
|
-
return [check("Readiness", "warn", "Readiness response did not include status/checks.")];
|
|
368
|
-
}
|
|
369
|
-
const degraded = Object.entries(checks)
|
|
370
|
-
.filter(([, value]) => value !== "ok")
|
|
371
|
-
.map(([name, value]) => `${name}=${String(value)}`);
|
|
372
|
-
if (degraded.length === 0) {
|
|
373
|
-
return [check("Readiness", "pass", `Runtime readiness is ${status}.`)];
|
|
374
|
-
}
|
|
375
|
-
return [
|
|
376
|
-
check(
|
|
377
|
-
"Readiness",
|
|
378
|
-
status === "unavailable" ? "fail" : "warn",
|
|
379
|
-
`Runtime readiness is ${status}: ${degraded.join(", ")}.`,
|
|
380
|
-
"Inspect /readyz and /metrics for safe aggregate runtime details.",
|
|
381
|
-
),
|
|
382
|
-
];
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
function findProjectRoot(startDir: string, deps: Pick<DoctorDeps, "exists">): string {
|
|
386
|
-
let current = resolve(startDir);
|
|
387
|
-
for (let i = 0; i < 6; i++) {
|
|
388
|
-
if (deps.exists(join(current, "package.json"))) return current;
|
|
389
|
-
const next = resolve(current, "..");
|
|
390
|
-
if (next === current) return startDir;
|
|
391
|
-
current = next;
|
|
392
|
-
}
|
|
393
|
-
return startDir;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
function checkPackage(rootDir: string, deps: DoctorDeps): DoctorCheck {
|
|
397
|
-
const packagePath = join(rootDir, "package.json");
|
|
398
|
-
const text = deps.readText(packagePath);
|
|
399
|
-
if (text === null) {
|
|
400
|
-
return check("Package metadata", "warn", "package.json was not found near the CLI bundle.");
|
|
401
|
-
}
|
|
402
|
-
try {
|
|
403
|
-
const parsed: unknown = JSON.parse(text);
|
|
404
|
-
if (!isObject(parsed)) {
|
|
405
|
-
return check("Package metadata", "warn", "package.json is not an object.");
|
|
406
|
-
}
|
|
407
|
-
const name = parsed["name"];
|
|
408
|
-
const version = parsed["version"];
|
|
409
|
-
if (typeof name === "string" && typeof version === "string") {
|
|
410
|
-
return check("Package metadata", "pass", `${name}@${version}`);
|
|
411
|
-
}
|
|
412
|
-
return check("Package metadata", "warn", "package.json is missing name or version.");
|
|
413
|
-
} catch {
|
|
414
|
-
return check("Package metadata", "warn", "package.json is not valid JSON.");
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
function countFilesInDir(dir: string, deps: Pick<DoctorDeps, "listDir" | "stat">): number | null {
|
|
419
|
-
const files = deps.listDir(dir);
|
|
420
|
-
if (files === null) return null;
|
|
421
|
-
let count = 0;
|
|
422
|
-
for (const file of files) {
|
|
423
|
-
const fileStat = deps.stat(join(dir, file));
|
|
424
|
-
if (fileStat !== null && !fileStat.isDirectory) count += 1;
|
|
425
|
-
}
|
|
426
|
-
return count;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
function formatBytes(bytes: number): string {
|
|
430
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
431
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
432
|
-
if (bytes < 1024 * 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MiB`;
|
|
433
|
-
return `${(bytes / 1024 / 1024 / 1024).toFixed(1)} GiB`;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
function dirSize(
|
|
437
|
-
dir: string,
|
|
438
|
-
deps: Pick<DoctorDeps, "listDir" | "stat">,
|
|
439
|
-
depth = 0,
|
|
440
|
-
): number | null {
|
|
441
|
-
const dirStat = deps.stat(dir);
|
|
442
|
-
if (dirStat === null) return null;
|
|
443
|
-
if (!dirStat.isDirectory) return dirStat.size;
|
|
444
|
-
if (depth > 4) return 0;
|
|
445
|
-
|
|
446
|
-
const files = deps.listDir(dir);
|
|
447
|
-
if (files === null) return null;
|
|
448
|
-
let total = 0;
|
|
449
|
-
for (const file of files) {
|
|
450
|
-
const childSize = dirSize(join(dir, file), deps, depth + 1);
|
|
451
|
-
if (childSize !== null) total += childSize;
|
|
452
|
-
}
|
|
453
|
-
return total;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function verboseChecks(options: DoctorOptions, deps: DoctorDeps): readonly DoctorCheck[] {
|
|
457
|
-
const dataDir = resolveDefaultDataDir(deps.env, deps.platform, deps.exists);
|
|
458
|
-
const logsDir = join(dataDir, "logs");
|
|
459
|
-
const chunksDir = join(dataDir, "chunks");
|
|
460
|
-
const providerCandidates = providerConfigCandidates(options, deps);
|
|
461
|
-
const captureMode =
|
|
462
|
-
deps.env["AGENT_INSPECTOR_MODE"] ?? deps.env["AGENT_INSPECTOR_CAPTURE_MODE"] ?? "simple";
|
|
463
|
-
const logCount = countFilesInDir(logsDir, deps);
|
|
464
|
-
const logSize = dirSize(logsDir, deps);
|
|
465
|
-
const chunkSize = dirSize(chunksDir, deps);
|
|
466
|
-
const homeDir =
|
|
467
|
-
deps.platform === "win32"
|
|
468
|
-
? (deps.env["USERPROFILE"] ?? deps.env["APPDATA"] ?? null)
|
|
469
|
-
: (deps.env["HOME"] ?? null);
|
|
470
|
-
const claudeSkill =
|
|
471
|
-
homeDir === null
|
|
472
|
-
? null
|
|
473
|
-
: join(homeDir, ".claude", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
474
|
-
const claudeCommand =
|
|
475
|
-
homeDir === null ? null : join(homeDir, ".claude", "commands", "agent-inspector-onboard.md");
|
|
476
|
-
const codexSkill =
|
|
477
|
-
homeDir === null
|
|
478
|
-
? null
|
|
479
|
-
: join(homeDir, ".codex", "skills", "agent-inspector-onboard", "SKILL.md");
|
|
480
|
-
const hasClaudeOnboarding =
|
|
481
|
-
claudeSkill !== null &&
|
|
482
|
-
claudeCommand !== null &&
|
|
483
|
-
deps.exists(claudeSkill) &&
|
|
484
|
-
deps.exists(claudeCommand);
|
|
485
|
-
const hasCodexOnboarding = codexSkill !== null && deps.exists(codexSkill);
|
|
486
|
-
|
|
487
|
-
return [
|
|
488
|
-
check("Data directory", "pass", dataDir),
|
|
489
|
-
check("Provider config search", "pass", providerCandidates.join(", ")),
|
|
490
|
-
check("Capture mode env", "pass", captureMode),
|
|
491
|
-
check(
|
|
492
|
-
"Log storage",
|
|
493
|
-
logCount === null ? "warn" : "pass",
|
|
494
|
-
logCount === null
|
|
495
|
-
? `Log directory was not found or could not be read: ${logsDir}`
|
|
496
|
-
: `${logCount} file(s), ${formatBytes(logSize ?? 0)} under ${logsDir}`,
|
|
497
|
-
),
|
|
498
|
-
check(
|
|
499
|
-
"Chunk storage",
|
|
500
|
-
chunkSize === null ? "warn" : "pass",
|
|
501
|
-
chunkSize === null
|
|
502
|
-
? `Chunk directory was not found or could not be read: ${chunksDir}`
|
|
503
|
-
: `${formatBytes(chunkSize)} under ${chunksDir}`,
|
|
504
|
-
),
|
|
505
|
-
check(
|
|
506
|
-
"Claude onboarding",
|
|
507
|
-
hasClaudeOnboarding ? "pass" : "warn",
|
|
508
|
-
hasClaudeOnboarding
|
|
509
|
-
? "Claude Code skill and slash command are installed."
|
|
510
|
-
: "Claude Code onboarding files were not found.",
|
|
511
|
-
"Run `agent-inspector onboard --force` after installing or upgrading.",
|
|
512
|
-
),
|
|
513
|
-
check(
|
|
514
|
-
"Codex onboarding",
|
|
515
|
-
hasCodexOnboarding ? "pass" : "warn",
|
|
516
|
-
hasCodexOnboarding
|
|
517
|
-
? "Codex onboarding skill is installed."
|
|
518
|
-
: "Codex onboarding skill was not found.",
|
|
519
|
-
"Run `agent-inspector onboard --force` after installing or upgrading.",
|
|
520
|
-
),
|
|
521
|
-
];
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function checkExtensionSource(rootDir: string, deps: DoctorDeps): DoctorCheck {
|
|
525
|
-
const extensionDir = join(rootDir, "extensions", "chrome");
|
|
526
|
-
const missing = EXTENSION_REQUIRED_FILES.filter((file) => !deps.exists(join(extensionDir, file)));
|
|
527
|
-
if (missing.length === 0) {
|
|
528
|
-
return check(
|
|
529
|
-
"Chrome extension source",
|
|
530
|
-
"pass",
|
|
531
|
-
"Manifest, side panel, service worker, and icons are present.",
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
return check(
|
|
535
|
-
"Chrome extension source",
|
|
536
|
-
"fail",
|
|
537
|
-
`Missing ${missing.length} Chrome extension file(s): ${missing.join(", ")}`,
|
|
538
|
-
"Restore the extension files before packaging or loading unpacked.",
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
function extensionManifestVersion(rootDir: string, deps: DoctorDeps): string | null {
|
|
543
|
-
const manifestText = deps.readText(join(rootDir, "extensions", "chrome", "manifest.json"));
|
|
544
|
-
if (manifestText === null) return null;
|
|
545
|
-
try {
|
|
546
|
-
const manifest: unknown = JSON.parse(manifestText);
|
|
547
|
-
if (!isObject(manifest)) return null;
|
|
548
|
-
const version = manifest["version"];
|
|
549
|
-
return typeof version === "string" && version !== "" ? version : null;
|
|
550
|
-
} catch {
|
|
551
|
-
return null;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function checkExtensionPackage(rootDir: string, deps: DoctorDeps): DoctorCheck {
|
|
556
|
-
const distDir = join(rootDir, "dist", "chrome-extension");
|
|
557
|
-
const files = deps.listDir(distDir);
|
|
558
|
-
if (files === null) {
|
|
559
|
-
return check(
|
|
560
|
-
"Chrome extension package",
|
|
561
|
-
"warn",
|
|
562
|
-
"No packaged Chrome extension artifact was found.",
|
|
563
|
-
"Run `npm run extension:zip` before Chrome Web Store upload.",
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
const manifestVersion = extensionManifestVersion(rootDir, deps);
|
|
568
|
-
if (manifestVersion !== null) {
|
|
569
|
-
const zipName = `agent-inspector-companion-v${manifestVersion}.zip`;
|
|
570
|
-
const checksumName = `${zipName}.sha256`;
|
|
571
|
-
const hasZip = files.includes(zipName);
|
|
572
|
-
const hasChecksum = files.includes(checksumName);
|
|
573
|
-
if (hasZip && hasChecksum) {
|
|
574
|
-
return check("Chrome extension package", "pass", `${zipName} with checksum is present.`);
|
|
575
|
-
}
|
|
576
|
-
if (hasZip) {
|
|
577
|
-
return check(
|
|
578
|
-
"Chrome extension package",
|
|
579
|
-
"warn",
|
|
580
|
-
`${zipName} exists but no .sha256 checksum was found.`,
|
|
581
|
-
"Run `npm run extension:zip` to regenerate the zip and checksum.",
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
return check(
|
|
585
|
-
"Chrome extension package",
|
|
586
|
-
"warn",
|
|
587
|
-
`Current Chrome extension package was not found: ${zipName}`,
|
|
588
|
-
"Run `npm run extension:zip` before Chrome Web Store upload.",
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
const zip = files.find((file) => file.endsWith(".zip"));
|
|
593
|
-
const checksum = files.find((file) => file.endsWith(".zip.sha256"));
|
|
594
|
-
if (zip !== undefined && checksum !== undefined) {
|
|
595
|
-
return check("Chrome extension package", "pass", `${zip} with checksum is present.`);
|
|
596
|
-
}
|
|
597
|
-
if (zip !== undefined) {
|
|
598
|
-
return check(
|
|
599
|
-
"Chrome extension package",
|
|
600
|
-
"warn",
|
|
601
|
-
`${zip} exists but no .sha256 checksum was found.`,
|
|
602
|
-
"Run `npm run extension:zip` to regenerate the zip and checksum.",
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
return check(
|
|
606
|
-
"Chrome extension package",
|
|
607
|
-
"warn",
|
|
608
|
-
"No Chrome extension zip artifact was found.",
|
|
609
|
-
"Run `npm run extension:zip` before Chrome Web Store upload.",
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
export async function buildDoctorReport(
|
|
614
|
-
options: DoctorOptions,
|
|
615
|
-
deps: DoctorDeps = createDoctorDeps(),
|
|
616
|
-
): Promise<DoctorReport> {
|
|
617
|
-
const rootDir = findProjectRoot(deps.cwd, deps);
|
|
618
|
-
const basePath = getConfiguredBasePath(deps.env);
|
|
619
|
-
const readiness = await deps.fetchReadiness(options.port, basePath);
|
|
620
|
-
const coreChecks = [
|
|
621
|
-
await checkProxyHealth(options.port, deps),
|
|
622
|
-
...readinessChecks(readiness),
|
|
623
|
-
checkProviderConfig(options, deps),
|
|
624
|
-
checkPackage(rootDir, deps),
|
|
625
|
-
];
|
|
626
|
-
const checksWithExtension = options.chromeExtension
|
|
627
|
-
? [...coreChecks, checkExtensionSource(rootDir, deps), checkExtensionPackage(rootDir, deps)]
|
|
628
|
-
: coreChecks;
|
|
629
|
-
const checks = options.verbose
|
|
630
|
-
? [...checksWithExtension, ...verboseChecks(options, deps)]
|
|
631
|
-
: checksWithExtension;
|
|
632
|
-
|
|
633
|
-
const passCount = checks.filter((item) => item.severity === "pass").length;
|
|
634
|
-
const warnCount = checks.filter((item) => item.severity === "warn").length;
|
|
635
|
-
const failCount = checks.filter((item) => item.severity === "fail").length;
|
|
636
|
-
|
|
637
|
-
return {
|
|
638
|
-
checks,
|
|
639
|
-
readiness,
|
|
640
|
-
passCount,
|
|
641
|
-
warnCount,
|
|
642
|
-
failCount,
|
|
643
|
-
exitCode: failCount > 0 ? 1 : 0,
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
export function buildDoctorSupportBundle(
|
|
648
|
-
options: DoctorOptions,
|
|
649
|
-
deps: DoctorDeps,
|
|
650
|
-
report: DoctorReport,
|
|
651
|
-
): DoctorSupportBundle {
|
|
652
|
-
return {
|
|
653
|
-
schema: "agent-inspector.support-bundle/v1",
|
|
654
|
-
createdAt: new Date().toISOString(),
|
|
655
|
-
report,
|
|
656
|
-
environment: {
|
|
657
|
-
platform: deps.platform,
|
|
658
|
-
cwd: deps.cwd,
|
|
659
|
-
basePath: getConfiguredBasePath(deps.env),
|
|
660
|
-
port: options.port,
|
|
661
|
-
hasControlToken:
|
|
662
|
-
deps.env["AGENT_INSPECTOR_CONTROL_TOKEN"] !== undefined ||
|
|
663
|
-
deps.env["AGENT_INSPECTOR_API_TOKEN"] !== undefined,
|
|
664
|
-
hasProxyToken: deps.env["AGENT_INSPECTOR_PROXY_TOKEN"] !== undefined,
|
|
665
|
-
hasProviderJsonOverride:
|
|
666
|
-
options.providersJson !== null || deps.env["AGENT_INSPECTOR_PROVIDERS_JSON"] !== undefined,
|
|
667
|
-
},
|
|
668
|
-
storage: verboseChecks(options, deps).filter((item) => item.name.includes("storage")),
|
|
669
|
-
};
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
export function formatDoctorReport(report: DoctorReport): string {
|
|
673
|
-
const lines = ["agent-inspector doctor", ""];
|
|
674
|
-
for (const item of report.checks) {
|
|
675
|
-
lines.push(`${item.severity.toUpperCase().padEnd(4)} ${item.name}: ${item.message}`);
|
|
676
|
-
if (item.hint !== null) {
|
|
677
|
-
lines.push(` ${item.hint}`);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
lines.push("");
|
|
681
|
-
lines.push(
|
|
682
|
-
`Summary: ${report.passCount} pass, ${report.warnCount} warn, ${report.failCount} fail`,
|
|
683
|
-
);
|
|
684
|
-
return `${lines.join("\n")}\n`;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
export function formatDoctorJson(report: DoctorReport): string {
|
|
688
|
-
return `${JSON.stringify(report, null, 2)}\n`;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
export function formatDoctorSupportBundle(bundle: DoctorSupportBundle): string {
|
|
692
|
-
return `${JSON.stringify(bundle, null, 2)}\n`;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
export function doctorHelp(): string {
|
|
696
|
-
return [
|
|
697
|
-
"agent-inspector doctor",
|
|
698
|
-
"",
|
|
699
|
-
"Usage:",
|
|
700
|
-
" agent-inspector doctor [--port <port>] [--config-dir <dir>] [--providers <json>] [--fix] [--json] [--support-bundle] [--verbose] [--chrome-extension]",
|
|
701
|
-
"",
|
|
702
|
-
"Checks local backend health, provider config presence, and package metadata.",
|
|
703
|
-
"The backend check probes the local health endpoint and does not require a composed Web UI.",
|
|
704
|
-
"Pass --fix to run safe local repairs before checks, including legacy data-dir migration.",
|
|
705
|
-
"Pass --json to emit a machine-readable report for CI, scripts, and Jenkins.",
|
|
706
|
-
"Pass --support-bundle to emit a redacted JSON bundle with readiness causes and storage summary.",
|
|
707
|
-
"Pass --verbose to include local paths, capture mode, and log storage checks.",
|
|
708
|
-
"Pass --chrome-extension to also check Chrome companion source and packaged artifacts.",
|
|
709
|
-
"",
|
|
710
|
-
].join("\n");
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
async function fetchHealth(port: number, basePath: string): Promise<HealthProbe> {
|
|
714
|
-
const controller = new AbortController();
|
|
715
|
-
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
716
|
-
try {
|
|
717
|
-
const response = await fetch(
|
|
718
|
-
appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/api/health", basePath),
|
|
719
|
-
{
|
|
720
|
-
cache: "no-store",
|
|
721
|
-
signal: controller.signal,
|
|
722
|
-
},
|
|
723
|
-
);
|
|
724
|
-
return { ok: response.ok, status: response.status };
|
|
725
|
-
} catch {
|
|
726
|
-
return { ok: false, status: null };
|
|
727
|
-
} finally {
|
|
728
|
-
clearTimeout(timeout);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
async function fetchReadiness(port: number, basePath: string): Promise<ReadinessProbe> {
|
|
733
|
-
const controller = new AbortController();
|
|
734
|
-
const timeout = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
735
|
-
try {
|
|
736
|
-
const response = await fetch(
|
|
737
|
-
appendBasePathToOrigin(`http://127.0.0.1:${port}`, "/readyz", basePath),
|
|
738
|
-
{
|
|
739
|
-
cache: "no-store",
|
|
740
|
-
signal: controller.signal,
|
|
741
|
-
},
|
|
742
|
-
);
|
|
743
|
-
let body: unknown = null;
|
|
744
|
-
try {
|
|
745
|
-
body = await response.json();
|
|
746
|
-
} catch {
|
|
747
|
-
body = null;
|
|
748
|
-
}
|
|
749
|
-
return { ok: response.ok, status: response.status, body };
|
|
750
|
-
} catch {
|
|
751
|
-
return { ok: false, status: null, body: null };
|
|
752
|
-
} finally {
|
|
753
|
-
clearTimeout(timeout);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function isPortOpen(port: number): Promise<boolean> {
|
|
758
|
-
return new Promise((resolveOpen) => {
|
|
759
|
-
const socket = createConnection({ host: "127.0.0.1", port });
|
|
760
|
-
const finish = (value: boolean): void => {
|
|
761
|
-
socket.removeAllListeners();
|
|
762
|
-
socket.destroy();
|
|
763
|
-
resolveOpen(value);
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
socket.setTimeout(PROBE_TIMEOUT_MS);
|
|
767
|
-
socket.once("connect", () => finish(true));
|
|
768
|
-
socket.once("timeout", () => finish(false));
|
|
769
|
-
socket.once("error", () => finish(false));
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
function readText(path: string): string | null {
|
|
774
|
-
try {
|
|
775
|
-
return readFileSync(path, "utf8");
|
|
776
|
-
} catch {
|
|
777
|
-
return null;
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
function listDir(path: string): readonly string[] | null {
|
|
782
|
-
try {
|
|
783
|
-
return readdirSync(path);
|
|
784
|
-
} catch {
|
|
785
|
-
return null;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
function stat(path: string): { isDirectory: boolean; size: number } | null {
|
|
790
|
-
try {
|
|
791
|
-
const fileStat = statSync(path);
|
|
792
|
-
return { isDirectory: fileStat.isDirectory(), size: fileStat.size };
|
|
793
|
-
} catch {
|
|
794
|
-
return null;
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
export function createDoctorDeps(): DoctorDeps {
|
|
799
|
-
return {
|
|
800
|
-
env: process.env,
|
|
801
|
-
platform: process.platform,
|
|
802
|
-
cwd: process.cwd(),
|
|
803
|
-
exists: existsSync,
|
|
804
|
-
readText,
|
|
805
|
-
listDir,
|
|
806
|
-
stat,
|
|
807
|
-
fetchHealth,
|
|
808
|
-
fetchReadiness,
|
|
809
|
-
isPortOpen,
|
|
810
|
-
};
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
export async function runDoctor(argv: readonly string[]): Promise<number> {
|
|
814
|
-
const parsed = parseDoctorArgs(argv);
|
|
815
|
-
if (parsed.kind === "error") {
|
|
816
|
-
process.stderr.write(`${parsed.message}\n`);
|
|
817
|
-
return 2;
|
|
818
|
-
}
|
|
819
|
-
if (parsed.options.help) {
|
|
820
|
-
process.stdout.write(doctorHelp());
|
|
821
|
-
return 0;
|
|
822
|
-
}
|
|
823
|
-
if (parsed.options.fix) {
|
|
824
|
-
migrateLegacyDataDirs();
|
|
825
|
-
}
|
|
826
|
-
const report = await buildDoctorReport(parsed.options);
|
|
827
|
-
if (parsed.options.supportBundle) {
|
|
828
|
-
const bundle = buildDoctorSupportBundle(parsed.options, createDoctorDeps(), report);
|
|
829
|
-
process.stdout.write(formatDoctorSupportBundle(bundle));
|
|
830
|
-
} else {
|
|
831
|
-
process.stdout.write(
|
|
832
|
-
parsed.options.json ? formatDoctorJson(report) : formatDoctorReport(report),
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
|
-
return report.exitCode;
|
|
836
|
-
}
|