@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,11 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">PI WEB mark</title>
|
|
3
|
+
<desc id="desc">A horizontal brand bar.</desc>
|
|
4
|
+
<style>
|
|
5
|
+
.bar { fill: #00f0d8; }
|
|
6
|
+
@media (prefers-color-scheme: light) {
|
|
7
|
+
.bar { fill: #008c82; }
|
|
8
|
+
}
|
|
9
|
+
</style>
|
|
10
|
+
<rect class="bar" x="128" y="448" width="768" height="128"/>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
6
|
+
<title>PI WEB</title>
|
|
7
|
+
<meta name="theme-color" content="#0d1117" />
|
|
8
|
+
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
9
|
+
<link rel="apple-touch-icon" href="./apple-touch-icon.png" />
|
|
10
|
+
<link rel="manifest" href="./manifest.webmanifest" />
|
|
11
|
+
<style>
|
|
12
|
+
:root {
|
|
13
|
+
color-scheme: dark;
|
|
14
|
+
/*
|
|
15
|
+
* Scale tokens: the parts of the look that do not change with the
|
|
16
|
+
* palette. Themes decide colour; these decide density, rhythm and
|
|
17
|
+
* shape, so two themes cannot disagree about how tall a row is or how
|
|
18
|
+
* round a card corner should be. Custom properties inherit through
|
|
19
|
+
* shadow roots, which is what lets every component read them.
|
|
20
|
+
*
|
|
21
|
+
* Spacing is a 2px-based scale with the small steps a dense tool
|
|
22
|
+
* actually uses; type is a compact scale for glancing rather than
|
|
23
|
+
* reading; radii step with the size of the thing they round.
|
|
24
|
+
*/
|
|
25
|
+
--pi-space-1: 2px;
|
|
26
|
+
--pi-space-2: 4px;
|
|
27
|
+
--pi-space-3: 6px;
|
|
28
|
+
--pi-space-4: 8px;
|
|
29
|
+
--pi-space-5: 10px;
|
|
30
|
+
--pi-space-6: 12px;
|
|
31
|
+
--pi-space-7: 16px;
|
|
32
|
+
--pi-space-8: 20px;
|
|
33
|
+
--pi-space-9: 24px;
|
|
34
|
+
--pi-text-2xs: 11px;
|
|
35
|
+
--pi-text-xs: 12px;
|
|
36
|
+
--pi-text-sm: 13px;
|
|
37
|
+
--pi-text-base: 14px;
|
|
38
|
+
--pi-text-md: 15px;
|
|
39
|
+
--pi-text-lg: 17px;
|
|
40
|
+
--pi-text-xl: 20px;
|
|
41
|
+
--pi-leading-tight: 1.25;
|
|
42
|
+
--pi-leading-normal: 1.45;
|
|
43
|
+
--pi-weight-regular: 400;
|
|
44
|
+
--pi-weight-medium: 500;
|
|
45
|
+
--pi-weight-semibold: 600;
|
|
46
|
+
--pi-radius-xs: 4px;
|
|
47
|
+
--pi-radius-sm: 6px;
|
|
48
|
+
--pi-radius-md: 8px;
|
|
49
|
+
--pi-radius-lg: 12px;
|
|
50
|
+
--pi-radius-xl: 16px;
|
|
51
|
+
--pi-radius-pill: 999px;
|
|
52
|
+
/* Touch floor first: a control is 44px on a finger, 32px under a mouse. */
|
|
53
|
+
--pi-control-height: 32px;
|
|
54
|
+
--pi-control-height-touch: 44px;
|
|
55
|
+
--pi-motion-fast: 120ms;
|
|
56
|
+
--pi-motion-base: 180ms;
|
|
57
|
+
--pi-motion-slow: 260ms;
|
|
58
|
+
--pi-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
59
|
+
--pi-focus-ring-width: 2px;
|
|
60
|
+
--pi-focus-ring-offset: 2px;
|
|
61
|
+
/*
|
|
62
|
+
* Type stacks, named by role. Nothing is downloaded: this app is a PWA
|
|
63
|
+
* that has to work offline and on a LAN address, so the identity comes
|
|
64
|
+
* from the pairing and the scale rather than from a webfont. A theme or
|
|
65
|
+
* a plugin can override any of these.
|
|
66
|
+
*
|
|
67
|
+
* UI prefers the platform's own interface face (SF, Segoe, Roboto);
|
|
68
|
+
* display reaches for a more characterful grotesque when the system has
|
|
69
|
+
* one and falls back to the UI stack; mono is the terminal's face, so
|
|
70
|
+
* transcript code and the terminal agree.
|
|
71
|
+
*/
|
|
72
|
+
--pi-font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
73
|
+
--pi-font-display: "SF Pro Display", "Segoe UI Variable Display", Inter, "Helvetica Neue", var(--pi-font-ui);
|
|
74
|
+
--pi-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
|
75
|
+
--pi-control-font-size: 16px;
|
|
76
|
+
--pi-control-font-family: var(--pi-font-ui);
|
|
77
|
+
--pi-control-monospace-font-family: var(--pi-font-mono);
|
|
78
|
+
--pi-bg: #0d1117;
|
|
79
|
+
--pi-surface: #161b22;
|
|
80
|
+
--pi-surface-hover: #21262d;
|
|
81
|
+
--pi-terminal-bg: #05070a;
|
|
82
|
+
--pi-terminal-text: #e6edf3;
|
|
83
|
+
--pi-border: #30363d;
|
|
84
|
+
--pi-border-muted: #21262d;
|
|
85
|
+
--pi-text: #e6edf3;
|
|
86
|
+
--pi-text-secondary: #c9d1d9;
|
|
87
|
+
--pi-text-bright: #f0f6fc;
|
|
88
|
+
--pi-muted: #8b949e;
|
|
89
|
+
--pi-dim: #6e7681;
|
|
90
|
+
--pi-accent: #58a6ff;
|
|
91
|
+
--pi-accent-border: #2f81f7;
|
|
92
|
+
--pi-selection-bg: #0d2847;
|
|
93
|
+
--pi-success: #3fb950;
|
|
94
|
+
--pi-success-border: #238636;
|
|
95
|
+
--pi-success-bg: #0f1b12;
|
|
96
|
+
--pi-success-surface: #0f2a16;
|
|
97
|
+
--pi-success-ring: #3fb95055;
|
|
98
|
+
--pi-warning: #d29922;
|
|
99
|
+
--pi-warning-border: #6e5200;
|
|
100
|
+
--pi-warning-surface: #1f1a10;
|
|
101
|
+
--pi-danger: #ff7b72;
|
|
102
|
+
--pi-purple: #d2a8ff;
|
|
103
|
+
--pi-purple-border: #a371f7;
|
|
104
|
+
--pi-purple-surface: #21132f;
|
|
105
|
+
--pi-overlay: #0008;
|
|
106
|
+
--pi-shadow-soft: #0006;
|
|
107
|
+
--pi-shadow: #0008;
|
|
108
|
+
--pi-shadow-strong: #000b;
|
|
109
|
+
--pi-bg-overlay-soft: #0d1117dd;
|
|
110
|
+
--pi-bg-overlay: #0d1117e6;
|
|
111
|
+
--pi-success-bg-overlay: #0f1b12ee;
|
|
112
|
+
--pi-terminal-selection: #264f78;
|
|
113
|
+
}
|
|
114
|
+
html, body { margin: 0; height: 100%; overflow: hidden; background: var(--pi-bg); color: var(--pi-text); }
|
|
115
|
+
body { min-height: 100dvh; }
|
|
116
|
+
</style>
|
|
117
|
+
<script type="module" crossorigin src="./assets/index-C2phil_t.js"></script>
|
|
118
|
+
<link rel="modulepreload" crossorigin href="./assets/vendor-editor-core-CXO8gGab.js">
|
|
119
|
+
<link rel="modulepreload" crossorigin href="./assets/vendor-editor-languages-CpW4sJsX.js">
|
|
120
|
+
</head>
|
|
121
|
+
<body>
|
|
122
|
+
<pi-web-app></pi-web-app>
|
|
123
|
+
</body>
|
|
124
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "PI WEB",
|
|
3
|
+
"short_name": "PI WEB",
|
|
4
|
+
"description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
|
|
5
|
+
"start_url": "./",
|
|
6
|
+
"scope": "./",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"background_color": "#0d1117",
|
|
9
|
+
"theme_color": "#0d1117",
|
|
10
|
+
"icons": [
|
|
11
|
+
{
|
|
12
|
+
"src": "./pwa-icon-192.png",
|
|
13
|
+
"sizes": "192x192",
|
|
14
|
+
"type": "image/png",
|
|
15
|
+
"purpose": "any maskable"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"src": "./pwa-icon-512.png",
|
|
19
|
+
"sizes": "512x512",
|
|
20
|
+
"type": "image/png",
|
|
21
|
+
"purpose": "any maskable"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
Binary file
|
|
Binary file
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize, resolve } from "node:path";
|
|
4
|
+
import { isPiWebPluginId, piWebPluginIdPattern } from "./shared/pluginIds.js";
|
|
5
|
+
export function defaultPiWebConfigPath(env = process.env) {
|
|
6
|
+
const xdgConfigHome = env["XDG_CONFIG_HOME"];
|
|
7
|
+
return join(xdgConfigHome !== undefined && xdgConfigHome !== "" ? xdgConfigHome : join(homedir(), ".config"), "pi-web", "config.json");
|
|
8
|
+
}
|
|
9
|
+
export function defaultPiWebDataDir() {
|
|
10
|
+
return join(homedir(), ".pi-web");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Default maximum HTTP body size (bytes) for the web/API and session daemon.
|
|
14
|
+
* Generous headroom for base64 image attachments (well above pi's 4.5MB
|
|
15
|
+
* per-image inline limit so several images fit in one request).
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
18
|
+
export const DEFAULT_UPLOADS_FOLDER = ".pi-web/uploads";
|
|
19
|
+
/**
|
|
20
|
+
* Default auto-cancel delay for extension dialogs whose extension set no
|
|
21
|
+
* `timeout` of its own: five minutes. `extensionDialogsTimeoutMs: 0` waits
|
|
22
|
+
* forever. Tunes the unattended-dialog safety valve only; dialogs are always
|
|
23
|
+
* enabled.
|
|
24
|
+
*/
|
|
25
|
+
export const DEFAULT_EXTENSION_DIALOGS_TIMEOUT_MS = 300_000;
|
|
26
|
+
export const PI_WEB_AGENT_COMMAND_ENV = "PI_WEB_AGENT_COMMAND";
|
|
27
|
+
export const PI_WEB_AGENT_DIR_ENV = "PI_WEB_AGENT_DIR";
|
|
28
|
+
export const PI_WEB_AGENT_SESSION_DIR_ENV = "PI_WEB_AGENT_SESSION_DIR";
|
|
29
|
+
export const PI_CODING_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
30
|
+
export const PI_CODING_AGENT_SESSION_DIR_ENV = "PI_CODING_AGENT_SESSION_DIR";
|
|
31
|
+
/**
|
|
32
|
+
* Session storage override env keys in precedence order: the deprecated
|
|
33
|
+
* `PI_WEB_` alias first so it keeps winning when both are set (today's
|
|
34
|
+
* precedence, preserved for the deprecation window), then the canonical pi
|
|
35
|
+
* SDK name.
|
|
36
|
+
*/
|
|
37
|
+
export const AGENT_SESSION_DIR_ENV_KEYS = [PI_WEB_AGENT_SESSION_DIR_ENV, PI_CODING_AGENT_SESSION_DIR_ENV];
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the pi agent state directory. PI WEB runs sessions on the bundled pi
|
|
40
|
+
* SDK only, so there is no command abstraction: the directory comes from the
|
|
41
|
+
* deprecated `PI_WEB_AGENT_DIR` alias, then the canonical `PI_CODING_AGENT_DIR`,
|
|
42
|
+
* then the deprecated `agent.dir` config alias, then the pi SDK default
|
|
43
|
+
* (`~/.pi/agent`, mirrored from the SDK's `getAgentDir()`). The session daemon
|
|
44
|
+
* exports the resolved value as `PI_CODING_AGENT_DIR` at startup, so the
|
|
45
|
+
* embedded SDK's own `getAgentDir()` observes the same directory and pi-web
|
|
46
|
+
* cannot drift from pi.
|
|
47
|
+
*/
|
|
48
|
+
export function effectiveAgentConfig(env = process.env, config = {}) {
|
|
49
|
+
const sources = [
|
|
50
|
+
[PI_WEB_AGENT_DIR_ENV, envValue(env, PI_WEB_AGENT_DIR_ENV)],
|
|
51
|
+
[PI_CODING_AGENT_DIR_ENV, envValue(env, PI_CODING_AGENT_DIR_ENV)],
|
|
52
|
+
["agent.dir", config.agent?.dir],
|
|
53
|
+
];
|
|
54
|
+
const configured = sources.find((source) => source[1] !== undefined);
|
|
55
|
+
const [sourceName, configuredDir] = configured ?? ["the agent directory default", defaultAgentDir(env)];
|
|
56
|
+
return { dir: resolveAgentDirPath(configuredDir, env, sourceName, "environment") };
|
|
57
|
+
}
|
|
58
|
+
/** The session storage override from the environment, in `AGENT_SESSION_DIR_ENV_KEYS` precedence order. */
|
|
59
|
+
export function agentSessionDirEnvOverride(env) {
|
|
60
|
+
for (const key of AGENT_SESSION_DIR_ENV_KEYS) {
|
|
61
|
+
const value = env[key];
|
|
62
|
+
if (value !== undefined && value !== "")
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
export function detectDeprecatedAgentInputs(env, config = {}) {
|
|
68
|
+
const inputs = [];
|
|
69
|
+
if (isEnvSet(env[PI_WEB_AGENT_COMMAND_ENV]))
|
|
70
|
+
inputs.push({ source: "environment", name: PI_WEB_AGENT_COMMAND_ENV });
|
|
71
|
+
if (isEnvSet(env[PI_WEB_AGENT_DIR_ENV]))
|
|
72
|
+
inputs.push({ source: "environment", name: PI_WEB_AGENT_DIR_ENV, replacement: PI_CODING_AGENT_DIR_ENV });
|
|
73
|
+
if (isEnvSet(env[PI_WEB_AGENT_SESSION_DIR_ENV]))
|
|
74
|
+
inputs.push({ source: "environment", name: PI_WEB_AGENT_SESSION_DIR_ENV, replacement: PI_CODING_AGENT_SESSION_DIR_ENV });
|
|
75
|
+
if (config.agent?.command !== undefined)
|
|
76
|
+
inputs.push({ source: "config", name: "agent.command" });
|
|
77
|
+
if (config.agent?.dir !== undefined)
|
|
78
|
+
inputs.push({ source: "config", name: "agent.dir", replacement: PI_CODING_AGENT_DIR_ENV });
|
|
79
|
+
return inputs;
|
|
80
|
+
}
|
|
81
|
+
export function effectiveUploadsConfig(config = {}) {
|
|
82
|
+
return { defaultFolder: config.uploads?.defaultFolder ?? DEFAULT_UPLOADS_FOLDER };
|
|
83
|
+
}
|
|
84
|
+
export function maxUploadBytes(env = process.env, config = {}) {
|
|
85
|
+
const fromEnv = env["PI_WEB_MAX_UPLOAD_BYTES"];
|
|
86
|
+
if (fromEnv !== undefined && fromEnv !== "") {
|
|
87
|
+
const parsed = Number(fromEnv);
|
|
88
|
+
if (Number.isInteger(parsed) && parsed > 0)
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
if (config.maxUploadBytes !== undefined)
|
|
92
|
+
return config.maxUploadBytes;
|
|
93
|
+
return DEFAULT_MAX_UPLOAD_BYTES;
|
|
94
|
+
}
|
|
95
|
+
export function piWebDataDir(env = process.env, cwd = process.cwd()) {
|
|
96
|
+
const configured = env["PI_WEB_DATA_DIR"];
|
|
97
|
+
if (configured === undefined || configured === "")
|
|
98
|
+
return defaultPiWebDataDir();
|
|
99
|
+
return resolve(cwd, configured);
|
|
100
|
+
}
|
|
101
|
+
export function piWebConfigPath(env = process.env, cwd = process.cwd()) {
|
|
102
|
+
const configured = env["PI_WEB_CONFIG"];
|
|
103
|
+
if (configured === undefined || configured === "")
|
|
104
|
+
return defaultPiWebConfigPath(env);
|
|
105
|
+
return resolve(cwd, configured);
|
|
106
|
+
}
|
|
107
|
+
export function loadPiWebConfig(options = {}) {
|
|
108
|
+
const env = options.env ?? process.env;
|
|
109
|
+
const path = piWebConfigPath(env, options.cwd ?? process.cwd());
|
|
110
|
+
if (!existsSync(path))
|
|
111
|
+
return { path, exists: false, config: {}, deprecatedAgentInputs: detectDeprecatedAgentInputs(env) };
|
|
112
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
113
|
+
if (!isRecord(parsed))
|
|
114
|
+
throw new Error(`PI WEB config must be a JSON object: ${path}`);
|
|
115
|
+
const config = parsePiWebConfig(parsed, path);
|
|
116
|
+
return { path, exists: true, config, deprecatedAgentInputs: detectDeprecatedAgentInputs(env, config) };
|
|
117
|
+
}
|
|
118
|
+
export function effectivePiWebConfig(options = {}) {
|
|
119
|
+
return resolveEffectivePiWebConfig(loadPiWebConfig(options), options);
|
|
120
|
+
}
|
|
121
|
+
export function resolveEffectivePiWebConfig(loaded, options = {}) {
|
|
122
|
+
const env = options.env ?? process.env;
|
|
123
|
+
const host = env["PI_WEB_HOST"];
|
|
124
|
+
const port = env["PI_WEB_PORT"] ?? env["PORT"];
|
|
125
|
+
const allowedHosts = env["PI_WEB_ALLOWED_HOSTS"];
|
|
126
|
+
const maxUpload = env["PI_WEB_MAX_UPLOAD_BYTES"];
|
|
127
|
+
const agent = effectiveAgentConfig(env, loaded.config);
|
|
128
|
+
return {
|
|
129
|
+
...loaded,
|
|
130
|
+
config: {
|
|
131
|
+
...loaded.config,
|
|
132
|
+
...(host !== undefined && host !== "" ? { host } : {}),
|
|
133
|
+
...(port !== undefined && port !== "" ? { port: parsePort(port, "PI_WEB_PORT") } : {}),
|
|
134
|
+
...(allowedHosts !== undefined && allowedHosts !== "" ? { allowedHosts: parseAllowedHostsEnv(allowedHosts) } : {}),
|
|
135
|
+
...(maxUpload !== undefined && maxUpload !== "" ? { maxUploadBytes: parseMaxUploadBytes(maxUpload, "PI_WEB_MAX_UPLOAD_BYTES") } : {}),
|
|
136
|
+
uploads: effectiveUploadsConfig(loaded.config),
|
|
137
|
+
// Always resolved (on by default) so the effective config is the single
|
|
138
|
+
// source of truth for the runtime state and the settings UI toggle.
|
|
139
|
+
spawnSessions: spawnSessionsEnabled(env, loaded.config),
|
|
140
|
+
// Resolved on by default like the other capabilities,
|
|
141
|
+
// so the effective config is the single source of truth for the runtime
|
|
142
|
+
// state and the settings UI toggle.
|
|
143
|
+
subsessions: subsessionsEnabled(env, loaded.config),
|
|
144
|
+
// Always resolved (on by default); the user is present for every ask.
|
|
145
|
+
askUser: askUserEnabled(env, loaded.config),
|
|
146
|
+
// Always resolved (on by default); inert outside Docker deployments.
|
|
147
|
+
environmentFacts: environmentFactsEnabled(env, loaded.config),
|
|
148
|
+
// Always resolved; the unattended-dialog safety valve, not a gate.
|
|
149
|
+
extensionDialogsTimeoutMs: loaded.config.extensionDialogsTimeoutMs ?? DEFAULT_EXTENSION_DIALOGS_TIMEOUT_MS,
|
|
150
|
+
agent,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export function savePiWebConfig(config, options = {}) {
|
|
155
|
+
const env = options.env ?? process.env;
|
|
156
|
+
const path = piWebConfigPath(env, options.cwd ?? process.cwd());
|
|
157
|
+
const normalized = parsePiWebConfig(piWebConfigRecord(config), path);
|
|
158
|
+
effectiveAgentConfig(env, normalized);
|
|
159
|
+
const existing = readExistingConfigObject(path);
|
|
160
|
+
if (existing["agent"] !== undefined)
|
|
161
|
+
parseAgentConfig(existing["agent"], path);
|
|
162
|
+
delete existing["host"];
|
|
163
|
+
delete existing["port"];
|
|
164
|
+
delete existing["allowedHosts"];
|
|
165
|
+
delete existing["shortcuts"];
|
|
166
|
+
delete existing["plugins"];
|
|
167
|
+
delete existing["pathAccess"];
|
|
168
|
+
delete existing["uploads"];
|
|
169
|
+
delete existing["maxUploadBytes"];
|
|
170
|
+
delete existing["spawnSessions"];
|
|
171
|
+
delete existing["subsessions"];
|
|
172
|
+
delete existing["askUser"];
|
|
173
|
+
delete existing["respectProjectTrust"];
|
|
174
|
+
delete existing["environmentFacts"];
|
|
175
|
+
delete existing["agent"];
|
|
176
|
+
const merged = { ...existing, ...piWebConfigRecord(normalized) };
|
|
177
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
178
|
+
writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
|
|
179
|
+
return { path, exists: true, config: normalized, deprecatedAgentInputs: detectDeprecatedAgentInputs(env, normalized) };
|
|
180
|
+
}
|
|
181
|
+
function readExistingConfigObject(path) {
|
|
182
|
+
if (!existsSync(path))
|
|
183
|
+
return {};
|
|
184
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
185
|
+
if (!isRecord(parsed))
|
|
186
|
+
throw new Error(`PI WEB config must be a JSON object: ${path}`);
|
|
187
|
+
return parsed;
|
|
188
|
+
}
|
|
189
|
+
function piWebConfigRecord(config) {
|
|
190
|
+
return {
|
|
191
|
+
...(config.host !== undefined ? { host: config.host } : {}),
|
|
192
|
+
...(config.port !== undefined ? { port: config.port } : {}),
|
|
193
|
+
...(config.allowedHosts !== undefined ? { allowedHosts: config.allowedHosts } : {}),
|
|
194
|
+
...(config.shortcuts !== undefined ? { shortcuts: config.shortcuts } : {}),
|
|
195
|
+
...(config.plugins !== undefined ? { plugins: config.plugins } : {}),
|
|
196
|
+
...(config.pathAccess !== undefined ? { pathAccess: config.pathAccess } : {}),
|
|
197
|
+
...(config.uploads !== undefined ? { uploads: config.uploads } : {}),
|
|
198
|
+
...(config.maxUploadBytes !== undefined ? { maxUploadBytes: config.maxUploadBytes } : {}),
|
|
199
|
+
...(config.spawnSessions !== undefined ? { spawnSessions: config.spawnSessions } : {}),
|
|
200
|
+
...(config.subsessions !== undefined ? { subsessions: config.subsessions } : {}),
|
|
201
|
+
...(config.askUser !== undefined ? { askUser: config.askUser } : {}),
|
|
202
|
+
...(config.environmentFacts !== undefined ? { environmentFacts: config.environmentFacts } : {}),
|
|
203
|
+
...(config.agent !== undefined ? { agent: config.agent } : {}),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function parsePiWebConfig(value, path) {
|
|
207
|
+
return {
|
|
208
|
+
...(value["host"] !== undefined ? { host: parseString(value["host"], "host", path) } : {}),
|
|
209
|
+
...(value["port"] !== undefined ? { port: parsePort(value["port"], "port", path) } : {}),
|
|
210
|
+
...(value["allowedHosts"] !== undefined ? { allowedHosts: parseAllowedHosts(value["allowedHosts"], path) } : {}),
|
|
211
|
+
...(value["shortcuts"] !== undefined ? { shortcuts: parseShortcuts(value["shortcuts"], path) } : {}),
|
|
212
|
+
...(value["plugins"] !== undefined ? { plugins: parsePlugins(value["plugins"], path) } : {}),
|
|
213
|
+
...(value["pathAccess"] !== undefined ? { pathAccess: parsePathAccessConfig(value["pathAccess"], path) } : {}),
|
|
214
|
+
...(value["uploads"] !== undefined ? { uploads: parseUploadsConfig(value["uploads"], path) } : {}),
|
|
215
|
+
...(value["maxUploadBytes"] !== undefined ? { maxUploadBytes: parseMaxUploadBytes(value["maxUploadBytes"], "maxUploadBytes", path) } : {}),
|
|
216
|
+
...(value["spawnSessions"] !== undefined ? { spawnSessions: parseSpawnSessions(value["spawnSessions"], path) } : {}),
|
|
217
|
+
...(value["subsessions"] !== undefined ? { subsessions: parseSubsessions(value["subsessions"], path) } : {}),
|
|
218
|
+
...(value["askUser"] !== undefined ? { askUser: parseAskUser(value["askUser"], path) } : {}),
|
|
219
|
+
...(value["environmentFacts"] !== undefined ? { environmentFacts: parseBooleanKey(value["environmentFacts"], "environmentFacts", path) } : {}),
|
|
220
|
+
...(value["extensionDialogsTimeoutMs"] !== undefined ? { extensionDialogsTimeoutMs: parseExtensionDialogsTimeoutMs(value["extensionDialogsTimeoutMs"], path) } : {}),
|
|
221
|
+
...(value["agent"] !== undefined ? { agent: parseAgentConfig(value["agent"], path) } : {}),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function parseMaxUploadBytes(value, key, path = "environment") {
|
|
225
|
+
const bytes = typeof value === "number" ? value : typeof value === "string" && value !== "" ? Number(value) : NaN;
|
|
226
|
+
if (!Number.isInteger(bytes) || bytes < 1)
|
|
227
|
+
throw new Error(`PI WEB config ${key} must be a positive integer: ${path}`);
|
|
228
|
+
return bytes;
|
|
229
|
+
}
|
|
230
|
+
function parseSpawnSessions(value, path) {
|
|
231
|
+
if (typeof value !== "boolean")
|
|
232
|
+
throw new Error(`PI WEB config spawnSessions must be a boolean: ${path}`);
|
|
233
|
+
return value;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Whether LLMs may start new sessions via the spawn_session tool. On by default
|
|
237
|
+
* (spawned sessions appear in the session list, so humans notice them); set the
|
|
238
|
+
* env var `PI_WEB_SPAWN_SESSIONS` or the `spawnSessions` config key to `false`
|
|
239
|
+
* to disable. The env var takes precedence over the config file.
|
|
240
|
+
*/
|
|
241
|
+
export function spawnSessionsEnabled(env = process.env, config = {}) {
|
|
242
|
+
const fromEnv = env["PI_WEB_SPAWN_SESSIONS"];
|
|
243
|
+
if (fromEnv !== undefined && fromEnv !== "")
|
|
244
|
+
return fromEnv === "1" || fromEnv.toLowerCase() === "true";
|
|
245
|
+
return config.spawnSessions ?? true;
|
|
246
|
+
}
|
|
247
|
+
function parseSubsessions(value, path) {
|
|
248
|
+
if (typeof value !== "boolean")
|
|
249
|
+
throw new Error(`PI WEB config subsessions must be a boolean: ${path}`);
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Whether LLMs may start tracked child sessions via the spawn_subsession
|
|
254
|
+
* family of tools. On by default; set the env var `PI_WEB_SUBSESSIONS` or the
|
|
255
|
+
* `subsessions` config key to `false` to disable. The env var takes precedence
|
|
256
|
+
* over the config file. Subsessions also require spawnSessions to be enabled
|
|
257
|
+
* (they share the same project-scope resolver).
|
|
258
|
+
*/
|
|
259
|
+
export function subsessionsEnabled(env = process.env, config = {}) {
|
|
260
|
+
const fromEnv = env["PI_WEB_SUBSESSIONS"];
|
|
261
|
+
if (fromEnv !== undefined && fromEnv !== "")
|
|
262
|
+
return fromEnv === "1" || fromEnv.toLowerCase() === "true";
|
|
263
|
+
return config.subsessions ?? true;
|
|
264
|
+
}
|
|
265
|
+
function parseAskUser(value, path) {
|
|
266
|
+
if (typeof value !== "boolean")
|
|
267
|
+
throw new Error(`PI WEB config askUser must be a boolean: ${path}`);
|
|
268
|
+
return value;
|
|
269
|
+
}
|
|
270
|
+
function parseExtensionDialogsTimeoutMs(value, path) {
|
|
271
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
272
|
+
throw new Error(`PI WEB config extensionDialogsTimeoutMs must be a non-negative integer: ${path}`);
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Whether LLMs may post a question set to the browser via the ask_user tool. On
|
|
278
|
+
* by default: the questions land in the session the user is already watching and
|
|
279
|
+
* nothing happens without them acting. Set the env var `PI_WEB_ASK_USER` or the
|
|
280
|
+
* `askUser` config key to `false` to remove the tool. The env var takes
|
|
281
|
+
* precedence over the config file.
|
|
282
|
+
*/
|
|
283
|
+
export function askUserEnabled(env = process.env, config = {}) {
|
|
284
|
+
const fromEnv = env["PI_WEB_ASK_USER"];
|
|
285
|
+
if (fromEnv !== undefined && fromEnv !== "")
|
|
286
|
+
return fromEnv === "1" || fromEnv.toLowerCase() === "true";
|
|
287
|
+
return config.askUser ?? true;
|
|
288
|
+
}
|
|
289
|
+
function parseBooleanKey(value, key, path) {
|
|
290
|
+
if (typeof value !== "boolean")
|
|
291
|
+
throw new Error(`PI WEB config ${key} must be a boolean: ${path}`);
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Whether PI WEB appends deployment environment facts to session system
|
|
296
|
+
* prompts. On by default; set the env var `PI_WEB_ENVIRONMENT_FACTS` or the
|
|
297
|
+
* `environmentFacts` config key to `false` to leave the system prompt
|
|
298
|
+
* untouched. The env var takes precedence over the config file.
|
|
299
|
+
*
|
|
300
|
+
* The facts describe the pi-web session nesting every session runs in; Docker
|
|
301
|
+
* deployments append container facts on top. The env var deliberately avoids
|
|
302
|
+
* the `PI_WEB_DOCKER_` prefix, which agent processes inherit for
|
|
303
|
+
* `pi-web-docker`.
|
|
304
|
+
*/
|
|
305
|
+
export function environmentFactsEnabled(env = process.env, config = {}) {
|
|
306
|
+
const fromEnv = env["PI_WEB_ENVIRONMENT_FACTS"];
|
|
307
|
+
if (fromEnv !== undefined && fromEnv !== "")
|
|
308
|
+
return fromEnv === "1" || fromEnv.toLowerCase() === "true";
|
|
309
|
+
return config.environmentFacts ?? true;
|
|
310
|
+
}
|
|
311
|
+
const OFFLINE_ENV_KEYS = ["PI_WEB_OFFLINE", "PI_OFFLINE"];
|
|
312
|
+
/**
|
|
313
|
+
* Whether the operator asked PI WEB (or pi itself) to stay offline, meaning
|
|
314
|
+
* background network access must be skipped. Matches the "set and non-empty"
|
|
315
|
+
* semantics used for the other runtime-only env switches.
|
|
316
|
+
*
|
|
317
|
+
* Deliberately narrower than `piWebStatus`'s update-check suppression: the
|
|
318
|
+
* `*_SKIP_VERSION_CHECK` keys only silence release lookups, while these keys ask
|
|
319
|
+
* for no background network at all.
|
|
320
|
+
*/
|
|
321
|
+
export function offlineModeEnabled(env = process.env) {
|
|
322
|
+
return OFFLINE_ENV_KEYS.some((key) => isEnvSet(env[key]));
|
|
323
|
+
}
|
|
324
|
+
function parseString(value, key, path) {
|
|
325
|
+
if (typeof value !== "string" || value === "")
|
|
326
|
+
throw new Error(`PI WEB config ${key} must be a non-empty string: ${path}`);
|
|
327
|
+
return value;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Keys still accepted under `agent` during the deprecation window: `command`
|
|
331
|
+
* is parsed only so the file round-trips and the deprecation detector can name
|
|
332
|
+
* it (the concept is gone), and `dir` remains honored as a deprecated alias.
|
|
333
|
+
* The section has no live schema — every accepted key is deprecated — so any
|
|
334
|
+
* other key fails loudly, naming the deprecated survivors.
|
|
335
|
+
*/
|
|
336
|
+
const DEPRECATED_AGENT_CONFIG_KEYS = new Set(["command", "dir"]);
|
|
337
|
+
export function parseAgentConfig(value, path, pathHost = "current") {
|
|
338
|
+
if (!isRecord(value))
|
|
339
|
+
throw new Error(`PI WEB config agent must be an object: ${path}`);
|
|
340
|
+
const unknownKey = Object.keys(value).find((key) => !DEPRECATED_AGENT_CONFIG_KEYS.has(key));
|
|
341
|
+
if (unknownKey !== undefined)
|
|
342
|
+
throw new Error(`PI WEB config agent accepts only the deprecated keys "command" and "dir"; unknown key ${JSON.stringify(unknownKey)}: ${path}`);
|
|
343
|
+
const command = value["command"];
|
|
344
|
+
const dir = value["dir"];
|
|
345
|
+
return {
|
|
346
|
+
// `agent.command` is deprecated and ignored (the multi-CLI abstraction is
|
|
347
|
+
// gone): it is parsed only so the file round-trips faithfully and the
|
|
348
|
+
// deprecation detector can name it. `agent.dir` is still honored as a
|
|
349
|
+
// deprecated alias during the deprecation window, so it keeps validation.
|
|
350
|
+
...(command !== undefined ? { command: parseString(command, "agent.command", path) } : {}),
|
|
351
|
+
...(dir !== undefined ? { dir: parseAgentDir(dir, "agent.dir", path, pathHost) } : {}),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function parseAgentDir(value, key, path, pathHost) {
|
|
355
|
+
const dir = parseString(value, key, path).trim();
|
|
356
|
+
const isAbsoluteDir = pathHost === "current" ? isHostAbsoluteAgentDir(dir) : isPortableAbsoluteAgentPath(dir);
|
|
357
|
+
if (!isAbsoluteDir && !isHomePath(dir, pathHost)) {
|
|
358
|
+
const absoluteLabel = pathHost === "current" ? "a host-absolute" : "an absolute";
|
|
359
|
+
throw new Error(`PI WEB config ${key} must be ${absoluteLabel} path or start with ~: ${path}`);
|
|
360
|
+
}
|
|
361
|
+
return dir;
|
|
362
|
+
}
|
|
363
|
+
function resolveAgentDirPath(value, env, key, path) {
|
|
364
|
+
const parsed = parseAgentDir(value, key, path, "current");
|
|
365
|
+
const expanded = expandHomePath(parsed, env);
|
|
366
|
+
if (!isHostAbsoluteAgentDir(expanded)) {
|
|
367
|
+
throw new Error(`PI WEB config ${key} must resolve to a host-absolute path: ${path}`);
|
|
368
|
+
}
|
|
369
|
+
return normalize(expanded);
|
|
370
|
+
}
|
|
371
|
+
export function isHostAbsoluteAgentDir(value) {
|
|
372
|
+
return isSafeAgentDirPath(value) && isAbsolute(value);
|
|
373
|
+
}
|
|
374
|
+
function isPortableAbsoluteAgentPath(value) {
|
|
375
|
+
return isSafeAgentDirPath(value) && isAbsoluteLike(value);
|
|
376
|
+
}
|
|
377
|
+
function isSafeAgentDirPath(value) {
|
|
378
|
+
return value !== "" && value === value.trim() && !hasControlCharacter(value);
|
|
379
|
+
}
|
|
380
|
+
function parsePort(value, key, path = "environment") {
|
|
381
|
+
const port = typeof value === "number" ? value : typeof value === "string" && value !== "" ? Number(value) : NaN;
|
|
382
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
383
|
+
throw new Error(`PI WEB config ${key} must be an integer from 1 to 65535: ${path}`);
|
|
384
|
+
return port;
|
|
385
|
+
}
|
|
386
|
+
function parseAllowedHosts(value, path) {
|
|
387
|
+
if (value === true)
|
|
388
|
+
return true;
|
|
389
|
+
if (!isNonEmptyStringArray(value)) {
|
|
390
|
+
throw new Error(`PI WEB config allowedHosts must be true or an array of non-empty strings: ${path}`);
|
|
391
|
+
}
|
|
392
|
+
return value;
|
|
393
|
+
}
|
|
394
|
+
function parseAllowedHostsEnv(value) {
|
|
395
|
+
if (value === "true")
|
|
396
|
+
return true;
|
|
397
|
+
return value.split(",").map((host) => host.trim()).filter((host) => host !== "");
|
|
398
|
+
}
|
|
399
|
+
export function parsePathAccessConfig(value, path) {
|
|
400
|
+
if (!isRecord(value))
|
|
401
|
+
throw new Error(`PI WEB config pathAccess must be an object: ${path}`);
|
|
402
|
+
const allowedPaths = value["allowedPaths"];
|
|
403
|
+
return {
|
|
404
|
+
...(allowedPaths !== undefined ? { allowedPaths: parseAllowedPaths(allowedPaths, path) } : {}),
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
function parseAllowedPaths(value, path) {
|
|
408
|
+
if (!isNonEmptyStringArray(value))
|
|
409
|
+
throw new Error(`PI WEB config pathAccess.allowedPaths must be an array of non-empty strings: ${path}`);
|
|
410
|
+
return value;
|
|
411
|
+
}
|
|
412
|
+
export function parseUploadsConfig(value, path) {
|
|
413
|
+
if (!isRecord(value))
|
|
414
|
+
throw new Error(`PI WEB config uploads must be an object: ${path}`);
|
|
415
|
+
const defaultFolder = value["defaultFolder"];
|
|
416
|
+
return {
|
|
417
|
+
...(defaultFolder !== undefined ? { defaultFolder: parseWorkspaceRelativeFolder(defaultFolder, "uploads.defaultFolder", path) } : {}),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function parseWorkspaceRelativeFolder(value, key, path) {
|
|
421
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
422
|
+
throw new Error(`PI WEB config ${key} must be a non-empty workspace-relative path: ${path}`);
|
|
423
|
+
if (isAbsoluteLike(value))
|
|
424
|
+
throw new Error(`PI WEB config ${key} must be workspace-relative: ${path}`);
|
|
425
|
+
const parts = value.split(/[\\/]+/).filter((part) => part !== "" && part !== ".");
|
|
426
|
+
if (parts.length === 0)
|
|
427
|
+
throw new Error(`PI WEB config ${key} must be a non-empty workspace-relative path: ${path}`);
|
|
428
|
+
if (parts.some((part) => part === ".."))
|
|
429
|
+
throw new Error(`PI WEB config ${key} must not contain path traversal: ${path}`);
|
|
430
|
+
return parts.join("/");
|
|
431
|
+
}
|
|
432
|
+
function isHomePath(value, pathHost) {
|
|
433
|
+
return value === "~" || value.startsWith("~/") || ((pathHost === "portable" || process.platform === "win32") && value.startsWith("~\\"));
|
|
434
|
+
}
|
|
435
|
+
function expandHomePath(value, env) {
|
|
436
|
+
const home = env["HOME"] !== undefined && env["HOME"] !== "" ? env["HOME"] : homedir();
|
|
437
|
+
if (value === "~")
|
|
438
|
+
return home;
|
|
439
|
+
if (value.startsWith("~/") || (process.platform === "win32" && value.startsWith("~\\")))
|
|
440
|
+
return join(home, value.slice(2));
|
|
441
|
+
return value;
|
|
442
|
+
}
|
|
443
|
+
// Exact mirror of the pi SDK default (`getAgentDir()`: `~/.pi/agent`) rather
|
|
444
|
+
// than a delegation, so the injected `env` stays authoritative; if the bundled
|
|
445
|
+
// SDK is ever swapped for a fork whose default moves, this mirror must move
|
|
446
|
+
// with it or the two resolutions drift.
|
|
447
|
+
function defaultAgentDir(env) {
|
|
448
|
+
return expandHomePath("~/.pi/agent", env);
|
|
449
|
+
}
|
|
450
|
+
function envValue(env, key) {
|
|
451
|
+
const value = env[key];
|
|
452
|
+
return value !== undefined && value !== "" ? value : undefined;
|
|
453
|
+
}
|
|
454
|
+
function isEnvSet(value) {
|
|
455
|
+
return value !== undefined && value !== "";
|
|
456
|
+
}
|
|
457
|
+
function hasControlCharacter(value) {
|
|
458
|
+
for (const character of value) {
|
|
459
|
+
const code = character.charCodeAt(0);
|
|
460
|
+
if (code < 32 || code === 127)
|
|
461
|
+
return true;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
function isAbsoluteLike(value) {
|
|
466
|
+
const withForwardSlashes = value.replace(/\\/g, "/");
|
|
467
|
+
return isAbsolute(value) || withForwardSlashes.startsWith("/") || /^[A-Za-z]:\//.test(withForwardSlashes);
|
|
468
|
+
}
|
|
469
|
+
function parseShortcuts(value, path) {
|
|
470
|
+
if (!isRecord(value))
|
|
471
|
+
throw new Error(`PI WEB config shortcuts must be an object: ${path}`);
|
|
472
|
+
return Object.fromEntries(Object.entries(value).map(([actionId, shortcut]) => {
|
|
473
|
+
if (shortcut !== null && (typeof shortcut !== "string" || shortcut === "")) {
|
|
474
|
+
throw new Error(`PI WEB config shortcut values must be non-empty strings or null: ${path}`);
|
|
475
|
+
}
|
|
476
|
+
return [actionId, shortcut];
|
|
477
|
+
}));
|
|
478
|
+
}
|
|
479
|
+
function parsePlugins(value, path) {
|
|
480
|
+
if (!isRecord(value) || Array.isArray(value))
|
|
481
|
+
throw new Error(`PI WEB config plugins must be an object: ${path}`);
|
|
482
|
+
return Object.fromEntries(Object.entries(value).map(([pluginId, config]) => {
|
|
483
|
+
if (!isPiWebPluginId(pluginId))
|
|
484
|
+
throw new Error(`PI WEB config plugin ids must match ${piWebPluginIdPattern.source}: ${path}`);
|
|
485
|
+
if (!isRecord(config) || Array.isArray(config))
|
|
486
|
+
throw new Error(`PI WEB config plugin entries must be objects: ${path}`);
|
|
487
|
+
const enabled = config["enabled"];
|
|
488
|
+
if (enabled !== undefined && typeof enabled !== "boolean")
|
|
489
|
+
throw new Error(`PI WEB config plugin enabled values must be booleans: ${path}`);
|
|
490
|
+
const settings = config["settings"];
|
|
491
|
+
if (settings !== undefined && (!isRecord(settings) || Array.isArray(settings)))
|
|
492
|
+
throw new Error(`PI WEB config plugin settings must be objects: ${path}`);
|
|
493
|
+
return [pluginId, config];
|
|
494
|
+
}));
|
|
495
|
+
}
|
|
496
|
+
function isRecord(value) {
|
|
497
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
498
|
+
}
|
|
499
|
+
function isNonEmptyStringArray(value) {
|
|
500
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string" && item !== "");
|
|
501
|
+
}
|
|
502
|
+
export function examplePiWebConfig(config = {}) {
|
|
503
|
+
return `${JSON.stringify({ host: config.host ?? "127.0.0.1", port: config.port ?? 8504, allowedHosts: config.allowedHosts ?? [] }, null, 2)}\n`;
|
|
504
|
+
}
|
|
505
|
+
//# sourceMappingURL=config.js.map
|