@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/cli/onboard.ts
DELETED
|
@@ -1,1668 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `agent-inspector onboard` subcommand.
|
|
3
|
-
*
|
|
4
|
-
* Generates Agent Inspector onboarding skills into the user's home dir.
|
|
5
|
-
* Claude Code gets a skill + slash command. Codex gets a skill under
|
|
6
|
-
* ~/.codex/skills that guides the user through connecting /api/mcp.
|
|
7
|
-
*
|
|
8
|
-
* Idempotent by default: re-runs without `--force` write missing files and
|
|
9
|
-
* refresh older generated files. `--force` overwrites, `--dry-run` writes
|
|
10
|
-
* nothing. Designed as an explicit setup step that can fail softly without
|
|
11
|
-
* blocking normal CLI use.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import {
|
|
15
|
-
mkdirSync,
|
|
16
|
-
writeFileSync,
|
|
17
|
-
existsSync,
|
|
18
|
-
readFileSync,
|
|
19
|
-
unlinkSync,
|
|
20
|
-
readdirSync,
|
|
21
|
-
rmdirSync,
|
|
22
|
-
} from "node:fs";
|
|
23
|
-
import { homedir } from "node:os";
|
|
24
|
-
import { dirname, join } from "node:path";
|
|
25
|
-
import { fileURLToPath } from "node:url";
|
|
26
|
-
|
|
27
|
-
import { appendBasePathToOrigin, getConfiguredBasePath } from "../lib/basePath.js";
|
|
28
|
-
import { detectAll, detectFirst } from "./detect-tools.js";
|
|
29
|
-
import { type OnboardTarget, resolveOnboardTarget } from "./onboardTarget.js";
|
|
30
|
-
import { renderCommandOnboard } from "./templates/command-onboard.js";
|
|
31
|
-
import {
|
|
32
|
-
renderCodexSkillOnboard,
|
|
33
|
-
REQUIRED_CODEX_PHASE_HEADINGS,
|
|
34
|
-
} from "./templates/codex-skill-onboard.js";
|
|
35
|
-
import { renderSkillOnboard, REQUIRED_PHASE_HEADINGS } from "./templates/skill-onboard.js";
|
|
36
|
-
|
|
37
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
-
const __dirname = dirname(__filename);
|
|
39
|
-
|
|
40
|
-
const DEFAULT_PORT = 9527;
|
|
41
|
-
const DEFAULT_ORIGIN = `http://localhost:${DEFAULT_PORT}`;
|
|
42
|
-
const DEFAULT_MCP_URL = appendBasePathToOrigin(
|
|
43
|
-
DEFAULT_ORIGIN,
|
|
44
|
-
"/api/mcp",
|
|
45
|
-
getConfiguredBasePath({}),
|
|
46
|
-
);
|
|
47
|
-
const LEGACY_MCP_URL = "http://localhost:9527/api/mcp";
|
|
48
|
-
const SKILL_DIR_NAME = "agent-inspector-onboard";
|
|
49
|
-
const SKILL_FILE_NAME = "SKILL.md";
|
|
50
|
-
// Windows reserves `:` for NTFS alternate data streams, so a file named
|
|
51
|
-
// `agent-inspector:onboard.md` becomes an empty `agent-inspector` on disk.
|
|
52
|
-
// Use `-` on Windows and `:` on Unix to match the slash-command convention
|
|
53
|
-
// (`/agent-inspector:onboard` vs `/agent-inspector-onboard`) within the
|
|
54
|
-
// constraints of each platform.
|
|
55
|
-
const COMMAND_FILE_NAME =
|
|
56
|
-
process.platform === "win32" ? "agent-inspector-onboard.md" : "agent-inspector:onboard.md";
|
|
57
|
-
|
|
58
|
-
const GENERATED_VERSION_PATTERN = /^\s*version:\s*([0-9]+\.[0-9]+\.[0-9]+(?:[-+][^\s]+)?)\s*$/m;
|
|
59
|
-
const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
60
|
-
const SEMVER_PATTERN = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/;
|
|
61
|
-
|
|
62
|
-
export type OnboardFlags = {
|
|
63
|
-
force: boolean;
|
|
64
|
-
dryRun: boolean;
|
|
65
|
-
skipProvider: boolean;
|
|
66
|
-
skipToolWire: boolean;
|
|
67
|
-
codexOnly: boolean;
|
|
68
|
-
skipCodexSkill: boolean;
|
|
69
|
-
opencode: boolean;
|
|
70
|
-
opencodeOnly: boolean;
|
|
71
|
-
opencodeConfig: string | null;
|
|
72
|
-
opencodeTransport: AgentMcpTransport;
|
|
73
|
-
opencodeMcpUrl: string | null;
|
|
74
|
-
mimo: boolean;
|
|
75
|
-
mimoOnly: boolean;
|
|
76
|
-
mimoConfig: string | null;
|
|
77
|
-
mimoTransport: AgentMcpTransport;
|
|
78
|
-
mimoMcpUrl: string | null;
|
|
79
|
-
instanceName: string | null;
|
|
80
|
-
baseUrl: string | null;
|
|
81
|
-
controlBaseUrl: string | null;
|
|
82
|
-
agentBaseUrl: string | null;
|
|
83
|
-
uiUrl: string | null | undefined;
|
|
84
|
-
mcpName: string | null;
|
|
85
|
-
uninstall: boolean;
|
|
86
|
-
status: boolean;
|
|
87
|
-
json: boolean;
|
|
88
|
-
skillDir: string | null;
|
|
89
|
-
codexSkillDir: string | null;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
type AgentMcpTransport = "local" | "remote";
|
|
93
|
-
type AgentMcpStatusTransport = AgentMcpTransport | "unknown";
|
|
94
|
-
type AgentMcpConfigState = "missing" | "configured" | "custom" | "disabled" | "invalid";
|
|
95
|
-
|
|
96
|
-
type AgentMcpConfigTarget = {
|
|
97
|
-
label: "OpenCode" | "MiMo Code";
|
|
98
|
-
configLabel: "OpenCode MCP config" | "MiMo Code MCP config";
|
|
99
|
-
configPath: string;
|
|
100
|
-
transport: AgentMcpTransport;
|
|
101
|
-
endpoint: string;
|
|
102
|
-
mcpName: string;
|
|
103
|
-
setupAction: string;
|
|
104
|
-
forceAction: string;
|
|
105
|
-
verifyCommand: string;
|
|
106
|
-
invalidAction: string;
|
|
107
|
-
configObjectName: string;
|
|
108
|
-
defaultSchema: string;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
type AgentMcpConfigStatus = {
|
|
112
|
-
label: "OpenCode MCP config" | "MiMo Code MCP config";
|
|
113
|
-
path: string;
|
|
114
|
-
state: AgentMcpConfigState;
|
|
115
|
-
transport: AgentMcpStatusTransport;
|
|
116
|
-
endpoint: string;
|
|
117
|
-
mcpName: string;
|
|
118
|
-
action: string;
|
|
119
|
-
forceAction: string;
|
|
120
|
-
verifyCommand: string;
|
|
121
|
-
detail: string;
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
type AgentMcpConfigPlan = {
|
|
125
|
-
status: AgentMcpConfigStatus;
|
|
126
|
-
body: string | null;
|
|
127
|
-
shouldWrite: boolean;
|
|
128
|
-
shouldRemove: boolean;
|
|
129
|
-
blockedMessage: string | null;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
type AgentConfigReadResult =
|
|
133
|
-
| {
|
|
134
|
-
ok: true;
|
|
135
|
-
exists: boolean;
|
|
136
|
-
config: Record<string, unknown>;
|
|
137
|
-
}
|
|
138
|
-
| {
|
|
139
|
-
ok: false;
|
|
140
|
-
exists: boolean;
|
|
141
|
-
message: string;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
type OnboardTargets = {
|
|
145
|
-
claudeSkillFile: string;
|
|
146
|
-
claudeCommandFile: string;
|
|
147
|
-
codexSkillFile: string;
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
type PlannedFile = {
|
|
151
|
-
label: string;
|
|
152
|
-
path: string;
|
|
153
|
-
body: string;
|
|
154
|
-
enabled: boolean;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
type ParsedVersion = {
|
|
158
|
-
major: number;
|
|
159
|
-
minor: number;
|
|
160
|
-
patch: number;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
type GeneratedFileState = "missing" | "current" | "outdated" | "newer" | "custom";
|
|
164
|
-
|
|
165
|
-
type GeneratedFileStatus = {
|
|
166
|
-
label: string;
|
|
167
|
-
path: string;
|
|
168
|
-
state: GeneratedFileState;
|
|
169
|
-
version: string | null;
|
|
170
|
-
action: string;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
function actionForStatus(label: string, state: GeneratedFileState): string {
|
|
174
|
-
switch (state) {
|
|
175
|
-
case "missing":
|
|
176
|
-
switch (label) {
|
|
177
|
-
case "Codex skill":
|
|
178
|
-
return "agent-inspector onboard --codex-only";
|
|
179
|
-
case "Claude skill":
|
|
180
|
-
case "Claude command":
|
|
181
|
-
return "agent-inspector onboard --skip-codex-skill";
|
|
182
|
-
default:
|
|
183
|
-
return "agent-inspector onboard";
|
|
184
|
-
}
|
|
185
|
-
case "outdated":
|
|
186
|
-
return "agent-inspector onboard --force";
|
|
187
|
-
case "custom":
|
|
188
|
-
return "preserved; use agent-inspector onboard --force to replace";
|
|
189
|
-
case "newer":
|
|
190
|
-
return "no action; installed file is newer than this package";
|
|
191
|
-
case "current":
|
|
192
|
-
return "no action";
|
|
193
|
-
default:
|
|
194
|
-
return "agent-inspector onboard";
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function parseFlags(argv: readonly string[]): OnboardFlags {
|
|
199
|
-
const flags: OnboardFlags = {
|
|
200
|
-
force: false,
|
|
201
|
-
dryRun: false,
|
|
202
|
-
skipProvider: false,
|
|
203
|
-
skipToolWire: false,
|
|
204
|
-
codexOnly: false,
|
|
205
|
-
skipCodexSkill: false,
|
|
206
|
-
opencode: false,
|
|
207
|
-
opencodeOnly: false,
|
|
208
|
-
opencodeConfig: null,
|
|
209
|
-
opencodeTransport: "local",
|
|
210
|
-
opencodeMcpUrl: null,
|
|
211
|
-
mimo: false,
|
|
212
|
-
mimoOnly: false,
|
|
213
|
-
mimoConfig: null,
|
|
214
|
-
mimoTransport: "local",
|
|
215
|
-
mimoMcpUrl: null,
|
|
216
|
-
instanceName: null,
|
|
217
|
-
baseUrl: null,
|
|
218
|
-
controlBaseUrl: null,
|
|
219
|
-
agentBaseUrl: null,
|
|
220
|
-
uiUrl: undefined,
|
|
221
|
-
mcpName: null,
|
|
222
|
-
uninstall: false,
|
|
223
|
-
status: false,
|
|
224
|
-
json: false,
|
|
225
|
-
skillDir: null,
|
|
226
|
-
codexSkillDir: null,
|
|
227
|
-
};
|
|
228
|
-
for (let i = 0; i < argv.length; i++) {
|
|
229
|
-
const arg = argv[i];
|
|
230
|
-
switch (arg) {
|
|
231
|
-
case undefined:
|
|
232
|
-
continue;
|
|
233
|
-
case "--force":
|
|
234
|
-
flags.force = true;
|
|
235
|
-
break;
|
|
236
|
-
case "--dry-run":
|
|
237
|
-
flags.dryRun = true;
|
|
238
|
-
break;
|
|
239
|
-
case "--skip-provider":
|
|
240
|
-
flags.skipProvider = true;
|
|
241
|
-
break;
|
|
242
|
-
case "--skip-tool-wire":
|
|
243
|
-
flags.skipToolWire = true;
|
|
244
|
-
break;
|
|
245
|
-
case "--codex-only":
|
|
246
|
-
flags.codexOnly = true;
|
|
247
|
-
break;
|
|
248
|
-
case "--skip-codex-skill":
|
|
249
|
-
flags.skipCodexSkill = true;
|
|
250
|
-
break;
|
|
251
|
-
case "--opencode":
|
|
252
|
-
flags.opencode = true;
|
|
253
|
-
break;
|
|
254
|
-
case "--opencode-only":
|
|
255
|
-
flags.opencode = true;
|
|
256
|
-
flags.opencodeOnly = true;
|
|
257
|
-
break;
|
|
258
|
-
case "--opencode-config": {
|
|
259
|
-
const next = argv[i + 1];
|
|
260
|
-
if (next === undefined) {
|
|
261
|
-
process.stderr.write(
|
|
262
|
-
"agent-inspector onboard: --opencode-config requires a path argument\n",
|
|
263
|
-
);
|
|
264
|
-
process.exit(2);
|
|
265
|
-
}
|
|
266
|
-
flags.opencodeConfig = next;
|
|
267
|
-
i++;
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
case "--opencode-transport": {
|
|
271
|
-
const next = argv[i + 1];
|
|
272
|
-
if (next === undefined) {
|
|
273
|
-
process.stderr.write(
|
|
274
|
-
"agent-inspector onboard: --opencode-transport requires local or remote\n",
|
|
275
|
-
);
|
|
276
|
-
process.exit(2);
|
|
277
|
-
}
|
|
278
|
-
switch (next) {
|
|
279
|
-
case "local":
|
|
280
|
-
case "remote":
|
|
281
|
-
flags.opencodeTransport = next;
|
|
282
|
-
break;
|
|
283
|
-
default:
|
|
284
|
-
process.stderr.write(
|
|
285
|
-
`agent-inspector onboard: invalid --opencode-transport: ${next}\n`,
|
|
286
|
-
);
|
|
287
|
-
process.exit(2);
|
|
288
|
-
}
|
|
289
|
-
i++;
|
|
290
|
-
break;
|
|
291
|
-
}
|
|
292
|
-
case "--opencode-mcp-url": {
|
|
293
|
-
const next = argv[i + 1];
|
|
294
|
-
if (next === undefined) {
|
|
295
|
-
process.stderr.write("agent-inspector onboard: --opencode-mcp-url requires a URL\n");
|
|
296
|
-
process.exit(2);
|
|
297
|
-
}
|
|
298
|
-
flags.opencodeMcpUrl = next;
|
|
299
|
-
i++;
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
case "--mimo":
|
|
303
|
-
flags.mimo = true;
|
|
304
|
-
break;
|
|
305
|
-
case "--mimo-only":
|
|
306
|
-
flags.mimo = true;
|
|
307
|
-
flags.mimoOnly = true;
|
|
308
|
-
break;
|
|
309
|
-
case "--mimo-config": {
|
|
310
|
-
const next = argv[i + 1];
|
|
311
|
-
if (next === undefined) {
|
|
312
|
-
process.stderr.write("agent-inspector onboard: --mimo-config requires a path argument\n");
|
|
313
|
-
process.exit(2);
|
|
314
|
-
}
|
|
315
|
-
flags.mimoConfig = next;
|
|
316
|
-
i++;
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case "--mimo-transport": {
|
|
320
|
-
const next = argv[i + 1];
|
|
321
|
-
if (next === undefined) {
|
|
322
|
-
process.stderr.write(
|
|
323
|
-
"agent-inspector onboard: --mimo-transport requires local or remote\n",
|
|
324
|
-
);
|
|
325
|
-
process.exit(2);
|
|
326
|
-
}
|
|
327
|
-
switch (next) {
|
|
328
|
-
case "local":
|
|
329
|
-
case "remote":
|
|
330
|
-
flags.mimoTransport = next;
|
|
331
|
-
break;
|
|
332
|
-
default:
|
|
333
|
-
process.stderr.write(`agent-inspector onboard: invalid --mimo-transport: ${next}\n`);
|
|
334
|
-
process.exit(2);
|
|
335
|
-
}
|
|
336
|
-
i++;
|
|
337
|
-
break;
|
|
338
|
-
}
|
|
339
|
-
case "--mimo-mcp-url": {
|
|
340
|
-
const next = argv[i + 1];
|
|
341
|
-
if (next === undefined) {
|
|
342
|
-
process.stderr.write("agent-inspector onboard: --mimo-mcp-url requires a URL\n");
|
|
343
|
-
process.exit(2);
|
|
344
|
-
}
|
|
345
|
-
flags.mimoMcpUrl = next;
|
|
346
|
-
i++;
|
|
347
|
-
break;
|
|
348
|
-
}
|
|
349
|
-
case "--instance": {
|
|
350
|
-
const next = argv[i + 1];
|
|
351
|
-
if (next === undefined || next.trim().length === 0) {
|
|
352
|
-
process.stderr.write("agent-inspector onboard: --instance requires a name\n");
|
|
353
|
-
process.exit(2);
|
|
354
|
-
}
|
|
355
|
-
flags.instanceName = next;
|
|
356
|
-
i++;
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
case "--base-url": {
|
|
360
|
-
const next = argv[i + 1];
|
|
361
|
-
if (next === undefined || next.trim().length === 0) {
|
|
362
|
-
process.stderr.write("agent-inspector onboard: --base-url requires a URL\n");
|
|
363
|
-
process.exit(2);
|
|
364
|
-
}
|
|
365
|
-
flags.baseUrl = next;
|
|
366
|
-
i++;
|
|
367
|
-
break;
|
|
368
|
-
}
|
|
369
|
-
case "--control-base-url": {
|
|
370
|
-
const next = argv[i + 1];
|
|
371
|
-
if (next === undefined || next.trim().length === 0) {
|
|
372
|
-
process.stderr.write("agent-inspector onboard: --control-base-url requires a URL\n");
|
|
373
|
-
process.exit(2);
|
|
374
|
-
}
|
|
375
|
-
flags.controlBaseUrl = next;
|
|
376
|
-
i++;
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
case "--agent-base-url": {
|
|
380
|
-
const next = argv[i + 1];
|
|
381
|
-
if (next === undefined || next.trim().length === 0) {
|
|
382
|
-
process.stderr.write("agent-inspector onboard: --agent-base-url requires a URL\n");
|
|
383
|
-
process.exit(2);
|
|
384
|
-
}
|
|
385
|
-
flags.agentBaseUrl = next;
|
|
386
|
-
i++;
|
|
387
|
-
break;
|
|
388
|
-
}
|
|
389
|
-
case "--ui-url": {
|
|
390
|
-
const next = argv[i + 1];
|
|
391
|
-
if (next === undefined || next.trim().length === 0) {
|
|
392
|
-
process.stderr.write("agent-inspector onboard: --ui-url requires a URL\n");
|
|
393
|
-
process.exit(2);
|
|
394
|
-
}
|
|
395
|
-
flags.uiUrl = next;
|
|
396
|
-
i++;
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
399
|
-
case "--no-ui":
|
|
400
|
-
flags.uiUrl = null;
|
|
401
|
-
break;
|
|
402
|
-
case "--mcp-name": {
|
|
403
|
-
const next = argv[i + 1];
|
|
404
|
-
if (next === undefined || next.trim().length === 0) {
|
|
405
|
-
process.stderr.write("agent-inspector onboard: --mcp-name requires a name\n");
|
|
406
|
-
process.exit(2);
|
|
407
|
-
}
|
|
408
|
-
flags.mcpName = next;
|
|
409
|
-
i++;
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
412
|
-
case "--uninstall":
|
|
413
|
-
flags.uninstall = true;
|
|
414
|
-
break;
|
|
415
|
-
case "--status":
|
|
416
|
-
flags.status = true;
|
|
417
|
-
break;
|
|
418
|
-
case "--json":
|
|
419
|
-
flags.json = true;
|
|
420
|
-
break;
|
|
421
|
-
case "--skill-dir": {
|
|
422
|
-
const next = argv[i + 1];
|
|
423
|
-
if (next === undefined) {
|
|
424
|
-
process.stderr.write("agent-inspector onboard: --skill-dir requires a path argument\n");
|
|
425
|
-
process.exit(2);
|
|
426
|
-
}
|
|
427
|
-
flags.skillDir = next;
|
|
428
|
-
i++;
|
|
429
|
-
break;
|
|
430
|
-
}
|
|
431
|
-
case "--codex-skill-dir": {
|
|
432
|
-
const next = argv[i + 1];
|
|
433
|
-
if (next === undefined) {
|
|
434
|
-
process.stderr.write(
|
|
435
|
-
"agent-inspector onboard: --codex-skill-dir requires a path argument\n",
|
|
436
|
-
);
|
|
437
|
-
process.exit(2);
|
|
438
|
-
}
|
|
439
|
-
flags.codexSkillDir = next;
|
|
440
|
-
i++;
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
case "--help":
|
|
444
|
-
case "-h":
|
|
445
|
-
printHelp();
|
|
446
|
-
process.exit(0);
|
|
447
|
-
break;
|
|
448
|
-
default:
|
|
449
|
-
process.stderr.write(`agent-inspector onboard: unknown flag: ${arg}\n`);
|
|
450
|
-
process.exit(2);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return flags;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function printHelp(): void {
|
|
457
|
-
process.stdout.write(`agent-inspector onboard - install Agent Inspector onboarding skills
|
|
458
|
-
|
|
459
|
-
Usage:
|
|
460
|
-
agent-inspector onboard [options]
|
|
461
|
-
|
|
462
|
-
Options:
|
|
463
|
-
--force Overwrite existing generated skills and slash command
|
|
464
|
-
--dry-run Print target paths and template previews, write nothing
|
|
465
|
-
--skip-provider Skip the provider-setup phase in the Claude skill body
|
|
466
|
-
--skip-tool-wire Skip the wire-tool phase in the Claude skill body
|
|
467
|
-
--skip-codex-skill Install only the Claude Code skill and slash command
|
|
468
|
-
--codex-only Install only the Codex skill
|
|
469
|
-
--opencode Also configure OpenCode MCP in opencode.json/jsonc
|
|
470
|
-
--opencode-only Configure only OpenCode MCP
|
|
471
|
-
--opencode-config <path> Override OpenCode config path (default: ~/.config/opencode/opencode.json)
|
|
472
|
-
--opencode-transport <local|remote>
|
|
473
|
-
OpenCode MCP transport to write (default: local)
|
|
474
|
-
--opencode-mcp-url <url> Agent Inspector MCP endpoint for OpenCode
|
|
475
|
-
--mimo Also configure MiMo Code MCP in mimocode.json/jsonc
|
|
476
|
-
--mimo-only Configure only MiMo Code MCP
|
|
477
|
-
--mimo-config <path> Override MiMo config path (default: ~/.config/mimocode/mimocode.jsonc)
|
|
478
|
-
--mimo-transport <local|remote>
|
|
479
|
-
MiMo Code MCP transport to write (default: local)
|
|
480
|
-
--mimo-mcp-url <url> Agent Inspector MCP endpoint for MiMo Code
|
|
481
|
-
--instance <name> Target a registered named Inspector instance
|
|
482
|
-
--base-url <url> Target an explicit credential-free backend control/agent Base URL
|
|
483
|
-
--control-base-url <url> Control API Base URL for a separated deployment
|
|
484
|
-
--agent-base-url <url> Proxy and MCP Base URL for Coding Agents
|
|
485
|
-
--ui-url <url> Browser UI URL for a separated deployment
|
|
486
|
-
--no-ui Declare that the explicit target has no browser UI
|
|
487
|
-
--mcp-name <name> MCP config key (named default: agent-inspector-<instance>)
|
|
488
|
-
--uninstall Remove generated files whose version matches this package
|
|
489
|
-
--status Show installed onboarding file versions and suggested action
|
|
490
|
-
--json Emit machine-readable JSON with --status
|
|
491
|
-
--skill-dir <path> Override the target Claude root directory (default: ~/.claude)
|
|
492
|
-
--codex-skill-dir <path> Override the target Codex skills directory (default: ~/.codex/skills)
|
|
493
|
-
-h, --help Show this help
|
|
494
|
-
|
|
495
|
-
Exit codes:
|
|
496
|
-
0 success (or already installed)
|
|
497
|
-
2 invalid arguments
|
|
498
|
-
1 write failed
|
|
499
|
-
`);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
function resolveTargets(flags: OnboardFlags): OnboardTargets {
|
|
503
|
-
const claudeRoot = flags.skillDir ?? join(homedir(), ".claude");
|
|
504
|
-
const claudeSkillDir = join(claudeRoot, "skills", SKILL_DIR_NAME);
|
|
505
|
-
const claudeCommandsDir = join(claudeRoot, "commands");
|
|
506
|
-
const codexRoot = process.env["CODEX_HOME"] ?? join(homedir(), ".codex");
|
|
507
|
-
const codexSkillsDir = flags.codexSkillDir ?? join(codexRoot, "skills");
|
|
508
|
-
return {
|
|
509
|
-
claudeSkillFile: join(claudeSkillDir, SKILL_FILE_NAME),
|
|
510
|
-
claudeCommandFile: join(claudeCommandsDir, COMMAND_FILE_NAME),
|
|
511
|
-
codexSkillFile: join(codexSkillsDir, SKILL_DIR_NAME, SKILL_FILE_NAME),
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
516
|
-
return typeof value === "object" && value !== null;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function isOpenCodeEnabled(flags: OnboardFlags): boolean {
|
|
520
|
-
return flags.opencode || flags.opencodeOnly;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
function isMiMoEnabled(flags: OnboardFlags): boolean {
|
|
524
|
-
return flags.mimo || flags.mimoOnly;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function hasOnlyAgentMcpTarget(flags: OnboardFlags): boolean {
|
|
528
|
-
return flags.opencodeOnly || flags.mimoOnly;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function resolveOpenCodeConfigPath(flags: OnboardFlags): string {
|
|
532
|
-
if (flags.opencodeConfig !== null) {
|
|
533
|
-
return flags.opencodeConfig;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
const configDir = join(homedir(), ".config", "opencode");
|
|
537
|
-
const jsoncPath = join(configDir, "opencode.jsonc");
|
|
538
|
-
if (existsSync(jsoncPath)) {
|
|
539
|
-
return jsoncPath;
|
|
540
|
-
}
|
|
541
|
-
return join(configDir, "opencode.json");
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function resolveMiMoConfigPath(flags: OnboardFlags): string {
|
|
545
|
-
if (flags.mimoConfig !== null) {
|
|
546
|
-
return flags.mimoConfig;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
const configDir = join(homedir(), ".config", "mimocode");
|
|
550
|
-
const jsoncPath = join(configDir, "mimocode.jsonc");
|
|
551
|
-
if (existsSync(jsoncPath)) {
|
|
552
|
-
return jsoncPath;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
const jsonPath = join(configDir, "mimocode.json");
|
|
556
|
-
if (existsSync(jsonPath)) {
|
|
557
|
-
return jsonPath;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const legacyDir = join(homedir(), ".mimocode");
|
|
561
|
-
const legacyJsoncPath = join(legacyDir, "config.jsonc");
|
|
562
|
-
if (existsSync(legacyJsoncPath)) {
|
|
563
|
-
return legacyJsoncPath;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const legacyJsonPath = join(legacyDir, "config.json");
|
|
567
|
-
if (existsSync(legacyJsonPath)) {
|
|
568
|
-
return legacyJsonPath;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return jsoncPath;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function onboardSelectionArguments(flags: OnboardFlags, target: OnboardTarget): string {
|
|
575
|
-
const args: string[] = [];
|
|
576
|
-
if (target.kind === "instance" && target.instanceName !== null) {
|
|
577
|
-
args.push("--instance", target.instanceName);
|
|
578
|
-
} else if (target.kind === "base-url") {
|
|
579
|
-
const usedSharedBaseUrl =
|
|
580
|
-
flags.baseUrl !== null &&
|
|
581
|
-
flags.controlBaseUrl === null &&
|
|
582
|
-
flags.agentBaseUrl === null &&
|
|
583
|
-
flags.uiUrl === undefined;
|
|
584
|
-
if (usedSharedBaseUrl) {
|
|
585
|
-
args.push("--base-url", target.baseUrl);
|
|
586
|
-
} else {
|
|
587
|
-
args.push("--control-base-url", target.controlBaseUrl);
|
|
588
|
-
args.push("--agent-base-url", target.agentBaseUrl);
|
|
589
|
-
if (target.uiUrl === null) {
|
|
590
|
-
args.push("--no-ui");
|
|
591
|
-
} else {
|
|
592
|
-
args.push("--ui-url", target.uiUrl);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
if (flags.mcpName !== null) args.push("--mcp-name", target.mcpName);
|
|
597
|
-
return args.length === 0 ? "" : ` ${args.join(" ")}`;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function openCodeTarget(flags: OnboardFlags, onboardTarget: OnboardTarget): AgentMcpConfigTarget {
|
|
601
|
-
const setupAction = `agent-inspector onboard --opencode-only${onboardSelectionArguments(flags, onboardTarget)}`;
|
|
602
|
-
return {
|
|
603
|
-
label: "OpenCode",
|
|
604
|
-
configLabel: "OpenCode MCP config",
|
|
605
|
-
configPath: resolveOpenCodeConfigPath(flags),
|
|
606
|
-
transport: flags.opencodeTransport,
|
|
607
|
-
endpoint: flags.opencodeMcpUrl ?? onboardTarget.mcpUrl,
|
|
608
|
-
mcpName: onboardTarget.mcpName,
|
|
609
|
-
setupAction,
|
|
610
|
-
forceAction: `${setupAction} --force`,
|
|
611
|
-
verifyCommand: "opencode mcp list",
|
|
612
|
-
invalidAction: `fix OpenCode JSON/JSONC, then rerun ${setupAction}`,
|
|
613
|
-
configObjectName: `mcp.${onboardTarget.mcpName}`,
|
|
614
|
-
defaultSchema: "https://opencode.ai/config.json",
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
function miMoTarget(flags: OnboardFlags, onboardTarget: OnboardTarget): AgentMcpConfigTarget {
|
|
619
|
-
const setupAction = `agent-inspector onboard --mimo-only${onboardSelectionArguments(flags, onboardTarget)}`;
|
|
620
|
-
return {
|
|
621
|
-
label: "MiMo Code",
|
|
622
|
-
configLabel: "MiMo Code MCP config",
|
|
623
|
-
configPath: resolveMiMoConfigPath(flags),
|
|
624
|
-
transport: flags.mimoTransport,
|
|
625
|
-
endpoint: flags.mimoMcpUrl ?? onboardTarget.mcpUrl,
|
|
626
|
-
mcpName: onboardTarget.mcpName,
|
|
627
|
-
setupAction,
|
|
628
|
-
forceAction: `${setupAction} --force`,
|
|
629
|
-
verifyCommand: "mimo mcp list",
|
|
630
|
-
invalidAction: `fix MiMo Code JSON/JSONC, then rerun ${setupAction}`,
|
|
631
|
-
configObjectName: `mcp.${onboardTarget.mcpName}`,
|
|
632
|
-
defaultSchema: "https://mimo.xiaomi.com/mimocode/config.json",
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
function stripJsoncComments(raw: string): string {
|
|
637
|
-
let result = "";
|
|
638
|
-
let index = 0;
|
|
639
|
-
let inString = false;
|
|
640
|
-
let stringChar = "";
|
|
641
|
-
|
|
642
|
-
while (index < raw.length) {
|
|
643
|
-
const ch = raw[index];
|
|
644
|
-
const next = raw[index + 1];
|
|
645
|
-
|
|
646
|
-
if (inString) {
|
|
647
|
-
if (ch === "\\" && next !== undefined) {
|
|
648
|
-
result += ch + next;
|
|
649
|
-
index += 2;
|
|
650
|
-
continue;
|
|
651
|
-
}
|
|
652
|
-
if (ch === stringChar) {
|
|
653
|
-
inString = false;
|
|
654
|
-
stringChar = "";
|
|
655
|
-
}
|
|
656
|
-
result += ch;
|
|
657
|
-
index += 1;
|
|
658
|
-
continue;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
if (ch === '"' || ch === "'") {
|
|
662
|
-
inString = true;
|
|
663
|
-
stringChar = ch;
|
|
664
|
-
result += ch;
|
|
665
|
-
index += 1;
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (ch === "/" && next === "/") {
|
|
670
|
-
while (index < raw.length && raw[index] !== "\n") {
|
|
671
|
-
index += 1;
|
|
672
|
-
}
|
|
673
|
-
continue;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
if (ch === "/" && next === "*") {
|
|
677
|
-
index += 2;
|
|
678
|
-
while (index < raw.length - 1 && !(raw[index] === "*" && raw[index + 1] === "/")) {
|
|
679
|
-
index += 1;
|
|
680
|
-
}
|
|
681
|
-
index += 2;
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
result += ch;
|
|
686
|
-
index += 1;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
return result;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
function readAgentConfig(path: string, target: AgentMcpConfigTarget): AgentConfigReadResult {
|
|
693
|
-
if (!existsSync(path)) {
|
|
694
|
-
return { ok: true, exists: false, config: {} };
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
try {
|
|
698
|
-
const raw = readFileSync(path, "utf8");
|
|
699
|
-
if (raw.trim().length === 0) {
|
|
700
|
-
return { ok: true, exists: true, config: {} };
|
|
701
|
-
}
|
|
702
|
-
const parsed: unknown = JSON.parse(stripJsoncComments(raw));
|
|
703
|
-
if (isObject(parsed)) {
|
|
704
|
-
return { ok: true, exists: true, config: parsed };
|
|
705
|
-
}
|
|
706
|
-
return {
|
|
707
|
-
ok: false,
|
|
708
|
-
exists: true,
|
|
709
|
-
message: `${target.label} config root must be a JSON object`,
|
|
710
|
-
};
|
|
711
|
-
} catch (err) {
|
|
712
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
713
|
-
return {
|
|
714
|
-
ok: false,
|
|
715
|
-
exists: true,
|
|
716
|
-
message,
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
function buildAgentMcpEntry(
|
|
722
|
-
transport: AgentMcpTransport,
|
|
723
|
-
endpoint: string,
|
|
724
|
-
): Record<string, unknown> {
|
|
725
|
-
switch (transport) {
|
|
726
|
-
case "local":
|
|
727
|
-
return {
|
|
728
|
-
type: "local",
|
|
729
|
-
command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
|
|
730
|
-
enabled: true,
|
|
731
|
-
};
|
|
732
|
-
case "remote":
|
|
733
|
-
return {
|
|
734
|
-
type: "remote",
|
|
735
|
-
url: endpoint,
|
|
736
|
-
enabled: true,
|
|
737
|
-
};
|
|
738
|
-
default:
|
|
739
|
-
return {
|
|
740
|
-
type: "local",
|
|
741
|
-
command: ["agent-inspector-mcp", "stdio", "--url", endpoint],
|
|
742
|
-
enabled: true,
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
function stringArrayEquals(value: unknown, expected: readonly string[]): boolean {
|
|
748
|
-
if (!Array.isArray(value) || value.length !== expected.length) {
|
|
749
|
-
return false;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
return value.every((item, index) => item === expected[index]);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
function knownMcpEndpoints(endpoint: string): readonly string[] {
|
|
756
|
-
return endpoint === DEFAULT_MCP_URL ? [DEFAULT_MCP_URL, LEGACY_MCP_URL] : [endpoint];
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
function isAgentLocalEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
|
|
760
|
-
if (!isObject(value)) {
|
|
761
|
-
return false;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
const enabled = value["enabled"];
|
|
765
|
-
if (!ignoreEnabled && enabled === false) {
|
|
766
|
-
return false;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const env = value["environment"];
|
|
770
|
-
const endpoints = knownMcpEndpoints(endpoint);
|
|
771
|
-
const usesUrlArg = endpoints.some((candidate) =>
|
|
772
|
-
stringArrayEquals(value["command"], ["agent-inspector-mcp", "stdio", "--url", candidate]),
|
|
773
|
-
);
|
|
774
|
-
const usesEnv =
|
|
775
|
-
stringArrayEquals(value["command"], ["agent-inspector-mcp", "stdio"]) &&
|
|
776
|
-
isObject(env) &&
|
|
777
|
-
endpoints.some((candidate) => env["AGENT_INSPECTOR_MCP_URL"] === candidate);
|
|
778
|
-
|
|
779
|
-
return value["type"] === "local" && (usesUrlArg || usesEnv);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
function isAgentRemoteEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
|
|
783
|
-
if (!isObject(value)) {
|
|
784
|
-
return false;
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const enabled = value["enabled"];
|
|
788
|
-
if (!ignoreEnabled && enabled === false) {
|
|
789
|
-
return false;
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
return (
|
|
793
|
-
value["type"] === "remote" &&
|
|
794
|
-
knownMcpEndpoints(endpoint).some((candidate) => value["url"] === candidate)
|
|
795
|
-
);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function isKnownAgentEntry(value: unknown, endpoint: string, ignoreEnabled: boolean): boolean {
|
|
799
|
-
return (
|
|
800
|
-
isAgentLocalEntry(value, endpoint, ignoreEnabled) ||
|
|
801
|
-
isAgentRemoteEntry(value, endpoint, ignoreEnabled)
|
|
802
|
-
);
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
function detectAgentTransport(value: unknown): AgentMcpStatusTransport {
|
|
806
|
-
if (!isObject(value)) {
|
|
807
|
-
return "unknown";
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
switch (value["type"]) {
|
|
811
|
-
case "local":
|
|
812
|
-
return "local";
|
|
813
|
-
case "remote":
|
|
814
|
-
return "remote";
|
|
815
|
-
default:
|
|
816
|
-
return "unknown";
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function agentActionForStatus(target: AgentMcpConfigTarget, status: AgentMcpConfigStatus): string {
|
|
821
|
-
switch (status.state) {
|
|
822
|
-
case "missing":
|
|
823
|
-
return target.setupAction;
|
|
824
|
-
case "configured":
|
|
825
|
-
return target.verifyCommand;
|
|
826
|
-
case "disabled":
|
|
827
|
-
case "custom":
|
|
828
|
-
return target.forceAction;
|
|
829
|
-
case "invalid":
|
|
830
|
-
return target.invalidAction;
|
|
831
|
-
default:
|
|
832
|
-
return target.setupAction;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
function makeAgentStatus(
|
|
837
|
-
target: AgentMcpConfigTarget,
|
|
838
|
-
state: AgentMcpConfigState,
|
|
839
|
-
transport: AgentMcpStatusTransport,
|
|
840
|
-
detail: string,
|
|
841
|
-
): AgentMcpConfigStatus {
|
|
842
|
-
const status: AgentMcpConfigStatus = {
|
|
843
|
-
label: target.configLabel,
|
|
844
|
-
path: target.configPath,
|
|
845
|
-
state,
|
|
846
|
-
transport,
|
|
847
|
-
endpoint: target.endpoint,
|
|
848
|
-
mcpName: target.mcpName,
|
|
849
|
-
action: target.setupAction,
|
|
850
|
-
forceAction: target.forceAction,
|
|
851
|
-
verifyCommand: target.verifyCommand,
|
|
852
|
-
detail,
|
|
853
|
-
};
|
|
854
|
-
|
|
855
|
-
return {
|
|
856
|
-
...status,
|
|
857
|
-
action: agentActionForStatus(target, status),
|
|
858
|
-
};
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
function getAgentEntry(config: Record<string, unknown>, mcpName: string): unknown {
|
|
862
|
-
const mcp = config["mcp"];
|
|
863
|
-
if (!isObject(mcp)) {
|
|
864
|
-
return undefined;
|
|
865
|
-
}
|
|
866
|
-
return mcp[mcpName];
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
function buildAgentConfig(
|
|
870
|
-
config: Record<string, unknown>,
|
|
871
|
-
target: AgentMcpConfigTarget,
|
|
872
|
-
): Record<string, unknown> {
|
|
873
|
-
const schema = typeof config["$schema"] === "string" ? config["$schema"] : target.defaultSchema;
|
|
874
|
-
const nextConfig: Record<string, unknown> = {
|
|
875
|
-
$schema: schema,
|
|
876
|
-
};
|
|
877
|
-
|
|
878
|
-
for (const [key, value] of Object.entries(config)) {
|
|
879
|
-
if (key !== "$schema" && key !== "mcp") {
|
|
880
|
-
nextConfig[key] = value;
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
const currentMcp = config["mcp"];
|
|
885
|
-
const nextMcp: Record<string, unknown> = isObject(currentMcp) ? { ...currentMcp } : {};
|
|
886
|
-
nextMcp[target.mcpName] = buildAgentMcpEntry(target.transport, target.endpoint);
|
|
887
|
-
nextConfig["mcp"] = nextMcp;
|
|
888
|
-
return nextConfig;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
function formatAgentConfig(config: Record<string, unknown>): string {
|
|
892
|
-
return `${JSON.stringify(config, null, 2)}\n`;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
function removeAgentEntry(
|
|
896
|
-
config: Record<string, unknown>,
|
|
897
|
-
mcpName: string,
|
|
898
|
-
): Record<string, unknown> {
|
|
899
|
-
const nextConfig: Record<string, unknown> = {};
|
|
900
|
-
for (const [key, value] of Object.entries(config)) {
|
|
901
|
-
if (key !== "mcp") {
|
|
902
|
-
nextConfig[key] = value;
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
const currentMcp = config["mcp"];
|
|
907
|
-
if (!isObject(currentMcp)) {
|
|
908
|
-
return nextConfig;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
const nextMcp: Record<string, unknown> = {};
|
|
912
|
-
for (const [key, value] of Object.entries(currentMcp)) {
|
|
913
|
-
if (key !== mcpName) {
|
|
914
|
-
nextMcp[key] = value;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if (Object.keys(nextMcp).length > 0) {
|
|
919
|
-
nextConfig["mcp"] = nextMcp;
|
|
920
|
-
}
|
|
921
|
-
return nextConfig;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
function readAgentStatus(target: AgentMcpConfigTarget): AgentMcpConfigStatus {
|
|
925
|
-
const read = readAgentConfig(target.configPath, target);
|
|
926
|
-
if (!read.ok) {
|
|
927
|
-
return makeAgentStatus(target, "invalid", "unknown", read.message);
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
const entry = getAgentEntry(read.config, target.mcpName);
|
|
931
|
-
if (entry === undefined) {
|
|
932
|
-
const detail = read.exists
|
|
933
|
-
? `${target.label} config has no ${target.configObjectName}`
|
|
934
|
-
: "Missing file";
|
|
935
|
-
return makeAgentStatus(target, "missing", "unknown", detail);
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
const transport = detectAgentTransport(entry);
|
|
939
|
-
if (isObject(entry) && entry["enabled"] === false) {
|
|
940
|
-
return makeAgentStatus(
|
|
941
|
-
target,
|
|
942
|
-
"disabled",
|
|
943
|
-
transport,
|
|
944
|
-
`${target.configObjectName} is present but disabled`,
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
if (isKnownAgentEntry(entry, target.endpoint, false)) {
|
|
949
|
-
return makeAgentStatus(
|
|
950
|
-
target,
|
|
951
|
-
"configured",
|
|
952
|
-
transport,
|
|
953
|
-
`${target.configObjectName} points at Agent Inspector MCP`,
|
|
954
|
-
);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
return makeAgentStatus(
|
|
958
|
-
target,
|
|
959
|
-
"custom",
|
|
960
|
-
transport,
|
|
961
|
-
`${target.configObjectName} exists but does not match Agent Inspector defaults`,
|
|
962
|
-
);
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
function buildAgentPlan(
|
|
966
|
-
target: AgentMcpConfigTarget,
|
|
967
|
-
uninstall: boolean,
|
|
968
|
-
force: boolean,
|
|
969
|
-
): AgentMcpConfigPlan {
|
|
970
|
-
const read = readAgentConfig(target.configPath, target);
|
|
971
|
-
if (!read.ok) {
|
|
972
|
-
return {
|
|
973
|
-
status: makeAgentStatus(target, "invalid", "unknown", read.message),
|
|
974
|
-
body: null,
|
|
975
|
-
shouldWrite: false,
|
|
976
|
-
shouldRemove: false,
|
|
977
|
-
blockedMessage: `${target.label} config is invalid: ${read.message}`,
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
const status = readAgentStatus(target);
|
|
982
|
-
const entry = getAgentEntry(read.config, target.mcpName);
|
|
983
|
-
const nextBody = formatAgentConfig(buildAgentConfig(read.config, target));
|
|
984
|
-
|
|
985
|
-
if (uninstall) {
|
|
986
|
-
const shouldRemove = entry !== undefined && isKnownAgentEntry(entry, target.endpoint, true);
|
|
987
|
-
const removeBody = shouldRemove
|
|
988
|
-
? formatAgentConfig(removeAgentEntry(read.config, target.mcpName))
|
|
989
|
-
: null;
|
|
990
|
-
return {
|
|
991
|
-
status,
|
|
992
|
-
body: removeBody,
|
|
993
|
-
shouldWrite: false,
|
|
994
|
-
shouldRemove,
|
|
995
|
-
blockedMessage: shouldRemove
|
|
996
|
-
? null
|
|
997
|
-
: `${target.label} ${target.configObjectName} is missing or custom; preserved`,
|
|
998
|
-
};
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
switch (status.state) {
|
|
1002
|
-
case "configured":
|
|
1003
|
-
return {
|
|
1004
|
-
status,
|
|
1005
|
-
body: nextBody,
|
|
1006
|
-
shouldWrite: force,
|
|
1007
|
-
shouldRemove: false,
|
|
1008
|
-
blockedMessage: null,
|
|
1009
|
-
};
|
|
1010
|
-
case "missing":
|
|
1011
|
-
return {
|
|
1012
|
-
status,
|
|
1013
|
-
body: nextBody,
|
|
1014
|
-
shouldWrite: true,
|
|
1015
|
-
shouldRemove: false,
|
|
1016
|
-
blockedMessage: null,
|
|
1017
|
-
};
|
|
1018
|
-
case "custom":
|
|
1019
|
-
case "disabled":
|
|
1020
|
-
return {
|
|
1021
|
-
status,
|
|
1022
|
-
body: nextBody,
|
|
1023
|
-
shouldWrite: force,
|
|
1024
|
-
shouldRemove: false,
|
|
1025
|
-
blockedMessage: force
|
|
1026
|
-
? null
|
|
1027
|
-
: `${target.label} ${target.configObjectName} is custom or disabled; use --force to replace`,
|
|
1028
|
-
};
|
|
1029
|
-
case "invalid":
|
|
1030
|
-
return {
|
|
1031
|
-
status,
|
|
1032
|
-
body: null,
|
|
1033
|
-
shouldWrite: false,
|
|
1034
|
-
shouldRemove: false,
|
|
1035
|
-
blockedMessage: `${target.label} config is invalid`,
|
|
1036
|
-
};
|
|
1037
|
-
default:
|
|
1038
|
-
return {
|
|
1039
|
-
status,
|
|
1040
|
-
body: null,
|
|
1041
|
-
shouldWrite: false,
|
|
1042
|
-
shouldRemove: false,
|
|
1043
|
-
blockedMessage: `${target.label} config state is unknown`,
|
|
1044
|
-
};
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1048
|
-
function buildDetectedSummary(): string {
|
|
1049
|
-
const entries = detectAll();
|
|
1050
|
-
const present = entries.filter((e) => e.result.found);
|
|
1051
|
-
const absent = entries.filter((e) => !e.result.found);
|
|
1052
|
-
const presentList = present.map((e) => e.displayName).join(", ") || "(none)";
|
|
1053
|
-
const absentList = absent.map((e) => e.displayName).join(", ") || "(none)";
|
|
1054
|
-
return ` - Detected: ${presentList}\n - Not detected: ${absentList}`;
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
function readPackageVersion(): string {
|
|
1058
|
-
const packageJsonPaths = [
|
|
1059
|
-
join(__dirname, "..", "package.json"),
|
|
1060
|
-
join(__dirname, "..", "..", "package.json"),
|
|
1061
|
-
];
|
|
1062
|
-
|
|
1063
|
-
try {
|
|
1064
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
1065
|
-
if (existsSync(packageJsonPath)) {
|
|
1066
|
-
const raw: unknown = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
1067
|
-
if (isObject(raw) && typeof raw["version"] === "string") {
|
|
1068
|
-
return raw["version"];
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
}
|
|
1072
|
-
} catch {
|
|
1073
|
-
// best-effort: leave version as the fallback
|
|
1074
|
-
}
|
|
1075
|
-
return "0.0.0";
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
function parseVersion(version: string): ParsedVersion | null {
|
|
1079
|
-
const match = SEMVER_PATTERN.exec(version);
|
|
1080
|
-
if (match === null) {
|
|
1081
|
-
return null;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
const majorText = match[1];
|
|
1085
|
-
const minorText = match[2];
|
|
1086
|
-
const patchText = match[3];
|
|
1087
|
-
if (majorText === undefined || minorText === undefined || patchText === undefined) {
|
|
1088
|
-
return null;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
const major = Number(majorText);
|
|
1092
|
-
const minor = Number(minorText);
|
|
1093
|
-
const patch = Number(patchText);
|
|
1094
|
-
if (!Number.isInteger(major) || !Number.isInteger(minor) || !Number.isInteger(patch)) {
|
|
1095
|
-
return null;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
return { major, minor, patch };
|
|
1099
|
-
}
|
|
1100
|
-
|
|
1101
|
-
function compareVersions(left: string, right: string): number | null {
|
|
1102
|
-
const leftVersion = parseVersion(left);
|
|
1103
|
-
const rightVersion = parseVersion(right);
|
|
1104
|
-
if (leftVersion === null || rightVersion === null) {
|
|
1105
|
-
return null;
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
const leftParts = [leftVersion.major, leftVersion.minor, leftVersion.patch];
|
|
1109
|
-
const rightParts = [rightVersion.major, rightVersion.minor, rightVersion.patch];
|
|
1110
|
-
for (let index = 0; index < leftParts.length; index++) {
|
|
1111
|
-
const leftPart = leftParts[index];
|
|
1112
|
-
const rightPart = rightParts[index];
|
|
1113
|
-
if (leftPart === undefined || rightPart === undefined) {
|
|
1114
|
-
return null;
|
|
1115
|
-
}
|
|
1116
|
-
if (leftPart > rightPart) {
|
|
1117
|
-
return 1;
|
|
1118
|
-
}
|
|
1119
|
-
if (leftPart < rightPart) {
|
|
1120
|
-
return -1;
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
return 0;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
function extractGeneratedVersion(body: string): string | null {
|
|
1128
|
-
const frontmatterMatch = FRONTMATTER_PATTERN.exec(body);
|
|
1129
|
-
if (frontmatterMatch === null) {
|
|
1130
|
-
return null;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
const frontmatter = frontmatterMatch[1];
|
|
1134
|
-
if (frontmatter === undefined) {
|
|
1135
|
-
return null;
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
const versionMatch = GENERATED_VERSION_PATTERN.exec(frontmatter);
|
|
1139
|
-
if (versionMatch === null) {
|
|
1140
|
-
return null;
|
|
1141
|
-
}
|
|
1142
|
-
|
|
1143
|
-
return versionMatch[1] ?? null;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
|
-
function hasAgentInspectorAuthor(body: string): boolean {
|
|
1147
|
-
const frontmatterMatch = FRONTMATTER_PATTERN.exec(body);
|
|
1148
|
-
if (frontmatterMatch === null) {
|
|
1149
|
-
return false;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
const frontmatter = frontmatterMatch[1];
|
|
1153
|
-
if (frontmatter === undefined) {
|
|
1154
|
-
return false;
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
return /^\s*author:\s*agent-inspector\s*$/m.test(frontmatter);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
function readGeneratedVersion(path: string): string | null {
|
|
1161
|
-
try {
|
|
1162
|
-
return extractGeneratedVersion(readFileSync(path, "utf8"));
|
|
1163
|
-
} catch {
|
|
1164
|
-
return null;
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
function isMatchingGeneratedFile(path: string, currentVersion: string): boolean {
|
|
1169
|
-
try {
|
|
1170
|
-
const body = readFileSync(path, "utf8");
|
|
1171
|
-
return hasAgentInspectorAuthor(body) && extractGeneratedVersion(body) === currentVersion;
|
|
1172
|
-
} catch {
|
|
1173
|
-
return false;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
function readGeneratedFileStatus(file: PlannedFile, currentVersion: string): GeneratedFileStatus {
|
|
1178
|
-
if (!existsSync(file.path)) {
|
|
1179
|
-
return {
|
|
1180
|
-
label: file.label,
|
|
1181
|
-
path: file.path,
|
|
1182
|
-
state: "missing",
|
|
1183
|
-
version: null,
|
|
1184
|
-
action: actionForStatus(file.label, "missing"),
|
|
1185
|
-
};
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
try {
|
|
1189
|
-
const body = readFileSync(file.path, "utf8");
|
|
1190
|
-
const version = extractGeneratedVersion(body);
|
|
1191
|
-
if (!hasAgentInspectorAuthor(body) || version === null) {
|
|
1192
|
-
return {
|
|
1193
|
-
label: file.label,
|
|
1194
|
-
path: file.path,
|
|
1195
|
-
state: "custom",
|
|
1196
|
-
version,
|
|
1197
|
-
action: actionForStatus(file.label, "custom"),
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
|
|
1201
|
-
const comparison = compareVersions(version, currentVersion);
|
|
1202
|
-
if (comparison === null) {
|
|
1203
|
-
return {
|
|
1204
|
-
label: file.label,
|
|
1205
|
-
path: file.path,
|
|
1206
|
-
state: "custom",
|
|
1207
|
-
version,
|
|
1208
|
-
action: actionForStatus(file.label, "custom"),
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
if (comparison < 0) {
|
|
1212
|
-
return {
|
|
1213
|
-
label: file.label,
|
|
1214
|
-
path: file.path,
|
|
1215
|
-
state: "outdated",
|
|
1216
|
-
version,
|
|
1217
|
-
action: actionForStatus(file.label, "outdated"),
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
if (comparison > 0) {
|
|
1221
|
-
return {
|
|
1222
|
-
label: file.label,
|
|
1223
|
-
path: file.path,
|
|
1224
|
-
state: "newer",
|
|
1225
|
-
version,
|
|
1226
|
-
action: actionForStatus(file.label, "newer"),
|
|
1227
|
-
};
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
return {
|
|
1231
|
-
label: file.label,
|
|
1232
|
-
path: file.path,
|
|
1233
|
-
state: "current",
|
|
1234
|
-
version,
|
|
1235
|
-
action: actionForStatus(file.label, "current"),
|
|
1236
|
-
};
|
|
1237
|
-
} catch {
|
|
1238
|
-
return {
|
|
1239
|
-
label: file.label,
|
|
1240
|
-
path: file.path,
|
|
1241
|
-
state: "custom",
|
|
1242
|
-
version: null,
|
|
1243
|
-
action: actionForStatus(file.label, "custom"),
|
|
1244
|
-
};
|
|
1245
|
-
}
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
function buildPlannedFiles(
|
|
1249
|
-
flags: OnboardFlags,
|
|
1250
|
-
targets: OnboardTargets,
|
|
1251
|
-
version: string,
|
|
1252
|
-
onboardTarget: OnboardTarget,
|
|
1253
|
-
): PlannedFile[] {
|
|
1254
|
-
const installClaude = !flags.codexOnly && !hasOnlyAgentMcpTarget(flags);
|
|
1255
|
-
const installCodex = !flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags);
|
|
1256
|
-
const detectedSummary = installClaude ? buildDetectedSummary() : "";
|
|
1257
|
-
const targetArguments = onboardSelectionArguments(flags, onboardTarget);
|
|
1258
|
-
const claudeSkillBody = installClaude
|
|
1259
|
-
? renderSkillOnboard({
|
|
1260
|
-
version,
|
|
1261
|
-
port: onboardTarget.port,
|
|
1262
|
-
baseUrl: onboardTarget.baseUrl,
|
|
1263
|
-
controlBaseUrl: onboardTarget.controlBaseUrl,
|
|
1264
|
-
agentBaseUrl: onboardTarget.agentBaseUrl,
|
|
1265
|
-
uiUrl: onboardTarget.uiUrl,
|
|
1266
|
-
mcpName: onboardTarget.mcpName,
|
|
1267
|
-
targetKind: onboardTarget.kind,
|
|
1268
|
-
instanceName: onboardTarget.instanceName,
|
|
1269
|
-
onboardTargetArguments: targetArguments,
|
|
1270
|
-
detectedSummary,
|
|
1271
|
-
})
|
|
1272
|
-
: "";
|
|
1273
|
-
const commandBody = installClaude ? renderCommandOnboard({ version }) : "";
|
|
1274
|
-
const codexSkillBody = installCodex
|
|
1275
|
-
? renderCodexSkillOnboard({
|
|
1276
|
-
version,
|
|
1277
|
-
port: onboardTarget.port,
|
|
1278
|
-
baseUrl: onboardTarget.baseUrl,
|
|
1279
|
-
controlBaseUrl: onboardTarget.controlBaseUrl,
|
|
1280
|
-
agentBaseUrl: onboardTarget.agentBaseUrl,
|
|
1281
|
-
uiUrl: onboardTarget.uiUrl,
|
|
1282
|
-
mcpName: onboardTarget.mcpName,
|
|
1283
|
-
targetKind: onboardTarget.kind,
|
|
1284
|
-
instanceName: onboardTarget.instanceName,
|
|
1285
|
-
onboardTargetArguments: targetArguments,
|
|
1286
|
-
})
|
|
1287
|
-
: "";
|
|
1288
|
-
|
|
1289
|
-
return [
|
|
1290
|
-
{
|
|
1291
|
-
label: "Claude skill",
|
|
1292
|
-
path: targets.claudeSkillFile,
|
|
1293
|
-
body: claudeSkillBody,
|
|
1294
|
-
enabled: installClaude,
|
|
1295
|
-
},
|
|
1296
|
-
{
|
|
1297
|
-
label: "Claude command",
|
|
1298
|
-
path: targets.claudeCommandFile,
|
|
1299
|
-
body: commandBody,
|
|
1300
|
-
enabled: installClaude,
|
|
1301
|
-
},
|
|
1302
|
-
{
|
|
1303
|
-
label: "Codex skill",
|
|
1304
|
-
path: targets.codexSkillFile,
|
|
1305
|
-
body: codexSkillBody,
|
|
1306
|
-
enabled: installCodex,
|
|
1307
|
-
},
|
|
1308
|
-
];
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
function shouldWrite(file: PlannedFile, force: boolean, currentVersion: string): boolean {
|
|
1312
|
-
if (!file.enabled) {
|
|
1313
|
-
return false;
|
|
1314
|
-
}
|
|
1315
|
-
if (force || !existsSync(file.path)) {
|
|
1316
|
-
return true;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
const existingVersion = readGeneratedVersion(file.path);
|
|
1320
|
-
if (existingVersion === null) {
|
|
1321
|
-
return false;
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
const comparison = compareVersions(existingVersion, currentVersion);
|
|
1325
|
-
return comparison !== null && comparison < 0;
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
function shouldUninstall(file: PlannedFile, currentVersion: string): boolean {
|
|
1329
|
-
return (
|
|
1330
|
-
file.enabled && existsSync(file.path) && isMatchingGeneratedFile(file.path, currentVersion)
|
|
1331
|
-
);
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
function removeEmptyDirectory(path: string): void {
|
|
1335
|
-
try {
|
|
1336
|
-
if (readdirSync(path).length === 0) {
|
|
1337
|
-
rmdirSync(path);
|
|
1338
|
-
}
|
|
1339
|
-
} catch {
|
|
1340
|
-
// best-effort cleanup only
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
function runUninstall(
|
|
1345
|
-
plannedFiles: readonly PlannedFile[],
|
|
1346
|
-
currentVersion: string,
|
|
1347
|
-
dryRun: boolean,
|
|
1348
|
-
agentPlans: readonly AgentMcpConfigPlan[],
|
|
1349
|
-
): number {
|
|
1350
|
-
const enabledFiles = plannedFiles.filter((file) => file.enabled);
|
|
1351
|
-
const filesToRemove = enabledFiles.filter((file) => shouldUninstall(file, currentVersion));
|
|
1352
|
-
|
|
1353
|
-
if (dryRun) {
|
|
1354
|
-
process.stdout.write(`agent-inspector onboard --uninstall --dry-run\n\n`);
|
|
1355
|
-
for (const file of enabledFiles) {
|
|
1356
|
-
const status = shouldUninstall(file, currentVersion) ? "matching" : "skipped";
|
|
1357
|
-
process.stdout.write(`${file.label}: ${file.path} (${status})\n`);
|
|
1358
|
-
}
|
|
1359
|
-
for (const plan of agentPlans) {
|
|
1360
|
-
const status = plan.shouldRemove ? "matching" : "skipped";
|
|
1361
|
-
process.stdout.write(`${plan.status.label}: ${plan.status.path} (${status})\n`);
|
|
1362
|
-
if (plan.blockedMessage !== null) {
|
|
1363
|
-
process.stdout.write(` ${plan.blockedMessage}\n`);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
process.stdout.write(`\nNo files were removed.\n`);
|
|
1367
|
-
return 0;
|
|
1368
|
-
}
|
|
1369
|
-
|
|
1370
|
-
const hasAgentConfigToRemove = agentPlans.some((plan) => plan.shouldRemove);
|
|
1371
|
-
if (filesToRemove.length === 0 && !hasAgentConfigToRemove) {
|
|
1372
|
-
process.stdout.write(
|
|
1373
|
-
"agent-inspector onboard: no generated onboarding files match this package version\n",
|
|
1374
|
-
);
|
|
1375
|
-
for (const plan of agentPlans) {
|
|
1376
|
-
if (plan.blockedMessage !== null) {
|
|
1377
|
-
process.stdout.write(`${plan.blockedMessage}\n`);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
return 0;
|
|
1381
|
-
}
|
|
1382
|
-
|
|
1383
|
-
try {
|
|
1384
|
-
for (const file of filesToRemove) {
|
|
1385
|
-
unlinkSync(file.path);
|
|
1386
|
-
removeEmptyDirectory(dirname(file.path));
|
|
1387
|
-
process.stdout.write(`Removed ${file.label}: ${file.path}\n`);
|
|
1388
|
-
}
|
|
1389
|
-
for (const plan of agentPlans) {
|
|
1390
|
-
if (plan.shouldRemove && plan.body !== null) {
|
|
1391
|
-
mkdirSync(dirname(plan.status.path), { recursive: true });
|
|
1392
|
-
writeFileSync(plan.status.path, plan.body, "utf8");
|
|
1393
|
-
process.stdout.write(`Removed ${plan.status.label} entry from: ${plan.status.path}\n`);
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
} catch (err) {
|
|
1397
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1398
|
-
process.stderr.write(`agent-inspector onboard: failed to remove files: ${msg}\n`);
|
|
1399
|
-
return 1;
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
return 0;
|
|
1403
|
-
}
|
|
1404
|
-
|
|
1405
|
-
function formatStatusVersion(status: GeneratedFileStatus): string {
|
|
1406
|
-
return status.version ?? "-";
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
function summarizeStatusAction(
|
|
1410
|
-
statuses: readonly GeneratedFileStatus[],
|
|
1411
|
-
agentStatuses: readonly AgentMcpConfigStatus[],
|
|
1412
|
-
): string {
|
|
1413
|
-
const actionable = statuses.filter(
|
|
1414
|
-
(status) => status.state === "missing" || status.state === "outdated",
|
|
1415
|
-
);
|
|
1416
|
-
const actions = actionable.map((status) => status.action);
|
|
1417
|
-
for (const agentStatus of agentStatuses) {
|
|
1418
|
-
if (agentStatus.state === "missing") {
|
|
1419
|
-
actions.push(agentStatus.action);
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
if (actions.length > 0) {
|
|
1423
|
-
const uniqueActions = Array.from(new Set(actions));
|
|
1424
|
-
return `Run: ${uniqueActions.join(" && ")}`;
|
|
1425
|
-
}
|
|
1426
|
-
for (const agentStatus of agentStatuses) {
|
|
1427
|
-
if (
|
|
1428
|
-
agentStatus.state === "custom" ||
|
|
1429
|
-
agentStatus.state === "disabled" ||
|
|
1430
|
-
agentStatus.state === "invalid"
|
|
1431
|
-
) {
|
|
1432
|
-
return `${agentStatus.label} needs review: ${agentStatus.action}`;
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
if (statuses.some((status) => status.state === "custom")) {
|
|
1436
|
-
return "Custom files are preserved. Use --force only if you want to replace them.";
|
|
1437
|
-
}
|
|
1438
|
-
if (statuses.some((status) => status.state === "newer")) {
|
|
1439
|
-
return "Installed files are newer than this package. No action needed.";
|
|
1440
|
-
}
|
|
1441
|
-
return "All selected onboarding files are current.";
|
|
1442
|
-
}
|
|
1443
|
-
|
|
1444
|
-
function runStatus(
|
|
1445
|
-
plannedFiles: readonly PlannedFile[],
|
|
1446
|
-
currentVersion: string,
|
|
1447
|
-
json: boolean,
|
|
1448
|
-
agentStatuses: readonly AgentMcpConfigStatus[],
|
|
1449
|
-
): number {
|
|
1450
|
-
const enabledFiles = plannedFiles.filter((file) => file.enabled);
|
|
1451
|
-
const statuses = enabledFiles.map((file) => readGeneratedFileStatus(file, currentVersion));
|
|
1452
|
-
const summary = summarizeStatusAction(statuses, agentStatuses);
|
|
1453
|
-
const opencodeConfig =
|
|
1454
|
-
agentStatuses.find((status) => status.label === "OpenCode MCP config") ?? null;
|
|
1455
|
-
const mimoConfig =
|
|
1456
|
-
agentStatuses.find((status) => status.label === "MiMo Code MCP config") ?? null;
|
|
1457
|
-
|
|
1458
|
-
if (json) {
|
|
1459
|
-
process.stdout.write(
|
|
1460
|
-
`${JSON.stringify(
|
|
1461
|
-
{
|
|
1462
|
-
packageVersion: currentVersion,
|
|
1463
|
-
files: statuses,
|
|
1464
|
-
opencodeConfig,
|
|
1465
|
-
mimoConfig,
|
|
1466
|
-
suggestedAction: summary,
|
|
1467
|
-
},
|
|
1468
|
-
null,
|
|
1469
|
-
2,
|
|
1470
|
-
)}\n`,
|
|
1471
|
-
);
|
|
1472
|
-
return 0;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
process.stdout.write(`agent-inspector onboard status\n`);
|
|
1476
|
-
process.stdout.write(`Package version: ${currentVersion}\n\n`);
|
|
1477
|
-
|
|
1478
|
-
for (const status of statuses) {
|
|
1479
|
-
process.stdout.write(`${status.label}: ${status.state}`);
|
|
1480
|
-
process.stdout.write(`, version ${formatStatusVersion(status)}\n`);
|
|
1481
|
-
process.stdout.write(` ${status.path}\n`);
|
|
1482
|
-
process.stdout.write(` Action: ${status.action}\n`);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
for (const agentStatus of agentStatuses) {
|
|
1486
|
-
process.stdout.write(`${agentStatus.label}: ${agentStatus.state}`);
|
|
1487
|
-
process.stdout.write(`, transport ${agentStatus.transport}\n`);
|
|
1488
|
-
process.stdout.write(` ${agentStatus.path}\n`);
|
|
1489
|
-
process.stdout.write(` MCP key: ${agentStatus.mcpName}\n`);
|
|
1490
|
-
process.stdout.write(` Endpoint: ${agentStatus.endpoint}\n`);
|
|
1491
|
-
process.stdout.write(` Detail: ${agentStatus.detail}\n`);
|
|
1492
|
-
process.stdout.write(` Action: ${agentStatus.action}\n`);
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
process.stdout.write(`\nSuggested action: ${summary}\n`);
|
|
1496
|
-
return 0;
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
export function runOnboard(argv: readonly string[]): Promise<number> {
|
|
1500
|
-
try {
|
|
1501
|
-
return Promise.resolve(runOnboardSync(argv));
|
|
1502
|
-
} catch (err) {
|
|
1503
|
-
// Last-resort safety net: report setup failures without blocking normal CLI use.
|
|
1504
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1505
|
-
process.stderr.write(`agent-inspector onboard: ${msg}\n`);
|
|
1506
|
-
process.stderr.write("(run `agent-inspector onboard` again after fixing the issue)\n");
|
|
1507
|
-
return Promise.resolve(0);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1511
|
-
function runOnboardSync(argv: readonly string[]): number {
|
|
1512
|
-
const flags = parseFlags(argv);
|
|
1513
|
-
const resolvedTarget = resolveOnboardTarget({
|
|
1514
|
-
instanceName: flags.instanceName,
|
|
1515
|
-
baseUrl: flags.baseUrl,
|
|
1516
|
-
controlBaseUrl: flags.controlBaseUrl,
|
|
1517
|
-
agentBaseUrl: flags.agentBaseUrl,
|
|
1518
|
-
uiUrl: flags.uiUrl,
|
|
1519
|
-
mcpName: flags.mcpName,
|
|
1520
|
-
});
|
|
1521
|
-
if (!resolvedTarget.ok) {
|
|
1522
|
-
process.stderr.write(`agent-inspector onboard: ${resolvedTarget.message}\n`);
|
|
1523
|
-
return 2;
|
|
1524
|
-
}
|
|
1525
|
-
const onboardTarget = resolvedTarget.target;
|
|
1526
|
-
const targets = resolveTargets(flags);
|
|
1527
|
-
const version = readPackageVersion();
|
|
1528
|
-
const plannedFiles = buildPlannedFiles(flags, targets, version, onboardTarget);
|
|
1529
|
-
const enabledFiles = plannedFiles.filter((file) => file.enabled);
|
|
1530
|
-
const agentPlans: AgentMcpConfigPlan[] = [];
|
|
1531
|
-
if (isOpenCodeEnabled(flags)) {
|
|
1532
|
-
agentPlans.push(
|
|
1533
|
-
buildAgentPlan(openCodeTarget(flags, onboardTarget), flags.uninstall, flags.force),
|
|
1534
|
-
);
|
|
1535
|
-
}
|
|
1536
|
-
if (isMiMoEnabled(flags)) {
|
|
1537
|
-
agentPlans.push(buildAgentPlan(miMoTarget(flags, onboardTarget), flags.uninstall, flags.force));
|
|
1538
|
-
}
|
|
1539
|
-
const agentStatuses = agentPlans.map((plan) => plan.status);
|
|
1540
|
-
|
|
1541
|
-
if (flags.json && !flags.status) {
|
|
1542
|
-
process.stderr.write("agent-inspector onboard: --json is only supported with --status\n");
|
|
1543
|
-
return 2;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
if (enabledFiles.length === 0 && agentPlans.length === 0) {
|
|
1547
|
-
process.stderr.write("agent-inspector onboard: no onboarding target selected\n");
|
|
1548
|
-
return 2;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
if (flags.status) {
|
|
1552
|
-
return runStatus(plannedFiles, version, flags.json, agentStatuses);
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
if (flags.uninstall) {
|
|
1556
|
-
return runUninstall(plannedFiles, version, flags.dryRun, agentPlans);
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
if (flags.dryRun) {
|
|
1560
|
-
process.stdout.write(`agent-inspector onboard --dry-run\n\n`);
|
|
1561
|
-
for (const file of enabledFiles) {
|
|
1562
|
-
const status = existsSync(file.path) ? "exists" : "missing";
|
|
1563
|
-
process.stdout.write(`${file.label}: ${file.path} (${status})\n`);
|
|
1564
|
-
}
|
|
1565
|
-
for (const plan of agentPlans) {
|
|
1566
|
-
process.stdout.write(`${plan.status.label}: ${plan.status.path} (${plan.status.state})\n`);
|
|
1567
|
-
process.stdout.write(` Transport: ${plan.status.transport}\n`);
|
|
1568
|
-
process.stdout.write(` MCP key: ${plan.status.mcpName}\n`);
|
|
1569
|
-
process.stdout.write(` Endpoint: ${plan.status.endpoint}\n`);
|
|
1570
|
-
process.stdout.write(` Action: ${plan.status.action}\n`);
|
|
1571
|
-
if (plan.blockedMessage !== null) {
|
|
1572
|
-
process.stdout.write(` ${plan.blockedMessage}\n`);
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
process.stdout.write(`\nClaude skill headings:\n`);
|
|
1576
|
-
if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
|
|
1577
|
-
for (const heading of REQUIRED_PHASE_HEADINGS) {
|
|
1578
|
-
process.stdout.write(` - ${heading}\n`);
|
|
1579
|
-
}
|
|
1580
|
-
} else {
|
|
1581
|
-
process.stdout.write(` (disabled by selected targets)\n`);
|
|
1582
|
-
}
|
|
1583
|
-
process.stdout.write(`\nCodex skill headings:\n`);
|
|
1584
|
-
if (!flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags)) {
|
|
1585
|
-
for (const heading of REQUIRED_CODEX_PHASE_HEADINGS) {
|
|
1586
|
-
process.stdout.write(` - ${heading}\n`);
|
|
1587
|
-
}
|
|
1588
|
-
} else {
|
|
1589
|
-
process.stdout.write(` (disabled by selected targets)\n`);
|
|
1590
|
-
}
|
|
1591
|
-
if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
|
|
1592
|
-
process.stdout.write(`\nDetected tools:\n${buildDetectedSummary()}\n`);
|
|
1593
|
-
}
|
|
1594
|
-
process.stdout.write(`\nNo files were written.\n`);
|
|
1595
|
-
return 0;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
const filesToWrite = plannedFiles.filter((file) => shouldWrite(file, flags.force, version));
|
|
1599
|
-
const agentPlansToWrite = agentPlans.filter((plan) => plan.shouldWrite && plan.body !== null);
|
|
1600
|
-
for (const plan of agentPlans) {
|
|
1601
|
-
if (plan.status.state === "invalid") {
|
|
1602
|
-
process.stderr.write(`agent-inspector onboard: ${plan.blockedMessage ?? "invalid"}\n`);
|
|
1603
|
-
return 1;
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
if (filesToWrite.length === 0 && agentPlansToWrite.length === 0) {
|
|
1607
|
-
process.stdout.write(
|
|
1608
|
-
"agent-inspector onboard: all selected onboarding files are already up to date\n",
|
|
1609
|
-
);
|
|
1610
|
-
for (const plan of agentPlans) {
|
|
1611
|
-
if (plan.blockedMessage !== null) {
|
|
1612
|
-
process.stdout.write(`${plan.blockedMessage}\n`);
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
process.stdout.write("Re-run with --force to refresh.\n");
|
|
1616
|
-
return 0;
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
try {
|
|
1620
|
-
for (const file of filesToWrite) {
|
|
1621
|
-
mkdirSync(dirname(file.path), { recursive: true });
|
|
1622
|
-
writeFileSync(file.path, file.body, "utf8");
|
|
1623
|
-
}
|
|
1624
|
-
for (const plan of agentPlansToWrite) {
|
|
1625
|
-
if (plan.body !== null) {
|
|
1626
|
-
mkdirSync(dirname(plan.status.path), { recursive: true });
|
|
1627
|
-
writeFileSync(plan.status.path, plan.body, "utf8");
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
} catch (err) {
|
|
1631
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1632
|
-
process.stderr.write(`agent-inspector onboard: failed to write files: ${msg}\n`);
|
|
1633
|
-
return 1;
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
const firstTool = detectFirst();
|
|
1637
|
-
const toolHint = firstTool !== null ? firstTool.displayName : "no known tool detected";
|
|
1638
|
-
|
|
1639
|
-
for (const file of filesToWrite) {
|
|
1640
|
-
process.stdout.write(`Installed/updated ${file.label} to: ${file.path}\n`);
|
|
1641
|
-
}
|
|
1642
|
-
for (const plan of agentPlansToWrite) {
|
|
1643
|
-
process.stdout.write(`Installed/updated ${plan.status.label}: ${plan.status.path}\n`);
|
|
1644
|
-
}
|
|
1645
|
-
process.stdout.write(`\nNext steps:\n`);
|
|
1646
|
-
if (!flags.codexOnly && !hasOnlyAgentMcpTarget(flags)) {
|
|
1647
|
-
process.stdout.write(` - Open Claude Code and run: /agent-inspector:onboard\n`);
|
|
1648
|
-
}
|
|
1649
|
-
if (!flags.skipCodexSkill && !hasOnlyAgentMcpTarget(flags)) {
|
|
1650
|
-
process.stdout.write(` - Open Codex and ask to use the agent-inspector-onboard skill\n`);
|
|
1651
|
-
}
|
|
1652
|
-
for (const plan of agentPlans) {
|
|
1653
|
-
process.stdout.write(` - Verify ${plan.status.label}: ${plan.status.verifyCommand}\n`);
|
|
1654
|
-
}
|
|
1655
|
-
if (hasOnlyAgentMcpTarget(flags)) {
|
|
1656
|
-
const refreshActions = agentPlans.map((plan) => plan.status.forceAction);
|
|
1657
|
-
const uniqueRefreshActions = Array.from(new Set(refreshActions));
|
|
1658
|
-
const refresh =
|
|
1659
|
-
uniqueRefreshActions.length > 0
|
|
1660
|
-
? uniqueRefreshActions.join(" && ")
|
|
1661
|
-
: "agent-inspector onboard --force";
|
|
1662
|
-
process.stdout.write(` - Refresh selected MCP config later: ${refresh}\n`);
|
|
1663
|
-
} else {
|
|
1664
|
-
process.stdout.write(` - Refresh later: agent-inspector onboard --force\n`);
|
|
1665
|
-
process.stdout.write(` - Detected primary tool: ${toolHint}\n`);
|
|
1666
|
-
}
|
|
1667
|
-
return 0;
|
|
1668
|
-
}
|