@vincenthanxiaodu/pi-web 1.202608.1
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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/cli.js +1149 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/apple-touch-icon.png +0 -0
- package/dist/client/assets/CodeViewer-D_Q6ljLV.js +4 -0
- package/dist/client/assets/TerminalPanel-CTGixJIz.js +187 -0
- package/dist/client/assets/index-C2phil_t.js +5637 -0
- package/dist/client/assets/vendor-editor-core-CXO8gGab.js +12 -0
- package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +46 -0
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +1 -0
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +1 -0
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +35 -0
- package/dist/client/favicon.svg +11 -0
- package/dist/client/index.html +124 -0
- package/dist/client/manifest.webmanifest +24 -0
- package/dist/client/pwa-icon-192.png +0 -0
- package/dist/client/pwa-icon-512.png +0 -0
- package/dist/config.js +505 -0
- package/dist/config.js.map +1 -0
- package/dist/docker/piWebDockerCommandPlan.js +212 -0
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -0
- package/dist/environment.js +4 -0
- package/dist/environment.js.map +1 -0
- package/dist/nativeServices/installedServiceDefinitions.js +602 -0
- package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
- package/dist/nativeServices/serviceAction.js +174 -0
- package/dist/nativeServices/serviceAction.js.map +1 -0
- package/dist/nativeServices/serviceDoctor.js +783 -0
- package/dist/nativeServices/serviceDoctor.js.map +1 -0
- package/dist/nativeServices/serviceInstall.js +35 -0
- package/dist/nativeServices/serviceInstall.js.map +1 -0
- package/dist/nativeServices/servicePlan.js +358 -0
- package/dist/nativeServices/servicePlan.js.map +1 -0
- package/dist/nativeServices/serviceProbe.js +447 -0
- package/dist/nativeServices/serviceProbe.js.map +1 -0
- package/dist/nativeServices/serviceRendering.js +132 -0
- package/dist/nativeServices/serviceRendering.js.map +1 -0
- package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
- package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
- package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
- package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
- package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
- package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
- package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
- package/dist/pi-web-plugins/git/git-backend.js +439 -0
- package/dist/pi-web-plugins/git/package.json +16 -0
- package/dist/pi-web-plugins/git/server-plugin.js +176 -0
- package/dist/pi-web-plugins/info/infoInternals.js +222 -0
- package/dist/pi-web-plugins/info/package.json +9 -0
- package/dist/pi-web-plugins/info/pi-web-plugin.js +48 -0
- package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
- package/dist/pi-web-plugins/relays/package.json +9 -0
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
- package/dist/pi-web-plugins/relays/relayDiscovery.js +228 -0
- package/dist/pi-web-plugins/relays/relaysPanelElement.js +550 -0
- package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
- package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
- package/dist/pi-web-plugins/updates/package.json +9 -0
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +185 -0
- package/dist/pi-web-plugins/updates/updatesLogic.js +97 -0
- package/dist/pi-web-plugins/workspace-tasks/config.js +91 -0
- package/dist/pi-web-plugins/workspace-tasks/package.json +9 -0
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +48 -0
- package/dist/pi-web-plugins/workspace-tasks/taskRunner.js +12 -0
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +288 -0
- package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +47 -0
- package/dist/piWebVersionReport.js +278 -0
- package/dist/piWebVersionReport.js.map +1 -0
- package/dist/plugin-api.d.ts +222 -0
- package/dist/pluginRecoveryCli.js +172 -0
- package/dist/pluginRecoveryCli.js.map +1 -0
- package/dist/server/activeAgentProfileProvider.js +25 -0
- package/dist/server/activeAgentProfileProvider.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +106 -0
- package/dist/server/activity/workspaceActivityService.js.map +1 -0
- package/dist/server/app.js +218 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/browserMessageProjection.js +54 -0
- package/dist/server/browserMessageProjection.js.map +1 -0
- package/dist/server/configRoutes.js +293 -0
- package/dist/server/configRoutes.js.map +1 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
- package/dist/server/diagnostics/nodePtySpawnHelper.js +135 -0
- package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -0
- package/dist/server/git/gitEnv.js +15 -0
- package/dist/server/git/gitEnv.js.map +1 -0
- package/dist/server/goals/goalArchive.js +184 -0
- package/dist/server/goals/goalArchive.js.map +1 -0
- package/dist/server/goals/goalFile.js +200 -0
- package/dist/server/goals/goalFile.js.map +1 -0
- package/dist/server/goals/goalStore.js +65 -0
- package/dist/server/goals/goalStore.js.map +1 -0
- package/dist/server/index.js +7 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/machines/machineClient.js +191 -0
- package/dist/server/machines/machineClient.js.map +1 -0
- package/dist/server/machines/machinePluginProxyRoutes.js +247 -0
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineProxyRoutes.js +318 -0
- package/dist/server/machines/machineProxyRoutes.js.map +1 -0
- package/dist/server/machines/machineRoutes.js +56 -0
- package/dist/server/machines/machineRoutes.js.map +1 -0
- package/dist/server/machines/machineService.js +274 -0
- package/dist/server/machines/machineService.js.map +1 -0
- package/dist/server/machines/machineStore.js +143 -0
- package/dist/server/machines/machineStore.js.map +1 -0
- package/dist/server/piPackageRoutes.js +91 -0
- package/dist/server/piPackageRoutes.js.map +1 -0
- package/dist/server/piPackageService.js +101 -0
- package/dist/server/piPackageService.js.map +1 -0
- package/dist/server/piWebPluginCatalog.js +584 -0
- package/dist/server/piWebPluginCatalog.js.map +1 -0
- package/dist/server/piWebPluginLifecycle.js +162 -0
- package/dist/server/piWebPluginLifecycle.js.map +1 -0
- package/dist/server/piWebPluginService.js +224 -0
- package/dist/server/piWebPluginService.js.map +1 -0
- package/dist/server/piWebReleaseLookupCache.js +37 -0
- package/dist/server/piWebReleaseLookupCache.js.map +1 -0
- package/dist/server/piWebStatus.js +624 -0
- package/dist/server/piWebStatus.js.map +1 -0
- package/dist/server/piWebStatusCache.js +45 -0
- package/dist/server/piWebStatusCache.js.map +1 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
- package/dist/server/plugins/serverPluginExec.js +184 -0
- package/dist/server/plugins/serverPluginExec.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +480 -0
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
- package/dist/server/projectTrustRoutes.js +80 -0
- package/dist/server/projectTrustRoutes.js.map +1 -0
- package/dist/server/projects/directorySuggestions.js +207 -0
- package/dist/server/projects/directorySuggestions.js.map +1 -0
- package/dist/server/projects/projectService.js +33 -0
- package/dist/server/projects/projectService.js.map +1 -0
- package/dist/server/realtime/sessionEventHub.js +93 -0
- package/dist/server/realtime/sessionEventHub.js.map +1 -0
- package/dist/server/requestCancellation.js +32 -0
- package/dist/server/requestCancellation.js.map +1 -0
- package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
- package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js +31 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +61 -0
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionServiceDependencies.js +34 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
- package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
- package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
- package/dist/server/sessiond.js +387 -0
- package/dist/server/sessiond.js.map +1 -0
- package/dist/server/sessions/askUserTool.js +102 -0
- package/dist/server/sessions/askUserTool.js.map +1 -0
- package/dist/server/sessions/attachmentService.js +119 -0
- package/dist/server/sessions/attachmentService.js.map +1 -0
- package/dist/server/sessions/authProviderOptions.js +50 -0
- package/dist/server/sessions/authProviderOptions.js.map +1 -0
- package/dist/server/sessions/authRoutes.js +59 -0
- package/dist/server/sessions/authRoutes.js.map +1 -0
- package/dist/server/sessions/authService.js +122 -0
- package/dist/server/sessions/authService.js.map +1 -0
- package/dist/server/sessions/builtinCommands.js +27 -0
- package/dist/server/sessions/builtinCommands.js.map +1 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/editPreview.js +196 -0
- package/dist/server/sessions/editPreview.js.map +1 -0
- package/dist/server/sessions/extensionDialogWaiters.js +76 -0
- package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +167 -0
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -0
- package/dist/server/sessions/interruptedRunStore.js +147 -0
- package/dist/server/sessions/interruptedRunStore.js.map +1 -0
- package/dist/server/sessions/messagePaging.js +43 -0
- package/dist/server/sessions/messagePaging.js.map +1 -0
- package/dist/server/sessions/modelCatalogRefresher.js +193 -0
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -0
- package/dist/server/sessions/oauthLoginFlowService.js +346 -0
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -0
- package/dist/server/sessions/pendingAskStore.js +275 -0
- package/dist/server/sessions/pendingAskStore.js.map +1 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
- package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +246 -0
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +4059 -0
- package/dist/server/sessions/piSessionService.js.map +1 -0
- package/dist/server/sessions/plainTextTheme.js +112 -0
- package/dist/server/sessions/plainTextTheme.js.map +1 -0
- package/dist/server/sessions/sessionArchiveStore.js +273 -0
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -0
- package/dist/server/sessions/sessionArchiveTree.js +35 -0
- package/dist/server/sessions/sessionArchiveTree.js.map +1 -0
- package/dist/server/sessions/sessionCleanup.js +157 -0
- package/dist/server/sessions/sessionCleanup.js.map +1 -0
- package/dist/server/sessions/sessionCommandService.js +336 -0
- package/dist/server/sessions/sessionCommandService.js.map +1 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/sessionFileFormat.js +27 -0
- package/dist/server/sessions/sessionFileFormat.js.map +1 -0
- package/dist/server/sessions/sessionFileHeader.js +104 -0
- package/dist/server/sessions/sessionFileHeader.js.map +1 -0
- package/dist/server/sessions/sessionModelScope.js +141 -0
- package/dist/server/sessions/sessionModelScope.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +90 -0
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -0
- package/dist/server/sessions/sessionNotificationStore.js +484 -0
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +744 -0
- package/dist/server/sessions/sessionRoutes.js.map +1 -0
- package/dist/server/sessions/sessionRuntimeStore.js +2 -0
- package/dist/server/sessions/sessionRuntimeStore.js.map +1 -0
- package/dist/server/sessions/sessionService.js +2 -0
- package/dist/server/sessions/sessionService.js.map +1 -0
- package/dist/server/sessions/sessionSummaryScanner.js +491 -0
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
- package/dist/server/sessions/sessionTreeProjection.js +250 -0
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
- package/dist/server/sessions/sessionUnreadStore.js +520 -0
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
- package/dist/server/sessions/shutdownDrain.js +73 -0
- package/dist/server/sessions/shutdownDrain.js.map +1 -0
- package/dist/server/sessions/spawnSessionTool.js +48 -0
- package/dist/server/sessions/spawnSessionTool.js.map +1 -0
- package/dist/server/sessions/spawnSubsessionTool.js +216 -0
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -0
- package/dist/server/sessions/spawnTargetResolver.js +23 -0
- package/dist/server/sessions/spawnTargetResolver.js.map +1 -0
- package/dist/server/sessions/subsessionTranscript.js +235 -0
- package/dist/server/sessions/subsessionTranscript.js.map +1 -0
- package/dist/server/status/machineStatusRoutes.js +8 -0
- package/dist/server/status/machineStatusRoutes.js.map +1 -0
- package/dist/server/status/machineStatusService.js +181 -0
- package/dist/server/status/machineStatusService.js.map +1 -0
- package/dist/server/status/workspaceAttribution.js +87 -0
- package/dist/server/status/workspaceAttribution.js.map +1 -0
- package/dist/server/storage/projectStore.js +88 -0
- package/dist/server/storage/projectStore.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js +141 -0
- package/dist/server/terminalProxyRoutes.js.map +1 -0
- package/dist/server/terminals/terminalRoutes.js +155 -0
- package/dist/server/terminals/terminalRoutes.js.map +1 -0
- package/dist/server/terminals/terminalService.js +305 -0
- package/dist/server/terminals/terminalService.js.map +1 -0
- package/dist/server/terminals/terminalSize.js +17 -0
- package/dist/server/terminals/terminalSize.js.map +1 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/updates/fleetRoutes.js +111 -0
- package/dist/server/updates/fleetRoutes.js.map +1 -0
- package/dist/server/updates/restartRoutes.js +48 -0
- package/dist/server/updates/restartRoutes.js.map +1 -0
- package/dist/server/updates/selfUpdateRoutes.js +145 -0
- package/dist/server/updates/selfUpdateRoutes.js.map +1 -0
- package/dist/server/webSocketBridge.js +32 -0
- package/dist/server/webSocketBridge.js.map +1 -0
- package/dist/server/workingDirectory.js +44 -0
- package/dist/server/workingDirectory.js.map +1 -0
- package/dist/server/workspaceExplorerRoutes.js +147 -0
- package/dist/server/workspaceExplorerRoutes.js.map +1 -0
- package/dist/server/workspaces/effectivePathAccess.js +8 -0
- package/dist/server/workspaces/effectivePathAccess.js.map +1 -0
- package/dist/server/workspaces/fileContentService.js +190 -0
- package/dist/server/workspaces/fileContentService.js.map +1 -0
- package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
- package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
- package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
- package/dist/server/workspaces/filePreviewService.js +81 -0
- package/dist/server/workspaces/filePreviewService.js.map +1 -0
- package/dist/server/workspaces/fileSuggestions.js +520 -0
- package/dist/server/workspaces/fileSuggestions.js.map +1 -0
- package/dist/server/workspaces/fileTreeService.js +35 -0
- package/dist/server/workspaces/fileTreeService.js.map +1 -0
- package/dist/server/workspaces/pathAccessPolicy.js +90 -0
- package/dist/server/workspaces/pathAccessPolicy.js.map +1 -0
- package/dist/server/workspaces/pathSafety.js +45 -0
- package/dist/server/workspaces/pathSafety.js.map +1 -0
- package/dist/server/workspaces/projectPiWebConfig.js +61 -0
- package/dist/server/workspaces/projectPiWebConfig.js.map +1 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js +17 -0
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceCatalog.js +47 -0
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
- package/dist/server/workspaces/workspaceContext.js +6 -0
- package/dist/server/workspaces/workspaceContext.js.map +1 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js +49 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
- package/dist/server/workspaces/workspaceRemovalService.js +256 -0
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
- package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
- package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
- package/dist/server-plugin-api.d.ts +140 -0
- package/dist/server-plugin-api.js +2 -0
- package/dist/server-plugin-api.js.map +1 -0
- package/dist/serverPluginRecovery.js +138 -0
- package/dist/serverPluginRecovery.js.map +1 -0
- package/dist/sessiond/activeAgentProfile.js +12 -0
- package/dist/sessiond/activeAgentProfile.js.map +1 -0
- package/dist/sessiond/config.js +20 -0
- package/dist/sessiond/config.js.map +1 -0
- package/dist/sessiond/sessionDaemonClient.js +105 -0
- package/dist/sessiond/sessionDaemonClient.js.map +1 -0
- package/dist/shared/activeAgentProfile.js +37 -0
- package/dist/shared/activeAgentProfile.js.map +1 -0
- package/dist/shared/activity.js +16 -0
- package/dist/shared/activity.js.map +1 -0
- package/dist/shared/apiTypes.js +41 -0
- package/dist/shared/apiTypes.js.map +1 -0
- package/dist/shared/capabilities.js +37 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/federatedRoutes.js +127 -0
- package/dist/shared/federatedRoutes.js.map +1 -0
- package/dist/shared/machinePluginIds.js +43 -0
- package/dist/shared/machinePluginIds.js.map +1 -0
- package/dist/shared/machineStatus.js +94 -0
- package/dist/shared/machineStatus.js.map +1 -0
- package/dist/shared/piWebStatusParsing.js +141 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -0
- package/dist/shared/pluginApiTypes.d.ts +150 -0
- package/dist/shared/pluginApiTypes.js +3 -0
- package/dist/shared/pluginApiTypes.js.map +1 -0
- package/dist/shared/pluginBackendProtocol.js +110 -0
- package/dist/shared/pluginBackendProtocol.js.map +1 -0
- package/dist/shared/pluginIds.js +10 -0
- package/dist/shared/pluginIds.js.map +1 -0
- package/dist/shared/pluginRecoveryCommands.js +14 -0
- package/dist/shared/pluginRecoveryCommands.js.map +1 -0
- package/dist/shared/promptAttachments.js +92 -0
- package/dist/shared/promptAttachments.js.map +1 -0
- package/dist/shared/selfUpdate.js +3 -0
- package/dist/shared/selfUpdate.js.map +1 -0
- package/dist/shared/thinkingLevels.js +31 -0
- package/dist/shared/thinkingLevels.js.map +1 -0
- package/dist/shared/workspaceDeletion.js +12 -0
- package/dist/shared/workspaceDeletion.js.map +1 -0
- package/dist/shared/workspaceFiles.js +42 -0
- package/dist/shared/workspaceFiles.js.map +1 -0
- package/dist/shared/workspaceRemovalProtocol.js +24 -0
- package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
- package/docs/assets/favicon.svg +11 -0
- package/docs/assets/pi-web-banner.png +0 -0
- package/docs/assets/pi-web-desktop.png +0 -0
- package/docs/assets/pi-web-mobile.png +0 -0
- package/docs/assets/pi-web-tablet.png +0 -0
- package/docs/config.md +444 -0
- package/docs/plugins.md +1305 -0
- package/examples/workspace-provider-plugin/README.md +52 -0
- package/examples/workspace-provider-plugin/package.json +25 -0
- package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
- package/examples/workspace-provider-plugin/src/server.ts +64 -0
- package/examples/workspace-provider-plugin/tsconfig.json +21 -0
- package/extensions/README-pi-web-restart-migration.md +45 -0
- package/extensions/pi-web.ts +324 -0
- package/install.sh +5 -0
- package/package.json +173 -0
- package/plugin-api.d.ts +1 -0
- package/server-plugin-api.d.ts +1 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { ASK_USER_ID_MAX_LENGTH, ASK_USER_OPTION_LIMIT, ASK_USER_OTHER_TEXT_MAX_LENGTH, ASK_USER_QUESTION_LIMIT, ASK_USER_TEXT_MAX_LENGTH, } from "../../shared/apiTypes.js";
|
|
3
|
+
/** Rejected input: a question set is malformed, or an answer does not fit its question. */
|
|
4
|
+
export class PendingAskValidationError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "PendingAskValidationError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Daemon-owned open-ask state: one unanswered question set per session.
|
|
12
|
+
*
|
|
13
|
+
* The store is pure domain logic — no Fastify, no Pi session, no I/O, no timers.
|
|
14
|
+
* It validates asks and answers, owns the open/supersede/submit/cancel
|
|
15
|
+
* transitions, and computes the answered-versus-unanswered outcome that both the
|
|
16
|
+
* model-facing message and the browser record are rendered from. Callers publish
|
|
17
|
+
* the returned asks and outcomes; the store never emits anything itself.
|
|
18
|
+
*
|
|
19
|
+
* State is deliberately daemon-lifetime and in-memory. An open ask is meaningful
|
|
20
|
+
* only while the session runtime that posted it exists, and browsers rehydrate it
|
|
21
|
+
* from `SessionStatus` rather than from disk.
|
|
22
|
+
*/
|
|
23
|
+
export class PendingAskStore {
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.openBySessionId = new Map();
|
|
26
|
+
this.now = options.now ?? (() => new Date());
|
|
27
|
+
this.createAskId = options.createAskId ?? randomUUID;
|
|
28
|
+
}
|
|
29
|
+
/** The session's open ask, for {@link SessionStatus} projection. */
|
|
30
|
+
pendingAsk(sessionId) {
|
|
31
|
+
const ask = this.openBySessionId.get(requireSessionId(sessionId));
|
|
32
|
+
return ask === undefined ? undefined : cloneAsk(ask);
|
|
33
|
+
}
|
|
34
|
+
open(input) {
|
|
35
|
+
const sessionId = requireSessionId(input.sessionId);
|
|
36
|
+
const questions = validateQuestions(input.questions);
|
|
37
|
+
const askedAt = this.timestamp();
|
|
38
|
+
const superseded = this.close(sessionId, "superseded", askedAt, new Map());
|
|
39
|
+
const ask = {
|
|
40
|
+
askId: requireId(this.createAskId(), "askId"),
|
|
41
|
+
askedAt,
|
|
42
|
+
questions,
|
|
43
|
+
};
|
|
44
|
+
this.openBySessionId.set(sessionId, ask);
|
|
45
|
+
return {
|
|
46
|
+
ask: cloneAsk(ask),
|
|
47
|
+
...(superseded === undefined ? {} : { superseded }),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record what the user replied and close the ask. Answers are validated against
|
|
52
|
+
* the open ask, so a submission that does not fit its questions is rejected
|
|
53
|
+
* rather than silently truncated; the ask stays open in that case.
|
|
54
|
+
*/
|
|
55
|
+
submit(sessionId, askId, submission) {
|
|
56
|
+
const ask = this.openBySessionId.get(requireSessionId(sessionId));
|
|
57
|
+
if (ask?.askId !== askId)
|
|
58
|
+
return { status: "stale" };
|
|
59
|
+
// Validate before closing so a submission that does not fit its questions
|
|
60
|
+
// leaves the ask open for the browser to correct.
|
|
61
|
+
const answers = validateSubmission(ask, submission);
|
|
62
|
+
return { status: "closed", outcome: this.requireClose(sessionId, "submitted", answers) };
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Close the session's open ask without a submission. The outcome reports every question as
|
|
66
|
+
* unanswered, because answers only ever reach the daemon through a submit.
|
|
67
|
+
*/
|
|
68
|
+
cancel(sessionId, askId) {
|
|
69
|
+
const ask = this.openBySessionId.get(requireSessionId(sessionId));
|
|
70
|
+
if (ask?.askId !== askId)
|
|
71
|
+
return { status: "stale" };
|
|
72
|
+
return { status: "closed", outcome: this.requireClose(sessionId, "cancelled", new Map()) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Close whatever ask the session currently has open, e.g. because the user sent
|
|
76
|
+
* an ordinary chat message instead of answering the form. Returns the outcome,
|
|
77
|
+
* or `undefined` when the session has no open ask.
|
|
78
|
+
*/
|
|
79
|
+
cancelOpen(sessionId) {
|
|
80
|
+
return this.close(requireSessionId(sessionId), "cancelled", this.timestamp(), new Map());
|
|
81
|
+
}
|
|
82
|
+
/** Drop the open ask of a session that is going away, without reporting an outcome. */
|
|
83
|
+
forgetSession(sessionId) {
|
|
84
|
+
this.openBySessionId.delete(requireSessionId(sessionId));
|
|
85
|
+
}
|
|
86
|
+
requireClose(sessionId, reason, answers) {
|
|
87
|
+
const outcome = this.close(sessionId, reason, this.timestamp(), answers);
|
|
88
|
+
if (outcome === undefined)
|
|
89
|
+
throw new Error(`Pending ask of session ${sessionId} disappeared while closing`);
|
|
90
|
+
return outcome;
|
|
91
|
+
}
|
|
92
|
+
close(sessionId, reason, closedAt, answers) {
|
|
93
|
+
const ask = this.openBySessionId.get(sessionId);
|
|
94
|
+
if (ask === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
this.openBySessionId.delete(sessionId);
|
|
97
|
+
return askUserOutcome(ask, answers, reason, closedAt);
|
|
98
|
+
}
|
|
99
|
+
timestamp() {
|
|
100
|
+
return this.now().toISOString();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Model-facing text of a closed ask. The model reads this, so it must name the
|
|
105
|
+
* unanswered questions as plainly as the answered ones.
|
|
106
|
+
*/
|
|
107
|
+
export function renderAskUserAnswersText(outcome) {
|
|
108
|
+
const lead = outcome.reason === "submitted"
|
|
109
|
+
? "The user submitted answers to your questions."
|
|
110
|
+
: `The question set was closed (${outcome.reason}) before it was fully answered.`;
|
|
111
|
+
return [lead, "", ...outcome.questions.map(questionLines).flat(), "", outcome.summary].join("\n");
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Notice for the model when a new ask replaced one the user never answered, so a
|
|
115
|
+
* supersede is never a silent loss of the earlier questions.
|
|
116
|
+
*/
|
|
117
|
+
export function renderSupersededAskText(outcome) {
|
|
118
|
+
return [
|
|
119
|
+
`This replaced an earlier question set (${outcome.askId}) that the user never submitted.`,
|
|
120
|
+
`Left unanswered: ${outcome.unansweredIds.join(", ")}.`,
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
function questionLines(record) {
|
|
124
|
+
const header = `- ${record.question.id}: ${record.question.question}`;
|
|
125
|
+
if (!record.answered)
|
|
126
|
+
return [header, " Unanswered."];
|
|
127
|
+
const parts = [...record.values.map((value) => `selected ${value}`)];
|
|
128
|
+
if (record.otherText !== undefined)
|
|
129
|
+
parts.push(`custom: ${JSON.stringify(record.otherText)}`);
|
|
130
|
+
return [header, ` Answered: ${parts.join("; ")}`];
|
|
131
|
+
}
|
|
132
|
+
function askUserOutcome(ask, answers, reason, closedAt) {
|
|
133
|
+
const questions = ask.questions.map((question) => questionRecord(question, answers.get(question.id)));
|
|
134
|
+
const unansweredIds = questions.filter((record) => !record.answered).map((record) => record.question.id);
|
|
135
|
+
const answeredCount = questions.length - unansweredIds.length;
|
|
136
|
+
return {
|
|
137
|
+
askId: ask.askId,
|
|
138
|
+
reason,
|
|
139
|
+
askedAt: ask.askedAt,
|
|
140
|
+
closedAt,
|
|
141
|
+
questions,
|
|
142
|
+
answeredCount,
|
|
143
|
+
unansweredIds,
|
|
144
|
+
summary: summaryLine(questions.length, answeredCount, unansweredIds),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function questionRecord(question, answer) {
|
|
148
|
+
const values = answer?.values ?? [];
|
|
149
|
+
const otherText = answer?.otherText;
|
|
150
|
+
return {
|
|
151
|
+
question: cloneQuestion(question),
|
|
152
|
+
answered: values.length > 0 || otherText !== undefined,
|
|
153
|
+
values: [...values],
|
|
154
|
+
...(otherText === undefined ? {} : { otherText }),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function summaryLine(total, answeredCount, unansweredIds) {
|
|
158
|
+
const answered = `Answered ${answeredCount.toString()} of ${total.toString()}`;
|
|
159
|
+
return unansweredIds.length === 0 ? `${answered}; none left unanswered` : `${answered}; unanswered: ${unansweredIds.join(", ")}`;
|
|
160
|
+
}
|
|
161
|
+
function validateQuestions(questions) {
|
|
162
|
+
if (questions.length === 0)
|
|
163
|
+
throw new PendingAskValidationError("An ask must contain at least one question");
|
|
164
|
+
if (questions.length > ASK_USER_QUESTION_LIMIT) {
|
|
165
|
+
throw new PendingAskValidationError(`An ask must not contain more than ${ASK_USER_QUESTION_LIMIT.toString()} questions`);
|
|
166
|
+
}
|
|
167
|
+
const seenIds = new Set();
|
|
168
|
+
return questions.map((question) => {
|
|
169
|
+
const id = requireId(question.id, "question id");
|
|
170
|
+
if (seenIds.has(id))
|
|
171
|
+
throw new PendingAskValidationError(`Duplicate question id ${id}`);
|
|
172
|
+
seenIds.add(id);
|
|
173
|
+
return validateQuestion(question, id);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function validateQuestion(question, id) {
|
|
177
|
+
if (question.options.length > ASK_USER_OPTION_LIMIT) {
|
|
178
|
+
throw new PendingAskValidationError(`Question ${id} must not offer more than ${ASK_USER_OPTION_LIMIT.toString()} options`);
|
|
179
|
+
}
|
|
180
|
+
const seenValues = new Set();
|
|
181
|
+
const options = question.options.map((option) => {
|
|
182
|
+
const value = requireId(option.value, `option value of question ${id}`);
|
|
183
|
+
if (seenValues.has(value))
|
|
184
|
+
throw new PendingAskValidationError(`Duplicate option value ${value} in question ${id}`);
|
|
185
|
+
seenValues.add(value);
|
|
186
|
+
return validateOption(option, value, id);
|
|
187
|
+
});
|
|
188
|
+
const detail = question.detail;
|
|
189
|
+
return {
|
|
190
|
+
id,
|
|
191
|
+
question: requireText(question.question, `text of question ${id}`),
|
|
192
|
+
...(detail === undefined ? {} : { detail: requireText(detail, `detail of question ${id}`) }),
|
|
193
|
+
options,
|
|
194
|
+
...(question.multiple === true ? { multiple: true } : {}),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function validateOption(option, value, questionId) {
|
|
198
|
+
const detail = option.detail;
|
|
199
|
+
return {
|
|
200
|
+
value,
|
|
201
|
+
label: requireText(option.label, `label of option ${value} in question ${questionId}`),
|
|
202
|
+
...(detail === undefined ? {} : { detail: requireText(detail, `detail of option ${value} in question ${questionId}`) }),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function validateSubmission(ask, submission) {
|
|
206
|
+
const questionsById = new Map(ask.questions.map((question) => [question.id, question]));
|
|
207
|
+
const answers = new Map();
|
|
208
|
+
for (const answer of submission.answers) {
|
|
209
|
+
const question = questionsById.get(answer.id);
|
|
210
|
+
if (question === undefined)
|
|
211
|
+
throw new PendingAskValidationError(`Unknown question id ${answer.id}`);
|
|
212
|
+
if (answers.has(answer.id))
|
|
213
|
+
throw new PendingAskValidationError(`Duplicate answer for question ${answer.id}`);
|
|
214
|
+
const validated = validateAnswer(question, answer);
|
|
215
|
+
// Untouched questions and explicitly empty answers are the same thing, so an
|
|
216
|
+
// empty answer is dropped rather than recorded as answered.
|
|
217
|
+
if (validated !== undefined)
|
|
218
|
+
answers.set(answer.id, validated);
|
|
219
|
+
}
|
|
220
|
+
return answers;
|
|
221
|
+
}
|
|
222
|
+
function validateAnswer(question, answer) {
|
|
223
|
+
const optionValues = new Set(question.options.map((option) => option.value));
|
|
224
|
+
const values = [];
|
|
225
|
+
for (const value of answer.values) {
|
|
226
|
+
if (!optionValues.has(value))
|
|
227
|
+
throw new PendingAskValidationError(`Question ${question.id} has no option ${value}`);
|
|
228
|
+
if (values.includes(value))
|
|
229
|
+
throw new PendingAskValidationError(`Duplicate value ${value} for question ${question.id}`);
|
|
230
|
+
values.push(value);
|
|
231
|
+
}
|
|
232
|
+
const otherText = normalizeOtherText(question, answer.otherText);
|
|
233
|
+
const selectionCount = values.length + (otherText === undefined ? 0 : 1);
|
|
234
|
+
if (question.multiple !== true && selectionCount > 1) {
|
|
235
|
+
throw new PendingAskValidationError(`Question ${question.id} accepts a single answer`);
|
|
236
|
+
}
|
|
237
|
+
if (selectionCount === 0)
|
|
238
|
+
return undefined;
|
|
239
|
+
return { id: question.id, values, ...(otherText === undefined ? {} : { otherText }) };
|
|
240
|
+
}
|
|
241
|
+
function normalizeOtherText(question, otherText) {
|
|
242
|
+
if (otherText === undefined)
|
|
243
|
+
return undefined;
|
|
244
|
+
if (otherText.length > ASK_USER_OTHER_TEXT_MAX_LENGTH) {
|
|
245
|
+
throw new PendingAskValidationError(`Other text of question ${question.id} exceeds its length limit`);
|
|
246
|
+
}
|
|
247
|
+
const trimmed = otherText.trim();
|
|
248
|
+
return trimmed === "" ? undefined : trimmed;
|
|
249
|
+
}
|
|
250
|
+
function cloneAsk(ask) {
|
|
251
|
+
return { askId: ask.askId, askedAt: ask.askedAt, questions: ask.questions.map(cloneQuestion) };
|
|
252
|
+
}
|
|
253
|
+
function cloneQuestion(question) {
|
|
254
|
+
return { ...question, options: question.options.map((option) => ({ ...option })) };
|
|
255
|
+
}
|
|
256
|
+
function requireSessionId(sessionId) {
|
|
257
|
+
if (sessionId === "")
|
|
258
|
+
throw new Error("sessionId must not be empty");
|
|
259
|
+
return sessionId;
|
|
260
|
+
}
|
|
261
|
+
function requireId(value, field) {
|
|
262
|
+
if (value.trim() === "")
|
|
263
|
+
throw new PendingAskValidationError(`${field} must not be empty`);
|
|
264
|
+
if (value.length > ASK_USER_ID_MAX_LENGTH)
|
|
265
|
+
throw new PendingAskValidationError(`${field} exceeds its length limit`);
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
function requireText(value, field) {
|
|
269
|
+
if (value.trim() === "")
|
|
270
|
+
throw new PendingAskValidationError(`${field} must not be empty`);
|
|
271
|
+
if (value.length > ASK_USER_TEXT_MAX_LENGTH)
|
|
272
|
+
throw new PendingAskValidationError(`${field} exceeds its length limit`);
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=pendingAskStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pendingAskStore.js","sourceRoot":"","sources":["../../../src/server/sessions/pendingAskStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { EXTENSION_DIALOG_ID_MAX_LENGTH, EXTENSION_DIALOG_INPUT_MAX_LENGTH, EXTENSION_DIALOG_OPTION_LIMIT, EXTENSION_DIALOG_TEXT_MAX_LENGTH, } from "../../shared/apiTypes.js";
|
|
3
|
+
/** Rejected input: the dialog is malformed, or an answer does not fit its kind. */
|
|
4
|
+
export class PendingExtensionDialogValidationError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "PendingExtensionDialogValidationError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Daemon-owned open-dialog state: the extension dialogs of every session,
|
|
12
|
+
* several per session because each dialog is an independent blocking wait
|
|
13
|
+
* inside extension code — opening one must never supersede another.
|
|
14
|
+
*
|
|
15
|
+
* The store is pure domain logic — no Fastify, no Pi session, no I/O, no
|
|
16
|
+
* timers. It validates dialogs and answers and owns the open/answer/cancel
|
|
17
|
+
* transitions; callers hold the waiting Promise resolvers, publish the
|
|
18
|
+
* returned records and outcomes, and own the timers that turn `timeoutAt`
|
|
19
|
+
* into a `"timeout"` cancel.
|
|
20
|
+
*
|
|
21
|
+
* State is deliberately daemon-lifetime and in-memory. An open dialog is
|
|
22
|
+
* meaningful only while the session runtime whose extension is waiting on it
|
|
23
|
+
* exists, and browsers rehydrate open dialogs from `SessionStatus` rather
|
|
24
|
+
* than from disk.
|
|
25
|
+
*/
|
|
26
|
+
export class PendingExtensionDialogStore {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
/** Per-session open dialogs in insertion order, so `pendingDialogs` reads oldest first. */
|
|
29
|
+
this.openBySessionId = new Map();
|
|
30
|
+
this.now = options.now ?? (() => new Date());
|
|
31
|
+
this.createDialogId = options.createDialogId ?? randomUUID;
|
|
32
|
+
}
|
|
33
|
+
/** The session's open dialogs, oldest first, for {@link SessionStatus} projection. */
|
|
34
|
+
pendingDialogs(sessionId) {
|
|
35
|
+
const dialogs = this.openBySessionId.get(requireSessionId(sessionId));
|
|
36
|
+
if (dialogs === undefined)
|
|
37
|
+
return [];
|
|
38
|
+
return [...dialogs.values()].map(cloneDialog);
|
|
39
|
+
}
|
|
40
|
+
open(input) {
|
|
41
|
+
const sessionId = requireSessionId(input.sessionId);
|
|
42
|
+
const kind = requireKind(input.kind);
|
|
43
|
+
const now = this.now();
|
|
44
|
+
const dialog = {
|
|
45
|
+
dialogId: requireId(this.createDialogId(), "dialogId"),
|
|
46
|
+
kind,
|
|
47
|
+
title: requireText(input.title, "dialog title"),
|
|
48
|
+
...kindFields(kind, input),
|
|
49
|
+
askedAt: now.toISOString(),
|
|
50
|
+
...timeoutField(input.timeoutMs, now),
|
|
51
|
+
runScoped: input.runScoped,
|
|
52
|
+
};
|
|
53
|
+
const dialogs = this.openBySessionId.get(sessionId) ?? new Map();
|
|
54
|
+
if (dialogs.has(dialog.dialogId)) {
|
|
55
|
+
throw new Error(`Dialog id ${dialog.dialogId} is already open in session ${sessionId}`);
|
|
56
|
+
}
|
|
57
|
+
dialogs.set(dialog.dialogId, dialog);
|
|
58
|
+
this.openBySessionId.set(sessionId, dialogs);
|
|
59
|
+
return cloneDialog(dialog);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Record the user's answer and close the dialog. The answer is validated
|
|
63
|
+
* against the dialog's kind first, so an answer that does not fit leaves the
|
|
64
|
+
* dialog open for the browser to correct.
|
|
65
|
+
*/
|
|
66
|
+
answer(sessionId, dialogId, value) {
|
|
67
|
+
const dialog = this.openBySessionId.get(requireSessionId(sessionId))?.get(dialogId);
|
|
68
|
+
if (dialog === undefined)
|
|
69
|
+
return { status: "stale" };
|
|
70
|
+
const answer = validateAnswer(dialog, value);
|
|
71
|
+
return { status: "closed", outcome: this.requireClose(sessionId, dialog, "answered", answer) };
|
|
72
|
+
}
|
|
73
|
+
/** Close the dialog without an answer; the extension's wait settles with its kind's cancel value. */
|
|
74
|
+
cancel(sessionId, dialogId, reason) {
|
|
75
|
+
const dialog = this.openBySessionId.get(requireSessionId(sessionId))?.get(dialogId);
|
|
76
|
+
if (dialog === undefined)
|
|
77
|
+
return { status: "stale" };
|
|
78
|
+
return { status: "closed", outcome: this.requireClose(sessionId, dialog, reason, undefined) };
|
|
79
|
+
}
|
|
80
|
+
requireClose(sessionId, dialog, reason, answer) {
|
|
81
|
+
const dialogs = this.openBySessionId.get(sessionId);
|
|
82
|
+
if (dialogs?.delete(dialog.dialogId) !== true) {
|
|
83
|
+
throw new Error(`Dialog ${dialog.dialogId} of session ${sessionId} disappeared while closing`);
|
|
84
|
+
}
|
|
85
|
+
if (dialogs.size === 0)
|
|
86
|
+
this.openBySessionId.delete(sessionId);
|
|
87
|
+
return {
|
|
88
|
+
dialogId: dialog.dialogId,
|
|
89
|
+
reason,
|
|
90
|
+
...(answer === undefined ? {} : { answer }),
|
|
91
|
+
askedAt: dialog.askedAt,
|
|
92
|
+
closedAt: this.timestamp(),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
timestamp() {
|
|
96
|
+
return this.now().toISOString();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function validateAnswer(dialog, value) {
|
|
100
|
+
switch (dialog.kind) {
|
|
101
|
+
case "confirm":
|
|
102
|
+
if (typeof value !== "boolean")
|
|
103
|
+
throw new PendingExtensionDialogValidationError(`Dialog ${dialog.dialogId} expects a boolean answer`);
|
|
104
|
+
return value;
|
|
105
|
+
case "select":
|
|
106
|
+
if (typeof value !== "string" || dialog.options?.includes(value) !== true) {
|
|
107
|
+
throw new PendingExtensionDialogValidationError(`Dialog ${dialog.dialogId} has no option ${String(value)}`);
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
case "input":
|
|
111
|
+
if (typeof value !== "string")
|
|
112
|
+
throw new PendingExtensionDialogValidationError(`Dialog ${dialog.dialogId} expects a text answer`);
|
|
113
|
+
if (value.length > EXTENSION_DIALOG_INPUT_MAX_LENGTH) {
|
|
114
|
+
throw new PendingExtensionDialogValidationError(`Answer of dialog ${dialog.dialogId} exceeds its length limit`);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/** Kind-specific fields of a validated record; irrelevant fields are dropped rather than rejected. */
|
|
120
|
+
function kindFields(kind, input) {
|
|
121
|
+
switch (kind) {
|
|
122
|
+
case "confirm": {
|
|
123
|
+
const message = optionalText(input.message, "dialog message");
|
|
124
|
+
return message === undefined ? {} : { message };
|
|
125
|
+
}
|
|
126
|
+
case "select":
|
|
127
|
+
return { options: validateOptions(input.options) };
|
|
128
|
+
case "input": {
|
|
129
|
+
const placeholder = optionalText(input.placeholder, "dialog placeholder");
|
|
130
|
+
return placeholder === undefined ? {} : { placeholder };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function validateOptions(options) {
|
|
135
|
+
if (options === undefined || options.length === 0) {
|
|
136
|
+
throw new PendingExtensionDialogValidationError("A select dialog must offer at least one option");
|
|
137
|
+
}
|
|
138
|
+
if (options.length > EXTENSION_DIALOG_OPTION_LIMIT) {
|
|
139
|
+
throw new PendingExtensionDialogValidationError(`A select dialog must not offer more than ${EXTENSION_DIALOG_OPTION_LIMIT.toString()} options`);
|
|
140
|
+
}
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
return options.map((option) => {
|
|
143
|
+
const validated = requireText(option, "select option");
|
|
144
|
+
if (seen.has(validated))
|
|
145
|
+
throw new PendingExtensionDialogValidationError(`Duplicate select option ${validated}`);
|
|
146
|
+
seen.add(validated);
|
|
147
|
+
return validated;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function timeoutField(timeoutMs, now) {
|
|
151
|
+
if (timeoutMs === undefined)
|
|
152
|
+
return {};
|
|
153
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
154
|
+
throw new PendingExtensionDialogValidationError("A dialog timeout must be a positive number of milliseconds");
|
|
155
|
+
}
|
|
156
|
+
return { timeoutAt: new Date(now.getTime() + timeoutMs).toISOString() };
|
|
157
|
+
}
|
|
158
|
+
function cloneDialog(dialog) {
|
|
159
|
+
return { ...dialog, ...(dialog.options === undefined ? {} : { options: [...dialog.options] }) };
|
|
160
|
+
}
|
|
161
|
+
function requireSessionId(sessionId) {
|
|
162
|
+
if (sessionId === "")
|
|
163
|
+
throw new Error("sessionId must not be empty");
|
|
164
|
+
return sessionId;
|
|
165
|
+
}
|
|
166
|
+
/** Runtime guard: the input crosses extension code, so the declared kind is checked despite its type. */
|
|
167
|
+
function requireKind(kind) {
|
|
168
|
+
if (kind !== "confirm" && kind !== "select" && kind !== "input") {
|
|
169
|
+
throw new PendingExtensionDialogValidationError(`Unknown dialog kind ${kind}`);
|
|
170
|
+
}
|
|
171
|
+
return kind;
|
|
172
|
+
}
|
|
173
|
+
function requireId(value, field) {
|
|
174
|
+
if (value.trim() === "")
|
|
175
|
+
throw new PendingExtensionDialogValidationError(`${field} must not be empty`);
|
|
176
|
+
if (value.length > EXTENSION_DIALOG_ID_MAX_LENGTH)
|
|
177
|
+
throw new PendingExtensionDialogValidationError(`${field} exceeds its length limit`);
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
function requireText(value, field) {
|
|
181
|
+
if (value.trim() === "")
|
|
182
|
+
throw new PendingExtensionDialogValidationError(`${field} must not be empty`);
|
|
183
|
+
if (value.length > EXTENSION_DIALOG_TEXT_MAX_LENGTH)
|
|
184
|
+
throw new PendingExtensionDialogValidationError(`${field} exceeds its length limit`);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
/** Optional cosmetic prose: blank means absent rather than being a validation error. */
|
|
188
|
+
function optionalText(value, field) {
|
|
189
|
+
if (value === undefined || value.trim() === "")
|
|
190
|
+
return undefined;
|
|
191
|
+
if (value.length > EXTENSION_DIALOG_TEXT_MAX_LENGTH) {
|
|
192
|
+
throw new PendingExtensionDialogValidationError(`${field} exceeds its length limit`);
|
|
193
|
+
}
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=pendingExtensionDialogStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pendingExtensionDialogStore.js","sourceRoot":"","sources":["../../../src/server/sessions/pendingExtensionDialogStore.ts"],"names":[],"mappings":"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"}
|