@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 @@
|
|
|
1
|
+
{"version":3,"file":"nodePtyNativeModule.js","sourceRoot":"","sources":["../../../src/server/diagnostics/nodePtyNativeModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,CAAC,MAAM,iCAAiC,GAAG,kEAAkE,CAAC;AAEpH,MAAM,WAAW,GAAG,iCAAiC,CAAC;AACtD,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAiBvD,MAAM,UAAU,wBAAwB,CAAC,UAA2C,EAAE;IACpF,IAAI,CAAC;QACH,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC,EAAE,CAAC;QAChC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;IACjE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,8BAA8B,CAAC,KAA+B;IAC5E,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,KAAK,WAAW,EAAE,CAAC,EAAE,CAAC;IAC5E,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE;YACL,KAAK,WAAW,EAAE;YAClB,8BAA8B,KAAK,CAAC,OAAO,EAAE;YAC7C,4DAA4D;YAC5D,yDAAyD;YACzD,OAAO,iCAAiC,EAAE;YAC1C,mCAAmC;SACpC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC,UAAU,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { accessSync, constants, existsSync, statSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export const PI_WEB_SPAWN_HELPER_ISSUE_URL = "https://github.com/jmfederico/pi-web/issues/4";
|
|
5
|
+
export const NODE_PTY_SPAWN_HELPER_UPSTREAM_ISSUE_URL = "https://github.com/microsoft/node-pty/issues/850";
|
|
6
|
+
const doctorLabel = "node-pty macOS spawn-helper executable";
|
|
7
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
8
|
+
export function checkNodePtyDarwinSpawnHelper(options = {}) {
|
|
9
|
+
const platform = options.platform ?? process.platform;
|
|
10
|
+
if (platform !== "darwin")
|
|
11
|
+
return { status: "skipped", reason: "not-macos" };
|
|
12
|
+
const arch = options.arch ?? process.arch;
|
|
13
|
+
const exists = options.exists ?? existsSync;
|
|
14
|
+
const stat = options.stat ?? statSync;
|
|
15
|
+
const access = options.access ?? accessSync;
|
|
16
|
+
let nodePtyPackageJsonPath;
|
|
17
|
+
try {
|
|
18
|
+
nodePtyPackageJsonPath = options.nodePtyPackageJsonPath ?? (options.resolveNodePtyPackageJson ?? resolveNodePtyPackageJson)();
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
return { status: "node-pty-not-found", message: errorMessage(error) };
|
|
22
|
+
}
|
|
23
|
+
const nodePtyRoot = dirname(nodePtyPackageJsonPath);
|
|
24
|
+
const nativeDir = findNodePtyNativeDir(nodePtyRoot, platform, arch, exists);
|
|
25
|
+
if (nativeDir === undefined) {
|
|
26
|
+
return {
|
|
27
|
+
status: "native-module-not-found",
|
|
28
|
+
nodePtyRoot,
|
|
29
|
+
expectedHelperPath: join(nodePtyRoot, "prebuilds", `${platform}-${arch}`, "spawn-helper"),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const helperPath = join(nativeDir, "spawn-helper");
|
|
33
|
+
try {
|
|
34
|
+
if (!stat(helperPath).isFile())
|
|
35
|
+
return { status: "spawn-helper-not-file", helperPath, nodePtyRoot };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (isFileNotFoundError(error))
|
|
39
|
+
return { status: "spawn-helper-missing", helperPath, nodePtyRoot };
|
|
40
|
+
return { status: "spawn-helper-stat-error", helperPath, nodePtyRoot, message: errorMessage(error) };
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
access(helperPath, constants.X_OK);
|
|
44
|
+
return { status: "ok", helperPath, nodePtyRoot };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return { status: "spawn-helper-not-executable", helperPath, nodePtyRoot, fixCommand: chmodFixCommand(helperPath) };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function formatNodePtyDarwinSpawnHelperCheck(check) {
|
|
51
|
+
if (check.status === "skipped")
|
|
52
|
+
return { ok: true, lines: [] };
|
|
53
|
+
if (check.status === "ok") {
|
|
54
|
+
return {
|
|
55
|
+
ok: true,
|
|
56
|
+
lines: [`✓ ${doctorLabel}`, ` ${check.helperPath}`],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (check.status === "spawn-helper-not-executable") {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
lines: [
|
|
63
|
+
`✗ ${doctorLabel}`,
|
|
64
|
+
` ${check.helperPath} exists but is not executable.`,
|
|
65
|
+
` Known upstream node-pty packaging issue: ${NODE_PTY_SPAWN_HELPER_UPSTREAM_ISSUE_URL}`,
|
|
66
|
+
` PI WEB tracking issue: ${PI_WEB_SPAWN_HELPER_ISSUE_URL}`,
|
|
67
|
+
" Proposed workaround:",
|
|
68
|
+
` ${check.fixCommand}`,
|
|
69
|
+
" Then run `pi-web doctor` again and retry opening a terminal.",
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
lines: [`✗ ${doctorLabel}`, ...failureDetails(check)],
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function resolveNodePtyPackageJson() {
|
|
79
|
+
return requireFromHere.resolve("node-pty/package.json");
|
|
80
|
+
}
|
|
81
|
+
function findNodePtyNativeDir(nodePtyRoot, platform, arch, exists) {
|
|
82
|
+
for (const dir of nodePtyNativeDirs(nodePtyRoot, platform, arch)) {
|
|
83
|
+
if (exists(join(dir, "pty.node")))
|
|
84
|
+
return dir;
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
function nodePtyNativeDirs(nodePtyRoot, platform, arch) {
|
|
89
|
+
const dirs = ["build/Release", "build/Debug", `prebuilds/${platform}-${arch}`];
|
|
90
|
+
return dirs.flatMap((dir) => [join(nodePtyRoot, dir), join(nodePtyRoot, "lib", dir)]);
|
|
91
|
+
}
|
|
92
|
+
function failureDetails(check) {
|
|
93
|
+
if (check.status === "node-pty-not-found") {
|
|
94
|
+
return [
|
|
95
|
+
` Could not resolve node-pty from PI WEB: ${check.message}`,
|
|
96
|
+
" Reinstall or update PI WEB, then run `pi-web doctor` again.",
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
if (check.status === "native-module-not-found") {
|
|
100
|
+
return [
|
|
101
|
+
` Could not find node-pty's native pty.node module under ${check.nodePtyRoot}.`,
|
|
102
|
+
` Expected macOS helper location: ${check.expectedHelperPath}`,
|
|
103
|
+
" Reinstall or update PI WEB, then run `pi-web doctor` again.",
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
if (check.status === "spawn-helper-missing") {
|
|
107
|
+
return [
|
|
108
|
+
` Expected helper is missing: ${check.helperPath}`,
|
|
109
|
+
" Reinstall or update PI WEB, then run `pi-web doctor` again.",
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
if (check.status === "spawn-helper-not-file") {
|
|
113
|
+
return [
|
|
114
|
+
` Expected helper is not a regular file: ${check.helperPath}`,
|
|
115
|
+
" Reinstall or update PI WEB, then run `pi-web doctor` again.",
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
return [
|
|
119
|
+
` Could not inspect ${check.helperPath}: ${check.message}`,
|
|
120
|
+
" Check the file permissions, then run `pi-web doctor` again.",
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
function chmodFixCommand(path) {
|
|
124
|
+
return `chmod +x ${shellSingleQuote(path)}`;
|
|
125
|
+
}
|
|
126
|
+
function shellSingleQuote(value) {
|
|
127
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
128
|
+
}
|
|
129
|
+
function isFileNotFoundError(error) {
|
|
130
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
131
|
+
}
|
|
132
|
+
function errorMessage(error) {
|
|
133
|
+
return error instanceof Error ? error.message : String(error);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=nodePtySpawnHelper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodePtySpawnHelper.js","sourceRoot":"","sources":["../../../src/server/diagnostics/nodePtySpawnHelper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const GIT_LOCAL_ENV_VARS = [
|
|
2
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
3
|
+
"GIT_COMMON_DIR",
|
|
4
|
+
"GIT_DIR",
|
|
5
|
+
"GIT_INDEX_FILE",
|
|
6
|
+
"GIT_OBJECT_DIRECTORY",
|
|
7
|
+
"GIT_PREFIX",
|
|
8
|
+
"GIT_QUARANTINE_PATH",
|
|
9
|
+
"GIT_WORK_TREE",
|
|
10
|
+
];
|
|
11
|
+
export function sanitizedGitEnv(env = process.env) {
|
|
12
|
+
const blocked = new Set(GIT_LOCAL_ENV_VARS);
|
|
13
|
+
return Object.fromEntries(Object.entries(env).filter(([key]) => !blocked.has(key)));
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=gitEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitEnv.js","sourceRoot":"","sources":["../../../src/server/git/gitEnv.ts"],"names":[],"mappings":"AAAA,MAAM,kBAAkB,GAAG;IACzB,kCAAkC;IAClC,gBAAgB;IAChB,SAAS;IACT,gBAAgB;IAChB,sBAAsB;IACtB,YAAY;IACZ,qBAAqB;IACrB,eAAe;CAChB,CAAC;AAEF,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,kBAAkB,CAAC,CAAC;IACpD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { appendFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join, posix } from "node:path";
|
|
5
|
+
import { GOALS_DIRECTORY } from "./goalStore.js";
|
|
6
|
+
import { extractGoalJsonBlock } from "./goalFile.js";
|
|
7
|
+
export class GoalArchiveError extends Error {
|
|
8
|
+
constructor(message, code) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = "GoalArchiveError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** How long another process's lock is honoured before it counts as abandoned. */
|
|
15
|
+
const LOCK_STALE_MS = 30_000;
|
|
16
|
+
/** The extension waits ~100ms for this lock, so ours must be far shorter. */
|
|
17
|
+
const LOCK_ATTEMPTS = 5;
|
|
18
|
+
const LOCK_RETRY_MS = 12;
|
|
19
|
+
export async function archiveWorkspaceGoal(workspacePath, goalId, options = {}) {
|
|
20
|
+
const safeId = goalIdForPath(goalId);
|
|
21
|
+
const directory = join(workspacePath, GOALS_DIRECTORY);
|
|
22
|
+
const now = options.now ?? (() => new Date());
|
|
23
|
+
const release = await acquireGoalLock(directory, safeId, now);
|
|
24
|
+
try {
|
|
25
|
+
const active = await findActiveGoalFile(directory, goalId);
|
|
26
|
+
if (active === undefined) {
|
|
27
|
+
return { goalId, archivedPath: "", alreadyArchived: true, agentMayRecreate: false };
|
|
28
|
+
}
|
|
29
|
+
const record = parseGoalRecord(active.content);
|
|
30
|
+
const archivedName = `goal_${archiveStamp(now())}_${safeId}.md`;
|
|
31
|
+
const archivedPath = join(directory, "archived", archivedName);
|
|
32
|
+
await mkdir(join(directory, "archived"), { recursive: true });
|
|
33
|
+
const archivedContent = archivedGoalContent(active.content, record, {
|
|
34
|
+
// Stored inside the record, not handed to the filesystem: the extension
|
|
35
|
+
// writes these as POSIX relative paths (`.pi/goals/...`), so a Windows
|
|
36
|
+
// separator here would be a foreign value in someone else's format.
|
|
37
|
+
archivedPath: posix.join(".pi", "goals", "archived", archivedName),
|
|
38
|
+
at: now().toISOString(),
|
|
39
|
+
});
|
|
40
|
+
const temporary = `${archivedPath}.${String(process.pid)}.${String(now().getTime())}.tmp`;
|
|
41
|
+
await writeFile(temporary, archivedContent, "utf8");
|
|
42
|
+
await rename(temporary, archivedPath);
|
|
43
|
+
await unlink(active.path);
|
|
44
|
+
appendGoalEvent(directory, {
|
|
45
|
+
type: "goal_archived",
|
|
46
|
+
goalId,
|
|
47
|
+
archivePath: archivedPath,
|
|
48
|
+
at: now().toISOString(),
|
|
49
|
+
source: "pi-web",
|
|
50
|
+
});
|
|
51
|
+
await dropPoolSnapshot(options.home ?? homedir(), workspacePath);
|
|
52
|
+
return { goalId, archivedPath, alreadyArchived: false, agentMayRecreate: true };
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
await release();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The lock file the extension uses. `wx` is the whole mechanism: whoever
|
|
60
|
+
* creates the file owns the goal. A lock left by a process that died is taken
|
|
61
|
+
* over once it is older than the staleness window, since otherwise a crash
|
|
62
|
+
* during a turn would make the goal permanently unarchivable.
|
|
63
|
+
*/
|
|
64
|
+
async function acquireGoalLock(directory, safeId, now) {
|
|
65
|
+
const locks = join(directory, ".locks");
|
|
66
|
+
const lockPath = join(locks, `${safeId}.lock`);
|
|
67
|
+
await mkdir(locks, { recursive: true });
|
|
68
|
+
for (let attempt = 0; attempt < LOCK_ATTEMPTS; attempt++) {
|
|
69
|
+
try {
|
|
70
|
+
await writeFile(lockPath, JSON.stringify({ pid: process.pid, startedAt: now().toISOString() }), { encoding: "utf8", flag: "wx" });
|
|
71
|
+
return async () => { await rm(lockPath, { force: true }); };
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
if (await lockIsStale(lockPath, now)) {
|
|
75
|
+
await rm(lockPath, { force: true });
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
await delay(LOCK_RETRY_MS);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
throw new GoalArchiveError("The goal is being written by another process; try again in a moment.", "locked");
|
|
82
|
+
}
|
|
83
|
+
async function lockIsStale(lockPath, now) {
|
|
84
|
+
try {
|
|
85
|
+
const stats = await stat(lockPath);
|
|
86
|
+
return now().getTime() - stats.mtimeMs > LOCK_STALE_MS;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function findActiveGoalFile(directory, goalId) {
|
|
93
|
+
let entries;
|
|
94
|
+
try {
|
|
95
|
+
entries = await readdir(directory);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (!entry.startsWith("active_goal_") || !entry.endsWith(".md"))
|
|
102
|
+
continue;
|
|
103
|
+
const path = join(directory, entry);
|
|
104
|
+
let content;
|
|
105
|
+
try {
|
|
106
|
+
content = await readFile(path, "utf8");
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const record = parseGoalRecord(content);
|
|
112
|
+
if (record["id"] === goalId)
|
|
113
|
+
return { path, content };
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
function parseGoalRecord(content) {
|
|
118
|
+
const block = extractGoalJsonBlock(content);
|
|
119
|
+
if (block === undefined)
|
|
120
|
+
throw new GoalArchiveError("The goal record has no JSON header to update.", "invalid");
|
|
121
|
+
const parsed = JSON.parse(block);
|
|
122
|
+
if (!isRecord(parsed))
|
|
123
|
+
throw new GoalArchiveError("The goal record's header is not an object.", "invalid");
|
|
124
|
+
return { ...parsed };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Rewrite the header and keep everything after it byte for byte: the prose is
|
|
128
|
+
* where the objective actually lives, so a rewritten body would change the goal
|
|
129
|
+
* while archiving it.
|
|
130
|
+
*/
|
|
131
|
+
function archivedGoalContent(content, record, archive) {
|
|
132
|
+
const block = extractGoalJsonBlock(content);
|
|
133
|
+
if (block === undefined)
|
|
134
|
+
throw new GoalArchiveError("The goal record has no JSON header to update.", "invalid");
|
|
135
|
+
const status = record["status"];
|
|
136
|
+
const nextRecord = {
|
|
137
|
+
...record,
|
|
138
|
+
status: status === "complete" || status === "completed" ? status : "paused",
|
|
139
|
+
stopReason: "user",
|
|
140
|
+
archivedPath: archive.archivedPath,
|
|
141
|
+
updatedAt: archive.at,
|
|
142
|
+
revision: typeof record["revision"] === "number" ? record["revision"] + 1 : 1,
|
|
143
|
+
};
|
|
144
|
+
delete nextRecord["activePath"];
|
|
145
|
+
const body = content.slice(content.indexOf(block) + block.length);
|
|
146
|
+
return `${JSON.stringify(nextRecord, undefined, 2)}${body}`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The ledger is append-only and read line by line, and its own reader tolerates
|
|
150
|
+
* a torn tail. One synchronous append keeps the record whole.
|
|
151
|
+
*/
|
|
152
|
+
function appendGoalEvent(directory, event) {
|
|
153
|
+
try {
|
|
154
|
+
appendFileSync(join(directory, "goal_events.jsonl"), `${JSON.stringify(event)}\n`, "utf8");
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// A missing ledger is not a reason to leave the goal in place; the archived
|
|
158
|
+
// file and the removed active file are the durable part of this operation.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Delete rather than rewrite: a missing snapshot costs one rescan, while a
|
|
163
|
+
* stale one keeps reporting a goal that is no longer open.
|
|
164
|
+
*/
|
|
165
|
+
async function dropPoolSnapshot(home, workspacePath) {
|
|
166
|
+
const candidates = [join(home, ".pi", ".goals-pool-snapshot.json"), join(workspacePath, ".pi", ".goals-pool-snapshot.json")];
|
|
167
|
+
await Promise.all(candidates.map(async (candidate) => { await rm(candidate, { force: true }); }));
|
|
168
|
+
}
|
|
169
|
+
function isRecord(value) {
|
|
170
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
171
|
+
}
|
|
172
|
+
function goalIdForPath(goalId) {
|
|
173
|
+
const safe = goalId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
174
|
+
if (safe === "" || safe.length > 128)
|
|
175
|
+
throw new GoalArchiveError("That goal id cannot be used as a file name.", "invalid");
|
|
176
|
+
return safe;
|
|
177
|
+
}
|
|
178
|
+
function archiveStamp(now) {
|
|
179
|
+
return now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
180
|
+
}
|
|
181
|
+
function delay(ms) {
|
|
182
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=goalArchive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goalArchive.js","sourceRoot":"","sources":["../../../src/server/goals/goalArchive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading goal records written by the `pi-goal-x` extension.
|
|
3
|
+
*
|
|
4
|
+
* A goal file is a Markdown document whose *leading* block is the complete
|
|
5
|
+
* record as pretty-printed JSON, followed by a human-readable rendering:
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* {
|
|
9
|
+
* "version": 3,
|
|
10
|
+
* "id": "...",
|
|
11
|
+
* ...
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* # Goal Prompt
|
|
15
|
+
* ...
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Only the JSON block is authoritative here; the prose below it is a
|
|
19
|
+
* projection of the same fields and is never parsed. The extension owns the
|
|
20
|
+
* format, so this reader is deliberately permissive: unknown fields are
|
|
21
|
+
* ignored and a record missing anything required is skipped rather than
|
|
22
|
+
* failing the whole listing.
|
|
23
|
+
*/
|
|
24
|
+
/** Guard against a runaway or truncated file consuming the parse. */
|
|
25
|
+
const MAX_GOAL_FILE_BYTES = 512 * 1024;
|
|
26
|
+
/**
|
|
27
|
+
* Extract the leading JSON object of a goal file.
|
|
28
|
+
*
|
|
29
|
+
* Brace counting is string- and escape-aware so a brace inside an objective
|
|
30
|
+
* (`"ship {this}"`) cannot end the block early. Returns undefined when the
|
|
31
|
+
* content does not begin with a JSON object, which is the expected outcome for
|
|
32
|
+
* a file caught mid-write.
|
|
33
|
+
*/
|
|
34
|
+
export function extractGoalJsonBlock(content) {
|
|
35
|
+
const start = content.indexOf("{");
|
|
36
|
+
if (start === -1)
|
|
37
|
+
return undefined;
|
|
38
|
+
// Anything before the object other than whitespace means this is not a goal
|
|
39
|
+
// file laid out as expected.
|
|
40
|
+
if (content.slice(0, start).trim() !== "")
|
|
41
|
+
return undefined;
|
|
42
|
+
let depth = 0;
|
|
43
|
+
let inString = false;
|
|
44
|
+
let escaped = false;
|
|
45
|
+
for (let index = start; index < content.length; index += 1) {
|
|
46
|
+
const character = content[index];
|
|
47
|
+
if (inString) {
|
|
48
|
+
if (escaped)
|
|
49
|
+
escaped = false;
|
|
50
|
+
else if (character === "\\")
|
|
51
|
+
escaped = true;
|
|
52
|
+
else if (character === "\"")
|
|
53
|
+
inString = false;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (character === "\"")
|
|
57
|
+
inString = true;
|
|
58
|
+
else if (character === "{")
|
|
59
|
+
depth += 1;
|
|
60
|
+
else if (character === "}") {
|
|
61
|
+
depth -= 1;
|
|
62
|
+
if (depth === 0)
|
|
63
|
+
return content.slice(start, index + 1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse one goal file into the summary the browser renders.
|
|
70
|
+
*
|
|
71
|
+
* Returns undefined for content that is not a usable record: a partial write,
|
|
72
|
+
* a foreign file that happens to live in the directory, or a record without
|
|
73
|
+
* the identity fields the UI needs. Callers skip those rather than surfacing
|
|
74
|
+
* an error, because a goal directory is written concurrently by the extension
|
|
75
|
+
* and a transient unparseable read is normal.
|
|
76
|
+
*/
|
|
77
|
+
export function parseGoalFile(content, filePath) {
|
|
78
|
+
if (content.length > MAX_GOAL_FILE_BYTES)
|
|
79
|
+
return undefined;
|
|
80
|
+
const block = extractGoalJsonBlock(content);
|
|
81
|
+
if (block === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
let record;
|
|
84
|
+
try {
|
|
85
|
+
record = JSON.parse(block);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const fields = asRecord(record);
|
|
91
|
+
if (fields === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
const id = optionalString(fields["id"]);
|
|
94
|
+
const objective = optionalString(fields["objective"]);
|
|
95
|
+
if (id === undefined || objective === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
const tasks = parseTasks(fields["taskList"]);
|
|
98
|
+
return {
|
|
99
|
+
id,
|
|
100
|
+
objective,
|
|
101
|
+
status: optionalString(fields["status"]) ?? "unknown",
|
|
102
|
+
path: filePath,
|
|
103
|
+
sisyphus: fields["sisyphus"] === true,
|
|
104
|
+
autoContinue: fields["autoContinue"] === true,
|
|
105
|
+
...optional("createdAt", optionalString(fields["createdAt"])),
|
|
106
|
+
...optional("updatedAt", optionalString(fields["updatedAt"])),
|
|
107
|
+
...optional("currentTaskId", optionalString(fields["currentTaskId"])),
|
|
108
|
+
...optional("stopReason", optionalString(fields["stopReason"])),
|
|
109
|
+
...optional("pauseReason", optionalString(fields["pauseReason"])),
|
|
110
|
+
...optional("verificationContract", optionalString(fields["verificationContract"])),
|
|
111
|
+
...optional("tokensUsed", optionalNonNegativeNumber(usageField(fields["usage"], "tokensUsed"))),
|
|
112
|
+
...optional("activeSeconds", optionalNonNegativeNumber(usageField(fields["usage"], "activeSeconds"))),
|
|
113
|
+
tasks,
|
|
114
|
+
completedTaskCount: countTasks(tasks, (task) => task.status === "complete"),
|
|
115
|
+
totalTaskCount: countTasks(tasks, () => true),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Order goals for display: unfinished work first, then most recently updated.
|
|
120
|
+
* A completed goal is still listed — it is the record of what was achieved —
|
|
121
|
+
* but never above a goal that still needs attention.
|
|
122
|
+
*/
|
|
123
|
+
export function sortGoalSummaries(goals) {
|
|
124
|
+
return [...goals].sort((left, right) => {
|
|
125
|
+
const leftDone = isFinishedStatus(left.status);
|
|
126
|
+
const rightDone = isFinishedStatus(right.status);
|
|
127
|
+
if (leftDone !== rightDone)
|
|
128
|
+
return leftDone ? 1 : -1;
|
|
129
|
+
return (right.updatedAt ?? "").localeCompare(left.updatedAt ?? "");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function isFinishedStatus(status) {
|
|
133
|
+
return status === "complete" || status === "completed" || status === "abandoned";
|
|
134
|
+
}
|
|
135
|
+
function parseTasks(value) {
|
|
136
|
+
const taskList = asRecord(value);
|
|
137
|
+
if (taskList === undefined)
|
|
138
|
+
return [];
|
|
139
|
+
const list = taskList["tasks"];
|
|
140
|
+
if (!Array.isArray(list))
|
|
141
|
+
return [];
|
|
142
|
+
const tasks = [];
|
|
143
|
+
for (const entry of list) {
|
|
144
|
+
const task = parseTask(entry);
|
|
145
|
+
if (task !== undefined)
|
|
146
|
+
tasks.push(task);
|
|
147
|
+
}
|
|
148
|
+
return tasks;
|
|
149
|
+
}
|
|
150
|
+
function parseTask(value) {
|
|
151
|
+
const fields = asRecord(value);
|
|
152
|
+
if (fields === undefined)
|
|
153
|
+
return undefined;
|
|
154
|
+
const id = optionalString(fields["id"]);
|
|
155
|
+
const title = optionalString(fields["title"]);
|
|
156
|
+
if (id === undefined || title === undefined)
|
|
157
|
+
return undefined;
|
|
158
|
+
const subtasks = Array.isArray(fields["subtasks"])
|
|
159
|
+
? fields["subtasks"].map(parseTask).filter((task) => task !== undefined)
|
|
160
|
+
: [];
|
|
161
|
+
return {
|
|
162
|
+
id,
|
|
163
|
+
title,
|
|
164
|
+
status: optionalString(fields["status"]) ?? "pending",
|
|
165
|
+
...optional("verificationContract", optionalString(fields["verificationContract"])),
|
|
166
|
+
...(subtasks.length === 0 ? {} : { subtasks }),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function countTasks(tasks, predicate) {
|
|
170
|
+
let count = 0;
|
|
171
|
+
for (const task of tasks) {
|
|
172
|
+
if (predicate(task))
|
|
173
|
+
count += 1;
|
|
174
|
+
if (task.subtasks !== undefined)
|
|
175
|
+
count += countTasks(task.subtasks, predicate);
|
|
176
|
+
}
|
|
177
|
+
return count;
|
|
178
|
+
}
|
|
179
|
+
function usageField(usage, key) {
|
|
180
|
+
return asRecord(usage)?.[key];
|
|
181
|
+
}
|
|
182
|
+
/** Narrow an unknown to a plain object without asserting a shape onto it. */
|
|
183
|
+
function asRecord(value) {
|
|
184
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
185
|
+
return undefined;
|
|
186
|
+
return Object.fromEntries(Object.entries(value));
|
|
187
|
+
}
|
|
188
|
+
function optionalString(value) {
|
|
189
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
190
|
+
}
|
|
191
|
+
function optionalNonNegativeNumber(value) {
|
|
192
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
193
|
+
}
|
|
194
|
+
function optional(key, value) {
|
|
195
|
+
const result = {};
|
|
196
|
+
if (value !== undefined)
|
|
197
|
+
result[key] = value;
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=goalFile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goalFile.js","sourceRoot":"","sources":["../../../src/server/goals/goalFile.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parseGoalFile, sortGoalSummaries } from "./goalFile.js";
|
|
4
|
+
/**
|
|
5
|
+
* Directory, relative to a workspace, where `pi-goal-x` keeps its records.
|
|
6
|
+
* Sessions of the same workspace share it, which is exactly why several goals
|
|
7
|
+
* can be open at once.
|
|
8
|
+
*/
|
|
9
|
+
export const GOALS_DIRECTORY = join(".pi", "goals");
|
|
10
|
+
/**
|
|
11
|
+
* Upper bound on files inspected per listing. A goal directory is small by
|
|
12
|
+
* nature; the cap only stops a pathological directory from stalling a request.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_GOAL_FILES = 200;
|
|
15
|
+
/**
|
|
16
|
+
* List the goals recorded under a workspace.
|
|
17
|
+
*
|
|
18
|
+
* The directory is written concurrently by the extension (it keeps a `.locks/`
|
|
19
|
+
* subdirectory for exactly that reason), so this reader treats an unreadable
|
|
20
|
+
* or half-written file as absent rather than as an error: a transient parse
|
|
21
|
+
* failure must not blank the panel for every other goal. A missing directory
|
|
22
|
+
* is simply "no goals", which is the common case.
|
|
23
|
+
*/
|
|
24
|
+
export async function readWorkspaceGoals(workspacePath, now = () => new Date()) {
|
|
25
|
+
const directory = join(workspacePath, GOALS_DIRECTORY);
|
|
26
|
+
const response = (goals) => ({
|
|
27
|
+
goals,
|
|
28
|
+
directory,
|
|
29
|
+
generatedAt: now().toISOString(),
|
|
30
|
+
});
|
|
31
|
+
let entries;
|
|
32
|
+
try {
|
|
33
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return response([]);
|
|
37
|
+
}
|
|
38
|
+
const files = entries
|
|
39
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
|
40
|
+
.slice(0, MAX_GOAL_FILES);
|
|
41
|
+
const goals = [];
|
|
42
|
+
await Promise.all(files.map(async (entry) => {
|
|
43
|
+
const path = join(directory, entry.name);
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = await readFile(path, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const goal = parseGoalFile(content, path);
|
|
52
|
+
if (goal !== undefined)
|
|
53
|
+
goals.push(goal);
|
|
54
|
+
}));
|
|
55
|
+
// Records are keyed by id, but a goal that was archived to a second file
|
|
56
|
+
// would otherwise appear twice; the most recently updated copy wins.
|
|
57
|
+
const byId = new Map();
|
|
58
|
+
for (const goal of goals) {
|
|
59
|
+
const existing = byId.get(goal.id);
|
|
60
|
+
if (existing === undefined || (goal.updatedAt ?? "") > (existing.updatedAt ?? ""))
|
|
61
|
+
byId.set(goal.id, goal);
|
|
62
|
+
}
|
|
63
|
+
return response(sortGoalSummaries([...byId.values()]));
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=goalStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goalStore.js","sourceRoot":"","sources":["../../../src/server/goals/goalStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEjE;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAEpD;;;GAGG;AACH,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,aAAqB,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE;IACpF,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,CAAC,KAA0B,EAA0B,EAAE,CAAC,CAAC;QACxE,KAAK;QACL,SAAS;QACT,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW,EAAE;KACjC,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,KAAK,GAAG,OAAO;SAClB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;SAC/D,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;IAE5B,MAAM,KAAK,GAAwB,EAAE,CAAC;IACtC,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1C,IAAI,IAAI,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC,CAAC;IAEJ,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,IAAI,GAAG,IAAI,GAAG,EAA6B,CAAC;IAClD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IAC7G,CAAC;IAED,OAAO,QAAQ,CAAC,iBAAiB,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { effectivePiWebConfig, maxUploadBytes } from "../config.js";
|
|
3
|
+
import { buildApp } from "./app.js";
|
|
4
|
+
const { config } = effectivePiWebConfig();
|
|
5
|
+
const app = await buildApp({ bodyLimit: maxUploadBytes(process.env, config) });
|
|
6
|
+
await app.listen({ port: config.port ?? 8504, host: config.host ?? "127.0.0.1" });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|