@jmfederico/pi-web 1.202608.0 → 1.202608.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/cli.js +287 -113
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/CodeViewer-BMWwxG7q.js +4 -0
- package/dist/client/assets/{TerminalPanel-fYrKc3jh.js → TerminalPanel-CacQDIYn.js} +6 -6
- package/dist/client/assets/index-DUW2xnoV.js +4279 -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/index.html +3 -3
- package/dist/config.js +110 -73
- package/dist/config.js.map +1 -1
- package/dist/docker/piWebDockerCommandPlan.js +4 -3
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- 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 +374 -71
- package/dist/nativeServices/serviceDoctor.js.map +1 -1
- package/dist/nativeServices/servicePlan.js +1 -1
- package/dist/nativeServices/servicePlan.js.map +1 -1
- package/dist/{pi-web-plugins → pi-packages}/relays/markdownDocument.js +1 -1
- package/dist/pi-packages/relays/package.json +11 -0
- package/dist/{pi-web-plugins → pi-packages}/relays/pi-web-plugin.js +4 -4
- package/dist/pi-packages/relays/prompts/relay-worktree.md +157 -0
- package/dist/pi-packages/relays/prompts/relay.md +152 -0
- package/dist/{pi-web-plugins → pi-packages}/relays/relayDiscovery.js +1 -1
- package/dist/{pi-web-plugins → pi-packages}/relays/relaysPanelElement.js +1 -1
- package/dist/pi-packages/relays/skills/relay/SKILL.md +123 -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/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
- 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 +16 -2
- package/dist/pi-web-plugins/info/package.json +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/updates/package.json +1 -1
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
- package/dist/piWebVersionReport.js +63 -10
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +33 -16
- package/dist/pluginRecoveryCli.js +172 -0
- package/dist/pluginRecoveryCli.js.map +1 -0
- package/dist/server/activity/workspaceActivityService.js +27 -19
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +41 -15
- package/dist/server/app.js.map +1 -1
- package/dist/server/configRoutes.js +3 -21
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/knownAutoInstallPiPackages.js +30 -0
- package/dist/server/knownAutoInstallPiPackages.js.map +1 -0
- package/dist/server/machines/machineClient.js +23 -4
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +165 -8
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +23 -0
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/piPackageIdentity.js +25 -0
- package/dist/server/piPackageIdentity.js.map +1 -0
- package/dist/server/piPackageService.js +69 -8
- package/dist/server/piPackageService.js.map +1 -1
- 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 +159 -261
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +41 -9
- package/dist/server/piWebStatus.js.map +1 -1
- 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/projectService.js +3 -1
- package/dist/server/projects/projectService.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +26 -11
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/requestCancellation.js +32 -0
- package/dist/server/requestCancellation.js.map +1 -0
- package/dist/server/sessiond/agentProcessEnvironment.js +29 -30
- package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -1
- package/dist/server/sessiond/autoInstallPiPackages.js +63 -0
- package/dist/server/sessiond/autoInstallPiPackages.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 +24 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
- package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +2 -1
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- 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 +284 -98
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/authService.js +15 -54
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
- package/dist/server/sessions/modelCatalogRefresher.js +4 -3
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
- package/dist/server/sessions/piSessionManagerGateway.js +18 -67
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +495 -142
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +24 -1
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- 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 +1 -14
- package/dist/server/sessions/sessionFileHeader.js.map +1 -1
- package/dist/server/sessions/sessionModelScope.js +160 -0
- package/dist/server/sessions/sessionModelScope.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +6 -6
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +55 -0
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +96 -224
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +5 -9
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +1 -1
- 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/piPackageDismissalStore.js +78 -0
- package/dist/server/storage/piPackageDismissalStore.js.map +1 -0
- package/dist/server/terminalProxyRoutes.js +2 -1
- package/dist/server/terminalProxyRoutes.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +20 -13
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +18 -9
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- 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/projectWorkspaceCwds.js +0 -5
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
- 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 +1 -3
- package/dist/server/workspaces/workspaceContext.js.map +1 -1
- package/dist/server/workspaces/workspaceDeletionRoutes.js +35 -125
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- 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 +3 -22
- package/dist/sessiond/activeAgentProfile.js.map +1 -1
- package/dist/sessiond/config.js +11 -0
- package/dist/sessiond/config.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +11 -9
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/activeAgentProfile.js +2 -41
- package/dist/shared/activeAgentProfile.js.map +1 -1
- package/dist/shared/activity.js +0 -3
- package/dist/shared/activity.js.map +1 -1
- package/dist/shared/apiTypes.js +7 -5
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +8 -6
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +36 -5
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machinePluginIds.js +4 -2
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/machineStatus.js +94 -0
- package/dist/shared/machineStatus.js.map +1 -0
- package/dist/shared/piWebStatusParsing.js +33 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- 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 +5 -0
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/pluginRecoveryCommands.js +14 -0
- package/dist/shared/pluginRecoveryCommands.js.map +1 -0
- package/dist/shared/workspaceFiles.js +41 -2
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/dist/shared/workspaceRemovalProtocol.js +24 -0
- package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
- package/docs/config.md +105 -52
- package/docs/plugins.md +403 -146
- 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/package.json +30 -11
- package/server-plugin-api.d.ts +1 -0
- package/dist/client/assets/CodeViewer-D91Sp61M.js +0 -4
- package/dist/client/assets/UnifiedDiffViewer-BafLWGtF.js +0 -39
- package/dist/client/assets/index-CA8q9_o7.js +0 -4025
- package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
- package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
- package/dist/pi-web-plugins/relays/package.json +0 -9
- package/dist/plugin-api/unstable.d.ts +0 -22
- package/dist/server/activity/workspaceActivityRoutes.js +0 -4
- package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
- package/dist/server/git/gitService.js.map +0 -1
- package/dist/server/gitRoutes.js +0 -23
- package/dist/server/gitRoutes.js.map +0 -1
- package/dist/server/sessions/parentSessionLocator.js +0 -75
- package/dist/server/sessions/parentSessionLocator.js.map +0 -1
- package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
- package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
- package/dist/server/workspaces/imagePreviewService.js +0 -40
- package/dist/server/workspaces/imagePreviewService.js.map +0 -1
- package/dist/server/workspaces/workspaceService.js +0 -52
- package/dist/server/workspaces/workspaceService.js.map +0 -1
- package/dist/shared/apiTypes.d.ts +0 -1217
- package/dist/shared/thinkingLevels.d.ts +0 -27
- package/plugin-api/unstable.d.ts +0 -1
- /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/README.md +0 -0
- /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/marked.esm.js +0 -0
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ Requirements:
|
|
|
36
36
|
|
|
37
37
|
- Node.js 22.19.0 or newer
|
|
38
38
|
- npm
|
|
39
|
-
- Pi Coding Agent `>=0.
|
|
39
|
+
- Pi Coding Agent `>=0.84.0`, configured for your user
|
|
40
40
|
- git and the development tools your agents need
|
|
41
41
|
|
|
42
42
|
Install and start PI WEB as per-user services:
|
|
@@ -78,7 +78,7 @@ PI WEB organizes work like this:
|
|
|
78
78
|
```text
|
|
79
79
|
Machine a local or remote PI WEB runtime endpoint
|
|
80
80
|
Project a folder on that machine
|
|
81
|
-
Workspace a
|
|
81
|
+
Workspace a provider-owned working folder; bundled Git discovers worktrees, otherwise the project folder
|
|
82
82
|
Session a Pi Coding Agent chat running inside a workspace
|
|
83
83
|
```
|
|
84
84
|
|
|
@@ -110,9 +110,9 @@ Read more: [Fleet and machines guide](https://pi-web.dev/machines)
|
|
|
110
110
|
|
|
111
111
|
## PI WEB plugins
|
|
112
112
|
|
|
113
|
-
PI WEB supports trusted browser
|
|
113
|
+
PI WEB supports trusted browser plugins plus optional sessiond-backed workspace providers. Bundled Git uses the same public provider and backend contracts available to installed plugins. Use **Settings → PI WEB plugins** to manage desired state on the selected machine.
|
|
114
114
|
|
|
115
|
-
Pi packages are a separate Pi package-manager concept. A Pi package may include a PI WEB
|
|
115
|
+
Pi packages are a separate Pi package-manager concept. A Pi package may include a PI WEB plugin, but installing a package and enabling it are different operations. Server-backed changes require a session-daemon restart; see the plugin guide for lifecycle and recovery details.
|
|
116
116
|
|
|
117
117
|
Read more: [PI WEB plugin guide and API](https://pi-web.dev/plugins)
|
|
118
118
|
|
package/dist/cli.js
CHANGED
|
@@ -5,13 +5,18 @@ import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
|
5
5
|
import { homedir, userInfo } from "node:os";
|
|
6
6
|
import { basename, dirname, join, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { defaultPiWebConfigPath, defaultPiWebDataDir,
|
|
9
|
-
import {
|
|
8
|
+
import { defaultPiWebConfigPath, defaultPiWebDataDir, examplePiWebConfig } from "./config.js";
|
|
9
|
+
import { piWebDockerCommand } from "./docker/piWebDockerCommandPlan.js";
|
|
10
|
+
import { ownEnvironmentValue } from "./environment.js";
|
|
11
|
+
import { runPluginRecoveryCli } from "./pluginRecoveryCli.js";
|
|
12
|
+
import { packageVersion, printPiWebVersionReport, probeRunningComponentReady, runningComponentsReady, } from "./piWebVersionReport.js";
|
|
10
13
|
import { checkNodePtyDarwinSpawnHelper, formatNodePtyDarwinSpawnHelperCheck } from "./server/diagnostics/nodePtySpawnHelper.js";
|
|
11
14
|
import { checkNodePtyNativeModule, formatNodePtyNativeModuleCheck } from "./server/diagnostics/nodePtyNativeModule.js";
|
|
12
15
|
import { installNativeServiceCandidate, nativeServiceInstallFailureNeedsPathAdvice, } from "./nativeServices/serviceInstall.js";
|
|
16
|
+
import { launchdBootoutArgs, launchdServiceTarget, orderServices, performServiceAction, serviceStartOrder, serviceStopOrder, settleLaunchdServiceUnload, startLaunchdService, } from "./nativeServices/serviceAction.js";
|
|
13
17
|
import { minimumSupportedNodeVersion, nativeServiceManagerRefs, productionNativeServiceIds, } from "./nativeServices/servicePlan.js";
|
|
14
|
-
import {
|
|
18
|
+
import { inspectInstalledNativeServiceDefinitions, } from "./nativeServices/installedServiceDefinitions.js";
|
|
19
|
+
import { formatNativeServiceDoctorResult, inferInstalledNativeServiceMode, inspectInstalledDevelopmentServiceInput, inspectInstalledProductionServiceContext, runNativeServiceDoctor, selectManagedNativeServiceConfig, } from "./nativeServices/serviceDoctor.js";
|
|
15
20
|
import { createNativeServiceAuthoritativeProbe, nativeServicePrerequisiteShellCheck, } from "./nativeServices/serviceProbe.js";
|
|
16
21
|
import { renderLaunchdPlist, renderSystemdUnit } from "./nativeServices/serviceRendering.js";
|
|
17
22
|
const PI_WEB_PACKAGE_NAME = "@jmfederico/pi-web";
|
|
@@ -24,12 +29,6 @@ const serviceRefs = {
|
|
|
24
29
|
uiDev: { id: "uiDev", ...nativeServiceManagerRefs.uiDev },
|
|
25
30
|
};
|
|
26
31
|
const productionServiceIds = [...productionNativeServiceIds];
|
|
27
|
-
const startServiceOrder = ["sessiond", "web", "uiDev"];
|
|
28
|
-
const stopServiceOrder = ["web", "uiDev", "sessiond"];
|
|
29
|
-
// Restart web/UI before sessiond: when `pi-web restart` runs in a pi-web
|
|
30
|
-
// terminal (owned by sessiond), restarting sessiond kills the command, so any
|
|
31
|
-
// services handled after it would never be restarted.
|
|
32
|
-
const restartServiceOrder = ["web", "uiDev", "sessiond"];
|
|
33
32
|
function platformLabel() {
|
|
34
33
|
if (process.platform === "darwin")
|
|
35
34
|
return "macOS";
|
|
@@ -55,9 +54,6 @@ function requireServiceBackend(command) {
|
|
|
55
54
|
return backend;
|
|
56
55
|
throw new Error(`\`${command}\` requires a supported per-user service manager (systemd user services or LaunchAgents) and is not supported on ${platformLabel()}.\n\n${manualRunAdvice()}`);
|
|
57
56
|
}
|
|
58
|
-
function supportsSystemdUserServices() {
|
|
59
|
-
return currentServiceBackend()?.kind === "systemd";
|
|
60
|
-
}
|
|
61
57
|
function manualRunAdvice() {
|
|
62
58
|
return [
|
|
63
59
|
"Run PI WEB manually from a checkout:",
|
|
@@ -89,6 +85,15 @@ function capture(command, args) {
|
|
|
89
85
|
const stderr = outputText(result.stderr);
|
|
90
86
|
return { status: result.status ?? 1, stdout: outputText(result.stdout), stderr: stderr === "" ? errorMessage : stderr };
|
|
91
87
|
}
|
|
88
|
+
function captureInstalledServiceDefinitionCommand(command, args) {
|
|
89
|
+
const result = spawnSync(command, args);
|
|
90
|
+
return {
|
|
91
|
+
status: result.status ?? 1,
|
|
92
|
+
stdout: result.stdout,
|
|
93
|
+
stderr: result.stderr,
|
|
94
|
+
spawnError: result.error instanceof Error ? result.error.message : "",
|
|
95
|
+
};
|
|
96
|
+
}
|
|
92
97
|
function runQuiet(command, args) {
|
|
93
98
|
return capture(command, args).status;
|
|
94
99
|
}
|
|
@@ -219,21 +224,11 @@ function allServiceRefs() {
|
|
|
219
224
|
function productionServiceRefs() {
|
|
220
225
|
return serviceRefList(productionServiceIds);
|
|
221
226
|
}
|
|
222
|
-
function orderServiceRefs(refs, order) {
|
|
223
|
-
const byId = new Map(refs.map((ref) => [ref.id, ref]));
|
|
224
|
-
return order.flatMap((id) => {
|
|
225
|
-
const ref = byId.get(id);
|
|
226
|
-
return ref === undefined ? [] : [ref];
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
227
|
function startOrder(refs) {
|
|
230
|
-
return
|
|
228
|
+
return orderServices(refs, serviceStartOrder);
|
|
231
229
|
}
|
|
232
230
|
function stopOrder(refs) {
|
|
233
|
-
return
|
|
234
|
-
}
|
|
235
|
-
function restartOrder(refs) {
|
|
236
|
-
return orderServiceRefs(refs, restartServiceOrder);
|
|
231
|
+
return orderServices(refs, serviceStopOrder);
|
|
237
232
|
}
|
|
238
233
|
function devRootPath() {
|
|
239
234
|
return resolve(process.cwd());
|
|
@@ -298,30 +293,30 @@ async function installSystemdServices(plan) {
|
|
|
298
293
|
function launchdDomain() {
|
|
299
294
|
return `gui/${String(userInfo().uid)}`;
|
|
300
295
|
}
|
|
301
|
-
function
|
|
302
|
-
return
|
|
303
|
-
}
|
|
304
|
-
function launchdIsLoaded(ref) {
|
|
305
|
-
return capture("launchctl", ["print", launchdServiceTarget(ref)]).status === 0;
|
|
306
|
-
}
|
|
307
|
-
function launchdBootout(ref) {
|
|
308
|
-
runQuiet("launchctl", ["bootout", launchdServiceTarget(ref)]);
|
|
296
|
+
function currentLaunchdServiceTarget(ref) {
|
|
297
|
+
return launchdServiceTarget(launchdDomain(), ref);
|
|
309
298
|
}
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
run("launchctl", ["enable", launchdServiceTarget(ref)], { check: true });
|
|
299
|
+
function launchdActionContext() {
|
|
300
|
+
return { domain: launchdDomain(), plistPath: launchdPlistPath };
|
|
313
301
|
}
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
302
|
+
function serviceActionDeps(backend, probeEnvironment = process.env) {
|
|
303
|
+
return {
|
|
304
|
+
run: (command, args) => run(command, args),
|
|
305
|
+
runQuiet,
|
|
306
|
+
sleep: (ms) => new Promise((resolve) => {
|
|
307
|
+
setTimeout(resolve, ms);
|
|
308
|
+
}),
|
|
309
|
+
isServiceRunning: (ref) => runtimeStatus(backend, ref).health === "running",
|
|
310
|
+
isComponentReady: (component) => probeRunningComponentReady(component, { configEnv: probeEnvironment }),
|
|
311
|
+
};
|
|
318
312
|
}
|
|
319
313
|
async function installLaunchdServices(plan) {
|
|
320
314
|
const selected = new Set(plan.services.map((service) => service.id));
|
|
321
315
|
await mkdir(launchdServiceDir, { recursive: true });
|
|
322
316
|
await mkdir(logDir, { recursive: true });
|
|
323
|
-
for (const ref of stopOrder(allServiceRefs()))
|
|
324
|
-
|
|
317
|
+
for (const ref of stopOrder(allServiceRefs())) {
|
|
318
|
+
runQuiet("launchctl", launchdBootoutArgs(currentLaunchdServiceTarget(ref)));
|
|
319
|
+
}
|
|
325
320
|
for (const ref of allServiceRefs().filter((candidate) => !selected.has(candidate.id))) {
|
|
326
321
|
await rm(launchdPlistPath(ref), { force: true });
|
|
327
322
|
}
|
|
@@ -329,8 +324,16 @@ async function installLaunchdServices(plan) {
|
|
|
329
324
|
const plistPath = join(launchdServiceDir, service.manager.launchdPlistName);
|
|
330
325
|
await writeFile(plistPath, renderLaunchdPlist(plan, service, logDir));
|
|
331
326
|
}
|
|
332
|
-
|
|
333
|
-
|
|
327
|
+
// Settle each bootout before deciding bootstrap vs kickstart: acting on a
|
|
328
|
+
// label launchd is still asynchronously unloading races the teardown and can
|
|
329
|
+
// lose the service entirely (the same defect the restart path had).
|
|
330
|
+
const context = launchdActionContext();
|
|
331
|
+
const deps = serviceActionDeps(plan.backend);
|
|
332
|
+
for (const service of plan.services) {
|
|
333
|
+
const ref = serviceRefFromPlan(service.id, service.manager);
|
|
334
|
+
await settleLaunchdServiceUnload(currentLaunchdServiceTarget(ref), deps);
|
|
335
|
+
startLaunchdService(ref, context, deps);
|
|
336
|
+
}
|
|
334
337
|
}
|
|
335
338
|
async function installNativeServices(plan) {
|
|
336
339
|
if (plan.backend.kind === "systemd")
|
|
@@ -350,7 +353,7 @@ async function uninstallSystemdServices() {
|
|
|
350
353
|
}
|
|
351
354
|
async function uninstallLaunchdServices() {
|
|
352
355
|
for (const ref of stopOrder(allServiceRefs())) {
|
|
353
|
-
|
|
356
|
+
runQuiet("launchctl", launchdBootoutArgs(currentLaunchdServiceTarget(ref)));
|
|
354
357
|
await rm(launchdPlistPath(ref), { force: true });
|
|
355
358
|
}
|
|
356
359
|
}
|
|
@@ -444,7 +447,7 @@ export function launchdRuntimeDetails(output) {
|
|
|
444
447
|
return { state, detail, pid };
|
|
445
448
|
}
|
|
446
449
|
function launchdRuntimeStatus(backend, ref) {
|
|
447
|
-
const target =
|
|
450
|
+
const target = currentLaunchdServiceTarget(ref);
|
|
448
451
|
const filePath = serviceFilePath(backend, ref);
|
|
449
452
|
if (!serviceFileExists(backend, ref))
|
|
450
453
|
return makeServiceRuntimeStatus(ref, "not-installed", "not installed", target, filePath);
|
|
@@ -610,40 +613,85 @@ async function uninstall() {
|
|
|
610
613
|
await uninstallNativeServices(backend);
|
|
611
614
|
console.log(`PI WEB ${backend.label} removed. Production and development service files were removed; config and data were left in place.`);
|
|
612
615
|
}
|
|
613
|
-
function
|
|
614
|
-
const orderedRefs = action === "stop" ? stopOrder(refs) : action === "restart" ? restartOrder(refs) : startOrder(refs);
|
|
615
|
-
run("systemctl", ["--user", action, ...orderedRefs.map((ref) => ref.systemdName)], { check: true });
|
|
616
|
-
}
|
|
617
|
-
function launchdServiceAction(action, refs) {
|
|
618
|
-
if (action === "stop") {
|
|
619
|
-
for (const ref of stopOrder(refs))
|
|
620
|
-
launchdBootout(ref);
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
if (action === "restart") {
|
|
624
|
-
// Restart each service fully (bootout + start) before moving to the next,
|
|
625
|
-
// so the web/UI services are back up before sessiond is restarted.
|
|
626
|
-
for (const ref of restartOrder(refs)) {
|
|
627
|
-
launchdBootout(ref);
|
|
628
|
-
launchdStart(ref);
|
|
629
|
-
}
|
|
630
|
-
return;
|
|
631
|
-
}
|
|
632
|
-
for (const ref of startOrder(refs))
|
|
633
|
-
launchdStart(ref);
|
|
634
|
-
}
|
|
635
|
-
function serviceAction(action) {
|
|
616
|
+
async function serviceAction(action) {
|
|
636
617
|
const backend = requireServiceBackend(`pi-web ${action}`);
|
|
637
618
|
if (action === "status") {
|
|
638
619
|
if (!printServiceStatusReport(backend))
|
|
639
620
|
process.exitCode = 1;
|
|
640
621
|
return;
|
|
641
622
|
}
|
|
623
|
+
await performLifecycleServiceAction(backend, action, process.env);
|
|
624
|
+
}
|
|
625
|
+
async function performLifecycleServiceAction(backend, action, probeEnvironment) {
|
|
642
626
|
const refs = installedServiceRefs(backend);
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
627
|
+
const input = { backend, action, refs, launchdContext: launchdActionContext() };
|
|
628
|
+
const result = await performServiceAction(input, serviceActionDeps(backend, probeEnvironment));
|
|
629
|
+
if (result.unreadyServices.length > 0) {
|
|
630
|
+
for (const ref of result.unreadyServices) {
|
|
631
|
+
const target = backend.kind === "systemd" ? ref.systemdName : currentLaunchdServiceTarget(ref);
|
|
632
|
+
console.log(`✗ ${serviceDisplayName(ref)} did not become ready (${target}).`);
|
|
633
|
+
}
|
|
634
|
+
console.log(" Check `pi-web status` and `pi-web logs` for details.");
|
|
635
|
+
process.exitCode = 1;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/** Offline restart guidance used only after a recovery config mutation. */
|
|
639
|
+
export function sessionDaemonRestartPlan(options = {}) {
|
|
640
|
+
const env = options.env ?? process.env;
|
|
641
|
+
if (options.explicitConfigPath === true && options.configPath !== undefined) {
|
|
642
|
+
return {
|
|
643
|
+
kind: "manual",
|
|
644
|
+
guidance: `Restart the session daemon process configured with PI_WEB_CONFIG=${JSON.stringify(options.configPath)}.`,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const dockerMode = activeDockerMode(env);
|
|
648
|
+
const runCommand = options.runCommand ?? ((command, args) => { run(command, args, { check: true }); });
|
|
649
|
+
if (dockerMode !== undefined) {
|
|
650
|
+
const command = piWebDockerCommand(dockerMode, "restart-sessiond");
|
|
651
|
+
return {
|
|
652
|
+
kind: "automatic",
|
|
653
|
+
command,
|
|
654
|
+
guidance: "Run the Docker control command from the PI WEB Docker host.",
|
|
655
|
+
perform: () => {
|
|
656
|
+
runCommand("pi-web-docker", [...(dockerMode === "dev" ? ["--dev"] : []), "restart-sessiond"]);
|
|
657
|
+
},
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
const backend = serviceBackendForPlatform(options.platform ?? process.platform);
|
|
661
|
+
const fileExists = options.serviceFileExists ?? existsSync;
|
|
662
|
+
if (backend?.kind === "systemd" && fileExists(systemdServicePath(serviceRefs.sessiond))) {
|
|
663
|
+
const args = ["--user", "restart", serviceRefs.sessiond.systemdName];
|
|
664
|
+
return {
|
|
665
|
+
kind: "automatic",
|
|
666
|
+
command: `systemctl ${args.join(" ")}`,
|
|
667
|
+
guidance: "Run:",
|
|
668
|
+
perform: () => { runCommand("systemctl", args); },
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
if (backend?.kind === "launchd" && fileExists(launchdPlistPath(serviceRefs.sessiond))) {
|
|
672
|
+
const target = `gui/${String(options.uid ?? userInfo().uid)}/${serviceRefs.sessiond.launchdLabel}`;
|
|
673
|
+
const args = ["kickstart", "-k", target];
|
|
674
|
+
return {
|
|
675
|
+
kind: "automatic",
|
|
676
|
+
command: `launchctl ${args.join(" ")}`,
|
|
677
|
+
guidance: "Run:",
|
|
678
|
+
perform: () => { runCommand("launchctl", args); },
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return {
|
|
682
|
+
kind: "manual",
|
|
683
|
+
guidance: "Stop and rerun the process that owns sessiond (from a checkout: `npm run start:sessiond`).",
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function activeDockerMode(env) {
|
|
687
|
+
if (!truthyEnvValue(ownEnvironmentValue(env, "PI_WEB_DOCKER_RUNTIME")))
|
|
688
|
+
return undefined;
|
|
689
|
+
if (ownEnvironmentValue(env, "PI_WEB_DOCKER_MODE") === "dev")
|
|
690
|
+
return "dev";
|
|
691
|
+
return "runtime";
|
|
692
|
+
}
|
|
693
|
+
function truthyEnvValue(value) {
|
|
694
|
+
return value !== undefined && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
647
695
|
}
|
|
648
696
|
function logs() {
|
|
649
697
|
const backend = requireServiceBackend("pi-web logs");
|
|
@@ -677,16 +725,14 @@ export function nodeVersionCheck() {
|
|
|
677
725
|
description: `node >= ${minimumSupportedNodeVersion}`,
|
|
678
726
|
});
|
|
679
727
|
}
|
|
680
|
-
export function
|
|
681
|
-
return effectivePiWebConfig({ env }).config.agent.command;
|
|
682
|
-
}
|
|
683
|
-
function generalDoctorChecks() {
|
|
728
|
+
export function generalDoctorChecks() {
|
|
684
729
|
const shell = serviceShellLabel();
|
|
685
|
-
const agentCommand = agentCommandForChecks();
|
|
686
730
|
return [
|
|
687
731
|
[`Caller login ${shell} can find node >= ${minimumSupportedNodeVersion}`, serviceShellCommand(nodeVersionCheck())],
|
|
688
732
|
[`Caller login ${shell} can find npm`, serviceShellCommand(commandWithVersionCheck("npm"))],
|
|
689
|
-
|
|
733
|
+
// Sessions run on the bundled pi SDK; the only CLI check is a hardcoded
|
|
734
|
+
// `pi` PATH probe — nothing names or locates a configurable command.
|
|
735
|
+
[`Caller login ${shell} can find pi`, serviceShellCommand(commandWithVersionCheck("pi"))],
|
|
690
736
|
];
|
|
691
737
|
}
|
|
692
738
|
function runChecks(checks) {
|
|
@@ -734,14 +780,115 @@ function printOptionalDoctorChecks() {
|
|
|
734
780
|
console.log(" PI WEB falls back to a bounded filesystem scan when rg is unavailable.");
|
|
735
781
|
}
|
|
736
782
|
}
|
|
737
|
-
function installedServiceDefinitions(backend, ids) {
|
|
738
|
-
|
|
783
|
+
function installedServiceDefinitions(backend, ids, purpose) {
|
|
784
|
+
const sources = ids.map((id) => ({
|
|
739
785
|
id,
|
|
740
|
-
|
|
786
|
+
path: serviceFilePath(backend, serviceRefs[id]),
|
|
787
|
+
systemdName: serviceRefs[id].systemdName,
|
|
788
|
+
launchdTarget: currentLaunchdServiceTarget(serviceRefs[id]),
|
|
741
789
|
}));
|
|
790
|
+
return inspectInstalledNativeServiceDefinitions(backend, sources, {
|
|
791
|
+
readFile: (path) => readFileSync(path),
|
|
792
|
+
realpath: realpathSync,
|
|
793
|
+
capture: captureInstalledServiceDefinitionCommand,
|
|
794
|
+
}, purpose);
|
|
795
|
+
}
|
|
796
|
+
/** Build an isolated probe environment without mutating the caller or process. */
|
|
797
|
+
export function managedServiceProbeEnvironment(backend, definitions, callerEnvironment) {
|
|
798
|
+
const selection = selectManagedNativeServiceConfig(backend, definitions, ownEnvironmentValue(callerEnvironment, "PI_WEB_CONFIG"));
|
|
799
|
+
if (!selection.ok)
|
|
800
|
+
return selection;
|
|
801
|
+
if (selection.value.source === "installed") {
|
|
802
|
+
return {
|
|
803
|
+
ok: true,
|
|
804
|
+
value: { ...callerEnvironment, PI_WEB_CONFIG: selection.value.configPath },
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
if (selection.value.source === "default" && definitions.length > 0) {
|
|
808
|
+
// An own empty value preserves default resolution while masking any
|
|
809
|
+
// prototype-inherited PI_WEB_CONFIG from downstream environment lookups.
|
|
810
|
+
return { ok: true, value: { ...callerEnvironment, PI_WEB_CONFIG: "" } };
|
|
811
|
+
}
|
|
812
|
+
return { ok: true, value: callerEnvironment };
|
|
813
|
+
}
|
|
814
|
+
function installedServiceProbeEnvironment(backend, ids, callerEnvironment, purpose, inspectDefinitions) {
|
|
815
|
+
// The explicit caller override wins without requiring installed definitions
|
|
816
|
+
// to be readable or canonical.
|
|
817
|
+
const callerConfigPath = ownEnvironmentValue(callerEnvironment, "PI_WEB_CONFIG");
|
|
818
|
+
if (callerConfigPath !== undefined && callerConfigPath !== "") {
|
|
819
|
+
return { ok: true, value: callerEnvironment };
|
|
820
|
+
}
|
|
821
|
+
if (ids.size === 0)
|
|
822
|
+
return { ok: true, value: callerEnvironment };
|
|
823
|
+
const definitions = inspectDefinitions(backend, [...ids], purpose);
|
|
824
|
+
if (!definitions.ok)
|
|
825
|
+
return definitions;
|
|
826
|
+
return managedServiceProbeEnvironment(backend, definitions.value, callerEnvironment);
|
|
742
827
|
}
|
|
743
|
-
function
|
|
744
|
-
|
|
828
|
+
function managedServiceProbeFailure(backend, message) {
|
|
829
|
+
return [
|
|
830
|
+
`Could not select managed PI_WEB_CONFIG from installed ${backend.label}: ${message}`,
|
|
831
|
+
"Repair the reported service-manager override or definition, then re-run `pi-web install` (or `pi-web install --dev`); a nonempty PI_WEB_CONFIG explicitly overrides managed config selection for this command.",
|
|
832
|
+
].join("\n");
|
|
833
|
+
}
|
|
834
|
+
/**
|
|
835
|
+
* Dispatch commands whose probes may need managed config. The service-manager,
|
|
836
|
+
* definition, lifecycle, doctor, and version boundaries are injected so this
|
|
837
|
+
* exact orchestration can be tested without mutating live services or process
|
|
838
|
+
* environment.
|
|
839
|
+
*/
|
|
840
|
+
export async function runReadinessCliCommand(command, dependencies) {
|
|
841
|
+
if (command === "version") {
|
|
842
|
+
await dependencies.printVersion();
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const environment = dependencies.environment;
|
|
846
|
+
if (command === "start" || command === "restart") {
|
|
847
|
+
const backend = dependencies.requireBackend(`pi-web ${command}`);
|
|
848
|
+
const installedIds = dependencies.installedServiceIds(backend);
|
|
849
|
+
const probeEnvironment = installedServiceProbeEnvironment(backend, installedIds, environment, command, dependencies.inspectDefinitions);
|
|
850
|
+
if (!probeEnvironment.ok)
|
|
851
|
+
throw new Error(managedServiceProbeFailure(backend, probeEnvironment.message));
|
|
852
|
+
await dependencies.runLifecycle(backend, command, probeEnvironment.value);
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const backend = dependencies.currentBackend();
|
|
856
|
+
const installedIds = backend === undefined
|
|
857
|
+
? new Set()
|
|
858
|
+
: dependencies.installedServiceIds(backend);
|
|
859
|
+
let versionReportOptions = { configEnv: environment };
|
|
860
|
+
let managedInspectionFailed = false;
|
|
861
|
+
if (backend !== undefined && activeDockerMode(environment) === undefined) {
|
|
862
|
+
const probeEnvironment = installedServiceProbeEnvironment(backend, installedIds, environment, "doctor", dependencies.inspectDefinitions);
|
|
863
|
+
if (probeEnvironment.ok) {
|
|
864
|
+
versionReportOptions = { configEnv: probeEnvironment.value };
|
|
865
|
+
}
|
|
866
|
+
else {
|
|
867
|
+
managedInspectionFailed = true;
|
|
868
|
+
versionReportOptions = { webEndpointError: managedServiceProbeFailure(backend, probeEnvironment.message) };
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
await dependencies.runDoctor({
|
|
872
|
+
backend,
|
|
873
|
+
installedIds,
|
|
874
|
+
environment,
|
|
875
|
+
versionReportOptions,
|
|
876
|
+
managedInspectionFailed,
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
function readinessCliCommandDependencies() {
|
|
880
|
+
return {
|
|
881
|
+
environment: process.env,
|
|
882
|
+
currentBackend: currentServiceBackend,
|
|
883
|
+
requireBackend: requireServiceBackend,
|
|
884
|
+
installedServiceIds,
|
|
885
|
+
inspectDefinitions: installedServiceDefinitions,
|
|
886
|
+
runLifecycle: performLifecycleServiceAction,
|
|
887
|
+
runDoctor: doctor,
|
|
888
|
+
printVersion: async () => { await printPiWebVersionReport(); },
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
function nativeServiceDoctorTarget(backend, ids) {
|
|
745
892
|
const mode = inferInstalledNativeServiceMode(ids);
|
|
746
893
|
if (mode === "ambiguous") {
|
|
747
894
|
return {
|
|
@@ -766,23 +913,16 @@ function nativeServiceDoctorTarget(backend) {
|
|
|
766
913
|
message: `installed ${mode} service set is incomplete; ${missingId} is missing.`,
|
|
767
914
|
};
|
|
768
915
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
773
|
-
catch (error) {
|
|
774
|
-
return {
|
|
775
|
-
kind: "inspection-failure",
|
|
776
|
-
message: error instanceof Error ? error.message : String(error),
|
|
777
|
-
};
|
|
778
|
-
}
|
|
916
|
+
const definitions = installedServiceDefinitions(backend, expectedIds, "doctor");
|
|
917
|
+
if (!definitions.ok)
|
|
918
|
+
return { kind: "inspection-failure", message: definitions.message };
|
|
779
919
|
if (mode === "development") {
|
|
780
|
-
const inspection = inspectInstalledDevelopmentServiceInput(backend, definitions);
|
|
920
|
+
const inspection = inspectInstalledDevelopmentServiceInput(backend, definitions.value);
|
|
781
921
|
return inspection.ok
|
|
782
922
|
? { kind: "installed-development", input: inspection.value }
|
|
783
923
|
: { kind: "inspection-failure", message: inspection.message };
|
|
784
924
|
}
|
|
785
|
-
const inspection = inspectInstalledProductionServiceContext(backend, definitions);
|
|
925
|
+
const inspection = inspectInstalledProductionServiceContext(backend, definitions.value);
|
|
786
926
|
return inspection.ok
|
|
787
927
|
? {
|
|
788
928
|
kind: "prospective-production",
|
|
@@ -791,8 +931,8 @@ function nativeServiceDoctorTarget(backend) {
|
|
|
791
931
|
}
|
|
792
932
|
: { kind: "inspection-failure", message: inspection.message };
|
|
793
933
|
}
|
|
794
|
-
async function printNativeServiceDoctorChecks(backend) {
|
|
795
|
-
const result = await runNativeServiceDoctor(nativeServiceDoctorTarget(backend), {
|
|
934
|
+
async function printNativeServiceDoctorChecks(backend, installedIds) {
|
|
935
|
+
const result = await runNativeServiceDoctor(nativeServiceDoctorTarget(backend, installedIds), {
|
|
796
936
|
probe: createNativeServiceAuthoritativeProbe(),
|
|
797
937
|
fileExists: regularFileExists,
|
|
798
938
|
});
|
|
@@ -841,11 +981,30 @@ function printPathSetupAdvice(shell = detectServiceShell()) {
|
|
|
841
981
|
console.log(" Avoid relying on prompt hooks; PI WEB services run non-interactive login shells.");
|
|
842
982
|
}
|
|
843
983
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
984
|
+
/**
|
|
985
|
+
* Running components an installed deployment must keep ready: the API-serving
|
|
986
|
+
* component whenever the web or UI dev service file is installed (the version
|
|
987
|
+
* endpoint probes the API port in both modes), and the session daemon whenever
|
|
988
|
+
* its service file is installed. Docker deployments have no native service
|
|
989
|
+
* files, so the Docker runtime marker expects both components. A machine with
|
|
990
|
+
* nothing installed expects nothing: doctor's manual-run guidance stays a
|
|
991
|
+
* passing outcome there.
|
|
992
|
+
*/
|
|
993
|
+
export function expectedRunningComponents(installedServices, env) {
|
|
994
|
+
if (activeDockerMode(env) !== undefined)
|
|
995
|
+
return ["web", "sessiond"];
|
|
996
|
+
const expected = [];
|
|
997
|
+
if (installedServices.has("web") || installedServices.has("uiDev"))
|
|
998
|
+
expected.push("web");
|
|
999
|
+
if (installedServices.has("sessiond"))
|
|
1000
|
+
expected.push("sessiond");
|
|
1001
|
+
return expected;
|
|
1002
|
+
}
|
|
1003
|
+
export function doctorExitCode(generalReadinessOk, nativeServicePlanOk, nodePtyRuntimeOk, runningComponentsOk) {
|
|
1004
|
+
return generalReadinessOk && nativeServicePlanOk && nodePtyRuntimeOk && runningComponentsOk ? 0 : 1;
|
|
1005
|
+
}
|
|
1006
|
+
async function doctor(context) {
|
|
1007
|
+
const { backend, installedIds, environment, versionReportOptions, managedInspectionFailed } = context;
|
|
849
1008
|
console.log(`Platform: ${platformLabel()}`);
|
|
850
1009
|
console.log(`Service backend: ${backend?.label ?? "manual run only"}`);
|
|
851
1010
|
console.log(`Service shell: ${describeServiceShell()}`);
|
|
@@ -853,7 +1012,13 @@ async function doctor() {
|
|
|
853
1012
|
console.log(`- Native user service plan checks skipped on ${platformLabel()}; no native-service drift is reported.`);
|
|
854
1013
|
}
|
|
855
1014
|
console.log("");
|
|
856
|
-
await printPiWebVersionReport();
|
|
1015
|
+
const runningVersionInfo = await printPiWebVersionReport(versionReportOptions);
|
|
1016
|
+
const expectedComponents = expectedRunningComponents(installedIds, environment);
|
|
1017
|
+
const runningComponentsOk = runningComponentsReady(runningVersionInfo, expectedComponents);
|
|
1018
|
+
if (!runningComponentsOk) {
|
|
1019
|
+
console.log("\n✗ Installed PI WEB services are unavailable or stale (see \"Running services\" above).");
|
|
1020
|
+
console.log(" Run `pi-web restart`, then check `pi-web status`.");
|
|
1021
|
+
}
|
|
857
1022
|
console.log("\nGeneral login-shell readiness (separate from native-service requirements):");
|
|
858
1023
|
const generalReadinessOk = runChecks(generalDoctorChecks());
|
|
859
1024
|
printOptionalDoctorChecks();
|
|
@@ -863,9 +1028,15 @@ async function doctor() {
|
|
|
863
1028
|
let nativeServiceReport = null;
|
|
864
1029
|
if (backend !== undefined) {
|
|
865
1030
|
console.log("\nNative service plan checks (service-manager context):");
|
|
866
|
-
|
|
1031
|
+
if (managedInspectionFailed) {
|
|
1032
|
+
console.log("✗ Installed native-service plan could not be inspected consistently with the readiness probe (see \"Running services\" above).");
|
|
1033
|
+
console.log(" Repair the reported service-manager definition before relying on doctor results.");
|
|
1034
|
+
}
|
|
1035
|
+
else {
|
|
1036
|
+
nativeServiceReport = await printNativeServiceDoctorChecks(backend, installedIds);
|
|
1037
|
+
}
|
|
867
1038
|
}
|
|
868
|
-
if (
|
|
1039
|
+
if (backend?.kind === "systemd") {
|
|
869
1040
|
const linger = isLingerEnabled();
|
|
870
1041
|
if (linger === true) {
|
|
871
1042
|
console.log("✓ systemd user lingering enabled");
|
|
@@ -885,7 +1056,7 @@ async function doctor() {
|
|
|
885
1056
|
else {
|
|
886
1057
|
console.log(`- systemd user lingering skipped on ${platformLabel()}`);
|
|
887
1058
|
}
|
|
888
|
-
const nativeServicePlanOk = nativeServiceReport?.ok ?? true;
|
|
1059
|
+
const nativeServicePlanOk = !managedInspectionFailed && (nativeServiceReport?.ok ?? true);
|
|
889
1060
|
const pathFailure = !generalReadinessOk || nativeServiceReport?.pathAdviceRecommended === true;
|
|
890
1061
|
if (pathFailure) {
|
|
891
1062
|
console.log("\nIf a command works in your terminal but fails in the service-manager check, compare the caller and manager contexts above.");
|
|
@@ -897,7 +1068,7 @@ async function doctor() {
|
|
|
897
1068
|
if (generalReadinessOk && backend === undefined) {
|
|
898
1069
|
console.log(`\n${manualRunAdvice()}`);
|
|
899
1070
|
}
|
|
900
|
-
if (doctorExitCode(generalReadinessOk, nativeServicePlanOk, nodePtyNativeModuleOk && nodePtySpawnHelperOk) !== 0)
|
|
1071
|
+
if (doctorExitCode(generalReadinessOk, nativeServicePlanOk, nodePtyNativeModuleOk && nodePtySpawnHelperOk, runningComponentsOk) !== 0)
|
|
901
1072
|
process.exitCode = 1;
|
|
902
1073
|
}
|
|
903
1074
|
function printNodePtyNativeModuleCheck() {
|
|
@@ -922,6 +1093,8 @@ Usage:
|
|
|
922
1093
|
pi-web install [--dev] [--host 127.0.0.1] [--port 8504] [--config ~/.config/pi-web/config.json]
|
|
923
1094
|
pi-web uninstall
|
|
924
1095
|
pi-web start|stop|restart|status|logs
|
|
1096
|
+
pi-web plugins disable <plugin-id> [--config <path>] [--restart]
|
|
1097
|
+
pi-web plugins safe-start show|clear|set <bundled-only|none> [--config <path>] [--restart]
|
|
925
1098
|
pi-web doctor
|
|
926
1099
|
pi-web version
|
|
927
1100
|
|
|
@@ -939,14 +1112,15 @@ async function main() {
|
|
|
939
1112
|
await install(args);
|
|
940
1113
|
else if (command === "uninstall")
|
|
941
1114
|
await uninstall();
|
|
942
|
-
else if (command === "start" || command === "
|
|
943
|
-
|
|
1115
|
+
else if (command === "start" || command === "restart" || command === "doctor" || command === "version") {
|
|
1116
|
+
await runReadinessCliCommand(command, readinessCliCommandDependencies());
|
|
1117
|
+
}
|
|
1118
|
+
else if (command === "stop" || command === "status")
|
|
1119
|
+
await serviceAction(command);
|
|
944
1120
|
else if (command === "logs")
|
|
945
1121
|
logs();
|
|
946
|
-
else if (command === "
|
|
947
|
-
|
|
948
|
-
else if (command === "version")
|
|
949
|
-
await printPiWebVersionReport();
|
|
1122
|
+
else if (command === "plugins")
|
|
1123
|
+
runPluginRecoveryCli(args, { restartPlan: (context) => sessionDaemonRestartPlan(context) });
|
|
950
1124
|
else if (command === "--version" || command === "-v")
|
|
951
1125
|
console.log(packageVersion());
|
|
952
1126
|
else if (command === "help" || command === "--help" || command === "-h")
|