@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,744 @@
|
|
|
1
|
+
import { ASK_USER_ID_MAX_LENGTH, ASK_USER_OPTION_LIMIT, ASK_USER_OTHER_TEXT_MAX_LENGTH, ASK_USER_QUESTION_LIMIT, EXTENSION_DIALOG_ID_MAX_LENGTH, EXTENSION_DIALOG_INPUT_MAX_LENGTH, SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH, SESSION_UNREAD_CATALOG_ID_MAX_LENGTH, SESSION_UNREAD_CWD_MAX_LENGTH, SESSION_UNREAD_SESSION_ID_MAX_LENGTH } from "../../shared/apiTypes.js";
|
|
2
|
+
import { projectBrowserMessageResponse } from "../browserMessageProjection.js";
|
|
3
|
+
import { normalizeRequestCwd } from "../workingDirectory.js";
|
|
4
|
+
import { clearInterruptedRuns, readInterruptedRuns } from "./interruptedRunStore.js";
|
|
5
|
+
import { normalizeSessionCleanupRequest } from "./sessionCleanup.js";
|
|
6
|
+
const MAX_NOTIFICATION_SESSION_ID_LENGTH = 512;
|
|
7
|
+
const MAX_NOTIFICATION_CWD_LENGTH = 32 * 1024;
|
|
8
|
+
const MAX_NOTIFICATION_DAEMON_ID_LENGTH = 512;
|
|
9
|
+
const MAX_NOTIFICATION_ID_LENGTH = 1024;
|
|
10
|
+
export function registerSessionRoutes(app, sessions, eventHub, prefix = "") {
|
|
11
|
+
app.get(`${prefix}/sessions`, async (request, reply) => {
|
|
12
|
+
if (request.query.cwd === undefined || request.query.cwd === "")
|
|
13
|
+
return reply.code(400).send({ error: "cwd query parameter is required" });
|
|
14
|
+
try {
|
|
15
|
+
return await sessions.list(normalizeRequestCwd(request.query.cwd));
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
app.post(`${prefix}/sessions`, async (request, reply) => {
|
|
22
|
+
try {
|
|
23
|
+
const body = requireRecord(request.body);
|
|
24
|
+
// An opaque label the caller uses to recognise its own construction's
|
|
25
|
+
// startup reports. Optional: only a browser row waiting for a session id
|
|
26
|
+
// has anything to correlate.
|
|
27
|
+
const startupToken = body["startupToken"] === undefined ? undefined : requireNonEmptyString(body, "startupToken");
|
|
28
|
+
return await sessions.start(normalizeRequestCwd(requireString(body, "cwd")), optionalField("startupToken", startupToken));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
app.get(`${prefix}/sessions/notifications`, async (_request, reply) => {
|
|
35
|
+
try {
|
|
36
|
+
return await sessions.notificationCatalog();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
app.get(`${prefix}/sessions/unread`, async (_request, reply) => {
|
|
43
|
+
try {
|
|
44
|
+
return await sessions.unreadCatalog();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return reply.code(503).send({ error: errorMessage(error) });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
// Declared with the other collection routes, ahead of `/sessions/:sessionId`,
|
|
51
|
+
// so "statuses" is never captured as a session id.
|
|
52
|
+
app.get(`${prefix}/sessions/statuses`, async (_request, reply) => {
|
|
53
|
+
try {
|
|
54
|
+
return await sessions.sessionStatusCatalog();
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return reply.code(503).send({ error: errorMessage(error) });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Runs a restart had to cut off. Read-and-clear: the record answers "what did
|
|
61
|
+
// the last restart interrupt", so once handed over it is spent -- leaving it
|
|
62
|
+
// would report the same interruption again after every reconnect.
|
|
63
|
+
app.get(`${prefix}/sessions/interrupted`, async (_request, reply) => {
|
|
64
|
+
try {
|
|
65
|
+
const record = await readInterruptedRuns();
|
|
66
|
+
if (record.runs.length > 0)
|
|
67
|
+
await clearInterruptedRuns();
|
|
68
|
+
return record;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return reply.code(503).send({ error: errorMessage(error) });
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
app.post(`${prefix}/sessions/:sessionId/unread/acknowledge`, async (request, reply) => {
|
|
75
|
+
let sessionId;
|
|
76
|
+
let acknowledgement;
|
|
77
|
+
try {
|
|
78
|
+
const body = requireRecord(request.body);
|
|
79
|
+
sessionId = requireNonEmptyBoundedString(request.params.sessionId, "sessionId", SESSION_UNREAD_SESSION_ID_MAX_LENGTH);
|
|
80
|
+
acknowledgement = {
|
|
81
|
+
cwd: normalizeRequestCwd(requireNonEmptyBoundedString(body["cwd"], "cwd", SESSION_UNREAD_CWD_MAX_LENGTH)),
|
|
82
|
+
catalogId: requireNonEmptyBoundedString(body["catalogId"], "catalogId", SESSION_UNREAD_CATALOG_ID_MAX_LENGTH),
|
|
83
|
+
throughCompletionOrder: requirePositiveSafeInteger(body["throughCompletionOrder"], "throughCompletionOrder"),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return await sessions.acknowledgeUnread(sessionId, acknowledgement);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return reply.code(503).send({ error: errorMessage(error) });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
app.post(`${prefix}/sessions/cleanup/preview`, async (request, reply) => {
|
|
97
|
+
try {
|
|
98
|
+
return await sessions.cleanupPreview(normalizeSessionCleanupRequest(optionalRecord(request.body)));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
app.post(`${prefix}/sessions/cleanup`, async (request, reply) => {
|
|
105
|
+
try {
|
|
106
|
+
return await sessions.cleanup(normalizeSessionCleanupRequest(optionalRecord(request.body)));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return reply.code(400).send({ error: errorMessage(error) });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
app.post(`${prefix}/sessions/bulk/archive`, async (request, reply) => {
|
|
113
|
+
try {
|
|
114
|
+
return await sessions.archiveMany(bulkMutationRefsFromBody(request.body));
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
app.post(`${prefix}/sessions/bulk/delete-archived`, async (request, reply) => {
|
|
121
|
+
try {
|
|
122
|
+
return await sessions.deleteArchivedMany(bulkMutationRefsFromBody(request.body));
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
app.get(`${prefix}/sessions/:sessionId/notifications`, async (request, reply) => {
|
|
129
|
+
try {
|
|
130
|
+
return await sessions.notificationInbox(notificationRefFromQuery(request.params.sessionId, request.query));
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return reply.code(notificationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
app.post(`${prefix}/sessions/:sessionId/notifications/dismiss`, async (request, reply) => {
|
|
137
|
+
try {
|
|
138
|
+
const body = requireRecord(request.body);
|
|
139
|
+
const ref = notificationRefFromBody(request.params.sessionId, body);
|
|
140
|
+
return await sessions.dismissNotification(ref, {
|
|
141
|
+
daemonInstanceId: requireNonEmptyBoundedString(body["daemonInstanceId"], "daemonInstanceId", MAX_NOTIFICATION_DAEMON_ID_LENGTH),
|
|
142
|
+
notificationId: requireNonEmptyBoundedString(body["notificationId"], "notificationId", MAX_NOTIFICATION_ID_LENGTH),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
return reply.code(notificationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
app.post(`${prefix}/sessions/:sessionId/notifications/dismiss-all`, async (request, reply) => {
|
|
150
|
+
try {
|
|
151
|
+
const body = requireRecord(request.body);
|
|
152
|
+
const ref = notificationRefFromBody(request.params.sessionId, body);
|
|
153
|
+
return await sessions.dismissAllNotifications(ref, {
|
|
154
|
+
daemonInstanceId: requireNonEmptyBoundedString(body["daemonInstanceId"], "daemonInstanceId", MAX_NOTIFICATION_DAEMON_ID_LENGTH),
|
|
155
|
+
throughOrder: requireNonNegativeSafeInteger(body["throughOrder"], "throughOrder"),
|
|
156
|
+
throughOverflowWatermark: requireNonNegativeSafeInteger(body["throughOverflowWatermark"], "throughOverflowWatermark"),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return reply.code(notificationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
app.get(`${prefix}/sessions/:sessionId/messages`, async (request, reply) => {
|
|
164
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
165
|
+
if (ref === undefined)
|
|
166
|
+
return reply;
|
|
167
|
+
try {
|
|
168
|
+
const page = { ...optionalField("before", optionalNumber(request.query.before)), ...optionalField("limit", optionalNumber(request.query.limit)) };
|
|
169
|
+
const messages = await sessions.messages(ref, page);
|
|
170
|
+
return projectBrowserMessageResponse(messages);
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
app.get(`${prefix}/sessions/:sessionId/status`, async (request, reply) => {
|
|
177
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
178
|
+
if (ref === undefined)
|
|
179
|
+
return reply;
|
|
180
|
+
try {
|
|
181
|
+
return await sessions.status(ref);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
app.get(`${prefix}/sessions/:sessionId/stream-snapshot`, async (request, reply) => {
|
|
188
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
189
|
+
if (ref === undefined)
|
|
190
|
+
return reply;
|
|
191
|
+
try {
|
|
192
|
+
return await sessions.streamSnapshot(ref);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
app.get(`${prefix}/sessions/:sessionId/models`, async (request, reply) => {
|
|
199
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
200
|
+
if (ref === undefined)
|
|
201
|
+
return reply;
|
|
202
|
+
try {
|
|
203
|
+
return { models: await sessions.availableModels(ref) };
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
app.get(`${prefix}/sessions/:sessionId/models/catalog`, async (request, reply) => {
|
|
210
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
211
|
+
if (ref === undefined)
|
|
212
|
+
return reply;
|
|
213
|
+
try {
|
|
214
|
+
return { models: await sessions.modelCatalog(ref) };
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
app.post(`${prefix}/sessions/:sessionId/models/enabled`, async (request, reply) => {
|
|
221
|
+
try {
|
|
222
|
+
const body = optionalRecord(request.body);
|
|
223
|
+
return { models: await sessions.setModelEnabled(sessionRefFromBody(request.params.sessionId, body), requireString(body, "provider"), requireString(body, "modelId"), requireBoolean(body, "enabled")) };
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
app.post(`${prefix}/sessions/:sessionId/model`, async (request, reply) => {
|
|
230
|
+
try {
|
|
231
|
+
const body = optionalRecord(request.body);
|
|
232
|
+
return await sessions.setModel(sessionRefFromBody(request.params.sessionId, body), requireString(body, "provider"), requireString(body, "modelId"));
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
app.post(`${prefix}/sessions/:sessionId/model/cycle`, async (request, reply) => {
|
|
239
|
+
try {
|
|
240
|
+
const body = optionalRecord(request.body);
|
|
241
|
+
const direction = body["direction"];
|
|
242
|
+
if (direction !== undefined && direction !== "forward" && direction !== "backward")
|
|
243
|
+
throw new Error("direction must be forward or backward");
|
|
244
|
+
return await sessions.cycleModel(sessionRefFromBody(request.params.sessionId, body), direction ?? "forward");
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
app.get(`${prefix}/sessions/:sessionId/thinking-levels`, async (request, reply) => {
|
|
251
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
252
|
+
if (ref === undefined)
|
|
253
|
+
return reply;
|
|
254
|
+
try {
|
|
255
|
+
return { levels: await sessions.availableThinkingLevels(ref) };
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
app.post(`${prefix}/sessions/:sessionId/thinking-level`, async (request, reply) => {
|
|
262
|
+
try {
|
|
263
|
+
const body = optionalRecord(request.body);
|
|
264
|
+
// The level string is validated against the session's live available levels
|
|
265
|
+
// in the service, so it stays correct if pi changes the set.
|
|
266
|
+
return await sessions.setThinkingLevel(sessionRefFromBody(request.params.sessionId, body), requireThinkingLevel(body["level"]));
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
app.post(`${prefix}/sessions/:sessionId/thinking-level/cycle`, async (request, reply) => {
|
|
273
|
+
try {
|
|
274
|
+
const body = optionalRecord(request.body);
|
|
275
|
+
return await sessions.cycleThinkingLevel(sessionRefFromBody(request.params.sessionId, body));
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
// Child sessions this session spawned, for a parent conversation that keeps
|
|
282
|
+
// living while its subagents run.
|
|
283
|
+
app.get(`${prefix}/sessions/:sessionId/subsessions`, async (request, reply) => {
|
|
284
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
285
|
+
if (ref === undefined)
|
|
286
|
+
return reply;
|
|
287
|
+
try {
|
|
288
|
+
return { subsessions: await sessions.subsessions(ref) };
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
return reply.code(503).send({ error: errorMessage(error) });
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
app.get(`${prefix}/sessions/:sessionId/commands`, async (request, reply) => {
|
|
295
|
+
const ref = sessionRefFromQueryOr400(request.params.sessionId, request.query, reply);
|
|
296
|
+
if (ref === undefined)
|
|
297
|
+
return reply;
|
|
298
|
+
try {
|
|
299
|
+
return await sessions.commands(ref);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
return reply.code(404).send({ error: errorMessage(error) });
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
app.post(`${prefix}/sessions/:sessionId/prompt`, async (request, reply) => {
|
|
306
|
+
try {
|
|
307
|
+
const body = optionalRecord(request.body);
|
|
308
|
+
await sessions.prompt(sessionRefFromBody(request.params.sessionId, body), body["text"], body["streamingBehavior"], body["attachments"], { clientMessageId: body["clientMessageId"] });
|
|
309
|
+
return { accepted: true };
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
app.post(`${prefix}/sessions/:sessionId/queue/clear`, async (request, reply) => {
|
|
316
|
+
try {
|
|
317
|
+
return await sessions.clearQueue(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
318
|
+
}
|
|
319
|
+
catch (error) {
|
|
320
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
app.post(`${prefix}/sessions/:sessionId/ask/submit`, async (request, reply) => {
|
|
324
|
+
try {
|
|
325
|
+
const body = requireRecord(request.body);
|
|
326
|
+
const askId = requireBoundedId(body["askId"], "askId");
|
|
327
|
+
return await sessions.submitAsk(sessionRefFromBody(request.params.sessionId, body), askId, askUserSubmissionFromBody(body));
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
app.post(`${prefix}/sessions/:sessionId/ask/cancel`, async (request, reply) => {
|
|
334
|
+
try {
|
|
335
|
+
const body = requireRecord(request.body);
|
|
336
|
+
return await sessions.cancelAsk(sessionRefFromBody(request.params.sessionId, body), requireBoundedId(body["askId"], "askId"));
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
app.post(`${prefix}/sessions/:sessionId/dialogs/answer`, async (request, reply) => {
|
|
343
|
+
try {
|
|
344
|
+
const body = requireRecord(request.body);
|
|
345
|
+
const answer = extensionDialogAnswerFromBody(body);
|
|
346
|
+
return await sessions.answerDialog(sessionRefFromBody(request.params.sessionId, body), answer.dialogId, answer.value);
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
app.post(`${prefix}/sessions/:sessionId/dialogs/cancel`, async (request, reply) => {
|
|
353
|
+
try {
|
|
354
|
+
const body = requireRecord(request.body);
|
|
355
|
+
const cancel = extensionDialogCancelFromBody(body);
|
|
356
|
+
return await sessions.cancelDialog(sessionRefFromBody(request.params.sessionId, body), cancel.dialogId);
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
app.post(`${prefix}/sessions/:sessionId/warnings/dismiss`, async (request, reply) => {
|
|
363
|
+
try {
|
|
364
|
+
const body = optionalRecord(request.body);
|
|
365
|
+
return await sessions.dismissWarning(sessionRefFromBody(request.params.sessionId, body), requireString(body, "dismissId"));
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
app.post(`${prefix}/sessions/:sessionId/attachments`, async (request, reply) => {
|
|
372
|
+
try {
|
|
373
|
+
const body = optionalRecord(request.body);
|
|
374
|
+
const folder = body["folder"];
|
|
375
|
+
if (folder !== undefined && typeof folder !== "string")
|
|
376
|
+
throw new Error("folder field must be a string");
|
|
377
|
+
const attachments = await sessions.saveAttachments(sessionRefFromBody(request.params.sessionId, body), body["attachments"], folder);
|
|
378
|
+
return { attachments };
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
app.post(`${prefix}/sessions/:sessionId/shell`, async (request, reply) => {
|
|
385
|
+
try {
|
|
386
|
+
const body = optionalRecord(request.body);
|
|
387
|
+
await sessions.shell(sessionRefFromBody(request.params.sessionId, body), requireString(body, "text"));
|
|
388
|
+
return { accepted: true };
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
app.post(`${prefix}/sessions/:sessionId/commands/run`, async (request, reply) => {
|
|
395
|
+
try {
|
|
396
|
+
const body = optionalRecord(request.body);
|
|
397
|
+
return await sessions.runCommand(sessionRefFromBody(request.params.sessionId, body), requireString(body, "text"));
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
app.post(`${prefix}/sessions/:sessionId/commands/respond`, async (request, reply) => {
|
|
404
|
+
try {
|
|
405
|
+
const body = optionalRecord(request.body);
|
|
406
|
+
return await sessions.respondToCommand(sessionRefFromBody(request.params.sessionId, body), requireString(body, "requestId"), requireString(body, "value"));
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
app.post(`${prefix}/sessions/:sessionId/tree/navigate`, async (request, reply) => {
|
|
413
|
+
try {
|
|
414
|
+
const body = requireRecord(request.body);
|
|
415
|
+
return await sessions.navigateTree(sessionRefFromBody(request.params.sessionId, body), sessionTreeNavigateRequestFromBody(body));
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
app.post(`${prefix}/sessions/:sessionId/tree/fork`, async (request, reply) => {
|
|
422
|
+
try {
|
|
423
|
+
const body = requireRecord(request.body);
|
|
424
|
+
return await sessions.forkFromTree(sessionRefFromBody(request.params.sessionId, body), sessionTreeForkRequestFromBody(body));
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
app.post(`${prefix}/sessions/:sessionId/abort`, async (request, reply) => {
|
|
431
|
+
try {
|
|
432
|
+
await sessions.abort(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
433
|
+
return { aborted: true };
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
app.post(`${prefix}/sessions/:sessionId/stop`, async (request, reply) => {
|
|
440
|
+
try {
|
|
441
|
+
await sessions.stop(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
442
|
+
return { stopped: true };
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
app.post(`${prefix}/sessions/:sessionId/archive`, async (request, reply) => {
|
|
449
|
+
try {
|
|
450
|
+
await sessions.archive(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
451
|
+
return { archived: true };
|
|
452
|
+
}
|
|
453
|
+
catch (error) {
|
|
454
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
app.post(`${prefix}/sessions/:sessionId/archive-tree`, async (request, reply) => {
|
|
458
|
+
try {
|
|
459
|
+
return await sessions.archiveTree(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
app.post(`${prefix}/sessions/:sessionId/restore`, async (request, reply) => {
|
|
466
|
+
try {
|
|
467
|
+
await sessions.restore(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
468
|
+
return { restored: true };
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
app.post(`${prefix}/sessions/:sessionId/reload`, async (request, reply) => {
|
|
475
|
+
try {
|
|
476
|
+
await sessions.reload(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
477
|
+
return { reloaded: true };
|
|
478
|
+
}
|
|
479
|
+
catch (error) {
|
|
480
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
app.post(`${prefix}/sessions/:sessionId/detach-parent`, async (request, reply) => {
|
|
484
|
+
try {
|
|
485
|
+
await sessions.detachParent(sessionRefFromBody(request.params.sessionId, optionalRecord(request.body)));
|
|
486
|
+
return { detached: true };
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
return reply.code(mutationErrorStatus(error)).send({ error: errorMessage(error) });
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
app.get(`${prefix}/sessions/:sessionId/events`, { websocket: true }, (socket, request) => {
|
|
493
|
+
// Only the id matters for event subscription; cwd is intentionally ignored
|
|
494
|
+
// so a malformed value cannot throw inside the websocket handler.
|
|
495
|
+
eventHub.add(request.params.sessionId, socket);
|
|
496
|
+
});
|
|
497
|
+
app.get(`${prefix}/sessions/events`, { websocket: true }, (socket) => {
|
|
498
|
+
eventHub.addGlobal(socket);
|
|
499
|
+
});
|
|
500
|
+
app.get(`${prefix}/events`, { websocket: true }, (socket) => {
|
|
501
|
+
eventHub.addGlobal(socket);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
function bulkMutationRefsFromBody(body) {
|
|
505
|
+
const record = requireRecord(body);
|
|
506
|
+
const sessions = record["sessions"];
|
|
507
|
+
if (!Array.isArray(sessions))
|
|
508
|
+
throw new Error("sessions field must be an array");
|
|
509
|
+
return sessions.map(parseBulkMutationRef);
|
|
510
|
+
}
|
|
511
|
+
function parseBulkMutationRef(value) {
|
|
512
|
+
const record = requireRecord(value);
|
|
513
|
+
const id = requireString(record, "id").trim();
|
|
514
|
+
if (id === "")
|
|
515
|
+
throw new Error("id field must not be empty");
|
|
516
|
+
return { id, cwd: requireRefCwd(record["cwd"]) };
|
|
517
|
+
}
|
|
518
|
+
function notificationRefFromQuery(id, query) {
|
|
519
|
+
const cwd = requireNonEmptyBoundedString(query.cwd, "cwd", MAX_NOTIFICATION_CWD_LENGTH);
|
|
520
|
+
return notificationRef(id, cwd);
|
|
521
|
+
}
|
|
522
|
+
function notificationRefFromBody(id, body) {
|
|
523
|
+
const cwd = requireNonEmptyBoundedString(body["cwd"], "cwd", MAX_NOTIFICATION_CWD_LENGTH);
|
|
524
|
+
return notificationRef(id, cwd);
|
|
525
|
+
}
|
|
526
|
+
function notificationRef(id, cwd) {
|
|
527
|
+
return {
|
|
528
|
+
id: requireNonEmptyBoundedString(id, "sessionId", MAX_NOTIFICATION_SESSION_ID_LENGTH),
|
|
529
|
+
cwd: normalizeRequestCwd(cwd),
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function sessionRefFromQuery(id, query) {
|
|
533
|
+
const cwd = query.cwd;
|
|
534
|
+
if (cwd === undefined || cwd === "")
|
|
535
|
+
throw new Error("cwd query parameter is required");
|
|
536
|
+
return { id, cwd: normalizeRequestCwd(cwd) };
|
|
537
|
+
}
|
|
538
|
+
// Every read-only per-session GET route starts with this parse; a malformed
|
|
539
|
+
// query is a 400, sent here so the handlers stay flat.
|
|
540
|
+
function sessionRefFromQueryOr400(id, query, reply) {
|
|
541
|
+
try {
|
|
542
|
+
return sessionRefFromQuery(id, query);
|
|
543
|
+
}
|
|
544
|
+
catch (error) {
|
|
545
|
+
reply.code(400).send({ error: errorMessage(error) });
|
|
546
|
+
return undefined;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
function sessionRefFromBody(id, body) {
|
|
550
|
+
return { id, cwd: requireRefCwd(body["cwd"]) };
|
|
551
|
+
}
|
|
552
|
+
// Every per-session ref is cwd-scoped: the workspace path picks the session
|
|
553
|
+
// store the id resolves in, and it is normalized here so everything past the
|
|
554
|
+
// route layer sees canonical paths.
|
|
555
|
+
function requireRefCwd(cwd) {
|
|
556
|
+
if (typeof cwd !== "string")
|
|
557
|
+
throw new Error("cwd field must be a string");
|
|
558
|
+
if (cwd === "")
|
|
559
|
+
throw new Error("cwd field must not be empty");
|
|
560
|
+
return normalizeRequestCwd(cwd);
|
|
561
|
+
}
|
|
562
|
+
function sessionTreeNavigateRequestFromBody(body) {
|
|
563
|
+
const targetId = requireNonEmptyString(body, "targetId");
|
|
564
|
+
const expectedLeafId = requireNullableString(body, "expectedLeafId");
|
|
565
|
+
return { targetId, expectedLeafId, summary: sessionTreeSummaryChoice(body["summary"]) };
|
|
566
|
+
}
|
|
567
|
+
function sessionTreeForkRequestFromBody(body) {
|
|
568
|
+
const entryId = requireNonEmptyString(body, "entryId");
|
|
569
|
+
const expectedLeafId = requireNullableString(body, "expectedLeafId");
|
|
570
|
+
return { entryId, expectedLeafId };
|
|
571
|
+
}
|
|
572
|
+
function sessionTreeSummaryChoice(value) {
|
|
573
|
+
const summary = requireRecord(value);
|
|
574
|
+
const mode = requireString(summary, "mode");
|
|
575
|
+
if (mode === "none" || mode === "default") {
|
|
576
|
+
if (Object.hasOwn(summary, "instructions"))
|
|
577
|
+
throw new Error(`instructions field is not valid for ${mode} summary mode`);
|
|
578
|
+
requireExactFields(summary, ["mode"], "summary");
|
|
579
|
+
return { mode };
|
|
580
|
+
}
|
|
581
|
+
if (mode === "custom") {
|
|
582
|
+
requireExactFields(summary, ["mode", "instructions"], "summary");
|
|
583
|
+
const instructions = requireString(summary, "instructions");
|
|
584
|
+
if (instructions.trim() === "")
|
|
585
|
+
throw new Error("instructions field must not be blank");
|
|
586
|
+
if (instructions.length > SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH) {
|
|
587
|
+
throw new Error(`instructions field must be at most ${String(SESSION_TREE_CUSTOM_INSTRUCTIONS_MAX_LENGTH)} characters`);
|
|
588
|
+
}
|
|
589
|
+
return { mode, instructions: instructions.trim() };
|
|
590
|
+
}
|
|
591
|
+
throw new Error("summary mode is invalid");
|
|
592
|
+
}
|
|
593
|
+
function requireExactFields(record, fields, label) {
|
|
594
|
+
const allowed = new Set(fields);
|
|
595
|
+
const unexpected = Object.keys(record).find((field) => !allowed.has(field));
|
|
596
|
+
if (unexpected !== undefined)
|
|
597
|
+
throw new Error(`${label} field contains unsupported property: ${unexpected}`);
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Shape-check one submitted answer set. Only transport-level checks belong here:
|
|
601
|
+
* whether the answers fit the questions that were actually asked is the pending
|
|
602
|
+
* ask store's job, since only it knows the open ask.
|
|
603
|
+
*/
|
|
604
|
+
function askUserSubmissionFromBody(body) {
|
|
605
|
+
const answers = body["answers"];
|
|
606
|
+
if (!Array.isArray(answers))
|
|
607
|
+
throw new Error("answers field must be an array");
|
|
608
|
+
if (answers.length > ASK_USER_QUESTION_LIMIT)
|
|
609
|
+
throw new Error("answers field has too many entries");
|
|
610
|
+
return { answers: answers.map(askUserAnswerFromValue) };
|
|
611
|
+
}
|
|
612
|
+
function askUserAnswerFromValue(value) {
|
|
613
|
+
const record = requireRecord(value);
|
|
614
|
+
const values = record["values"];
|
|
615
|
+
if (!Array.isArray(values))
|
|
616
|
+
throw new Error("values field must be an array");
|
|
617
|
+
if (values.length > ASK_USER_OPTION_LIMIT)
|
|
618
|
+
throw new Error("values field has too many entries");
|
|
619
|
+
const otherText = record["otherText"];
|
|
620
|
+
if (otherText !== undefined && typeof otherText !== "string")
|
|
621
|
+
throw new Error("otherText field must be a string");
|
|
622
|
+
if (typeof otherText === "string" && otherText.length > ASK_USER_OTHER_TEXT_MAX_LENGTH)
|
|
623
|
+
throw new Error("otherText field is too long");
|
|
624
|
+
return {
|
|
625
|
+
id: requireBoundedId(record["id"], "id"),
|
|
626
|
+
values: values.map((entry) => requireBoundedId(entry, "values entry")),
|
|
627
|
+
...(otherText === undefined ? {} : { otherText }),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function requireBoundedId(value, field) {
|
|
631
|
+
return requireNonEmptyBoundedString(value, field, ASK_USER_ID_MAX_LENGTH);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Shape-check one dialog answer. Only transport-level checks belong here:
|
|
635
|
+
* whether the value fits the answered dialog's kind is the pending dialog
|
|
636
|
+
* store's job, since only it knows the open dialog.
|
|
637
|
+
*/
|
|
638
|
+
function extensionDialogAnswerFromBody(body) {
|
|
639
|
+
const dialogId = requireNonEmptyBoundedString(body["dialogId"], "dialogId", EXTENSION_DIALOG_ID_MAX_LENGTH);
|
|
640
|
+
const value = body["value"];
|
|
641
|
+
if (typeof value === "boolean")
|
|
642
|
+
return { dialogId, value };
|
|
643
|
+
if (typeof value === "string") {
|
|
644
|
+
if (value.length > EXTENSION_DIALOG_INPUT_MAX_LENGTH)
|
|
645
|
+
throw new Error("value field is too long");
|
|
646
|
+
return { dialogId, value };
|
|
647
|
+
}
|
|
648
|
+
throw new Error("value field must be a string or a boolean");
|
|
649
|
+
}
|
|
650
|
+
function extensionDialogCancelFromBody(body) {
|
|
651
|
+
return { dialogId: requireNonEmptyBoundedString(body["dialogId"], "dialogId", EXTENSION_DIALOG_ID_MAX_LENGTH) };
|
|
652
|
+
}
|
|
653
|
+
function optionalRecord(value) {
|
|
654
|
+
if (value === undefined || value === null)
|
|
655
|
+
return {};
|
|
656
|
+
return requireRecord(value);
|
|
657
|
+
}
|
|
658
|
+
function requireRecord(value) {
|
|
659
|
+
if (!isRecord(value))
|
|
660
|
+
throw new Error("request body must be an object");
|
|
661
|
+
return value;
|
|
662
|
+
}
|
|
663
|
+
function requireString(record, field) {
|
|
664
|
+
const value = record[field];
|
|
665
|
+
if (typeof value !== "string")
|
|
666
|
+
throw new Error(`${field} field must be a string`);
|
|
667
|
+
return value;
|
|
668
|
+
}
|
|
669
|
+
function requireBoolean(record, field) {
|
|
670
|
+
const value = record[field];
|
|
671
|
+
if (typeof value !== "boolean")
|
|
672
|
+
throw new Error(`${field} field must be a boolean`);
|
|
673
|
+
return value;
|
|
674
|
+
}
|
|
675
|
+
function requireNonEmptyString(record, field) {
|
|
676
|
+
const value = requireString(record, field);
|
|
677
|
+
if (value.trim() === "")
|
|
678
|
+
throw new Error(`${field} field must not be empty`);
|
|
679
|
+
return value;
|
|
680
|
+
}
|
|
681
|
+
function requireNullableString(record, field) {
|
|
682
|
+
if (!Object.hasOwn(record, field))
|
|
683
|
+
throw new Error(`${field} field is required`);
|
|
684
|
+
const value = record[field];
|
|
685
|
+
if (value === null)
|
|
686
|
+
return null;
|
|
687
|
+
if (typeof value !== "string")
|
|
688
|
+
throw new Error(`${field} field must be a string or null`);
|
|
689
|
+
if (value.trim() === "")
|
|
690
|
+
throw new Error(`${field} field must not be empty`);
|
|
691
|
+
return value;
|
|
692
|
+
}
|
|
693
|
+
function requireNonEmptyBoundedString(value, field, maxLength) {
|
|
694
|
+
if (typeof value !== "string")
|
|
695
|
+
throw new Error(`${field} field must be a string`);
|
|
696
|
+
if (value === "")
|
|
697
|
+
throw new Error(`${field} field must not be empty`);
|
|
698
|
+
if (value.length > maxLength)
|
|
699
|
+
throw new Error(`${field} field is too long`);
|
|
700
|
+
return value;
|
|
701
|
+
}
|
|
702
|
+
function requireNonNegativeSafeInteger(value, field) {
|
|
703
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
704
|
+
throw new Error(`${field} field must be a non-negative safe integer`);
|
|
705
|
+
}
|
|
706
|
+
return value;
|
|
707
|
+
}
|
|
708
|
+
function requirePositiveSafeInteger(value, field) {
|
|
709
|
+
const parsed = requireNonNegativeSafeInteger(value, field);
|
|
710
|
+
if (parsed === 0)
|
|
711
|
+
throw new Error(`${field} field must be positive`);
|
|
712
|
+
return parsed;
|
|
713
|
+
}
|
|
714
|
+
function requireThinkingLevel(value) {
|
|
715
|
+
if (typeof value !== "string" || value === "")
|
|
716
|
+
throw new Error("level field is invalid");
|
|
717
|
+
return value;
|
|
718
|
+
}
|
|
719
|
+
function optionalField(key, value) {
|
|
720
|
+
return value === undefined ? {} : { [key]: value };
|
|
721
|
+
}
|
|
722
|
+
function optionalNumber(value) {
|
|
723
|
+
if (value === undefined || value === "")
|
|
724
|
+
return undefined;
|
|
725
|
+
const parsed = Number(value);
|
|
726
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
727
|
+
}
|
|
728
|
+
function errorMessage(error) {
|
|
729
|
+
return error instanceof Error ? error.message : String(error);
|
|
730
|
+
}
|
|
731
|
+
function mutationErrorStatus(error) {
|
|
732
|
+
return isSessionNotFoundError(error) ? 404 : 400;
|
|
733
|
+
}
|
|
734
|
+
function notificationErrorStatus(error) {
|
|
735
|
+
return isSessionNotFoundError(error) ? 404 : 400;
|
|
736
|
+
}
|
|
737
|
+
function isSessionNotFoundError(error) {
|
|
738
|
+
const message = errorMessage(error);
|
|
739
|
+
return message === "Session not found" || message === "Archived session not found";
|
|
740
|
+
}
|
|
741
|
+
function isRecord(value) {
|
|
742
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
743
|
+
}
|
|
744
|
+
//# sourceMappingURL=sessionRoutes.js.map
|