@chainingintention/pi-web-cn 2.0.0 → 2.202610.0
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 +61 -106
- package/dist/.plugins-ready +1 -0
- package/dist/cli.js +331 -174
- package/dist/cli.js.map +1 -1
- package/dist/client/apple-touch-icon-dev.png +0 -0
- package/dist/client/assets/PiWebApp-BXSziMEg.js +4921 -0
- package/dist/client/assets/classic-CyM97Akm.css +54 -0
- package/dist/client/assets/external-B9K8duyP.js +2 -0
- package/dist/client/assets/graphite-DquaqFXU.css +54 -0
- package/dist/client/assets/index-cdtUOfOW.js +2 -0
- package/dist/client/assets/paper-CB1oLuRr.css +54 -0
- package/dist/client/assets/pi-web-dark-D_KtbM95.css +54 -0
- package/dist/client/assets/pi-web-light-DSNLMIBI.css +54 -0
- package/dist/client/assets/vendor-editor-core-BfnHL1Dg.js +10 -0
- package/dist/client/assets/vendor-editor-languages-bNquDJP0.js +28 -0
- package/dist/client/favicon-dev.svg +8 -0
- package/dist/client/index.html +7 -51
- package/dist/client/manifest.webmanifest +2 -1
- package/dist/client/pwa-icon-dev-192.png +0 -0
- package/dist/client/pwa-icon-dev-512.png +0 -0
- package/dist/config.js +55 -8
- package/dist/config.js.map +1 -1
- package/dist/docker/piWebDockerCommandPlan.js +13 -4
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- package/dist/environment.js +4 -0
- package/dist/environment.js.map +1 -0
- package/dist/localization.js +24 -0
- package/dist/localization.js.map +1 -0
- package/dist/nativeServices/installedServiceDefinitions.js +617 -0
- package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
- package/dist/nativeServices/serviceAction.js +78 -20
- package/dist/nativeServices/serviceAction.js.map +1 -1
- package/dist/nativeServices/serviceDoctor.js +418 -114
- package/dist/nativeServices/serviceDoctor.js.map +1 -1
- package/dist/nativeServices/servicePlan.js +2 -2
- package/dist/nativeServices/servicePlan.js.map +1 -1
- package/dist/pi-packages/captains-log/README.md +11 -0
- package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
- package/dist/pi-packages/captains-log/dist/browser/index.js +143 -0
- package/dist/pi-packages/captains-log/dist/browser/markdown.js +68 -0
- package/dist/pi-packages/captains-log/dist/browser/panel.js +148 -0
- package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
- package/dist/pi-packages/captains-log/dist/companion.js +126 -0
- package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
- package/dist/pi-packages/captains-log/dist/server.js +210 -0
- package/dist/pi-packages/captains-log/dist/store.js +75 -0
- package/dist/pi-packages/captains-log/docs/usage.md +29 -0
- package/dist/pi-packages/captains-log/i18n.js +7 -0
- package/dist/pi-packages/captains-log/package.json +35 -0
- package/dist/pi-packages/captains-log/tsconfig.json +21 -0
- package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
- package/dist/pi-packages/relays/extensions/index.js +61 -0
- package/dist/pi-packages/relays/i18n.js +7 -0
- package/dist/{pi-web-plugins → pi-packages}/relays/markdownDocument.js +3 -2
- package/dist/pi-packages/relays/package.json +20 -0
- package/dist/pi-packages/relays/pi-web-plugin.js +96 -0
- package/dist/pi-packages/relays/prompts/relay-worktree.md +18 -0
- package/dist/pi-packages/relays/prompts/relay.md +18 -0
- package/dist/{pi-web-plugins → pi-packages}/relays/relayDiscovery.js +2 -2
- package/dist/pi-packages/relays/relayDispatch.js +67 -0
- package/dist/pi-packages/relays/relayIdentity.js +37 -0
- package/dist/{pi-web-plugins → pi-packages}/relays/relaysPanelElement.js +98 -59
- package/dist/pi-packages/relays/skills/relay/SKILL.md +50 -0
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +74 -0
- package/dist/pi-packages/workflows/browser/pi-web-plugin.js +42 -0
- package/dist/pi-packages/workflows/browser/worktreeDialog.js +170 -0
- package/dist/pi-packages/workflows/package.json +23 -0
- package/dist/pi-packages/workflows/prompts/worktree.md +21 -0
- package/dist/pi-packages/workflows/server-plugin.js +586 -0
- package/dist/pi-packages/workflows/vite.config.mjs +17 -0
- package/dist/pi-web-plugins/code-highlight/browser/pi-web-plugin.js +22 -0
- package/dist/pi-web-plugins/code-highlight/package.json +13 -0
- package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +466 -0
- package/dist/pi-web-plugins/files/package.json +15 -0
- package/dist/pi-web-plugins/git/browser/git-contract.js +22 -11
- package/dist/pi-web-plugins/git/browser/git-panel.js +228 -100
- package/dist/pi-web-plugins/git/browser/i18n.js +7 -0
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +28 -4
- package/dist/pi-web-plugins/git/git-backend.js +61 -21
- package/dist/pi-web-plugins/git/gitStatusCache.js +48 -0
- package/dist/pi-web-plugins/git/i18n.js +7 -0
- package/dist/pi-web-plugins/git/package.json +1 -0
- package/dist/pi-web-plugins/git/server-plugin.js +45 -24
- package/dist/pi-web-plugins/git/workspace-discovery-notify.cjs +14 -0
- package/dist/pi-web-plugins/git/workspaceDiscoveryHooks.js +186 -0
- package/dist/pi-web-plugins/info/i18n.js +7 -0
- package/dist/pi-web-plugins/info/infoInternals.js +76 -65
- package/dist/pi-web-plugins/info/package.json +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +40 -42
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +4255 -0
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +11 -0
- package/dist/pi-web-plugins/mermaid/package.json +13 -0
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +219 -0
- package/dist/pi-web-plugins/terminal/i18n.js +7 -0
- package/dist/pi-web-plugins/terminal/package.json +16 -0
- package/dist/pi-web-plugins/terminal/server-plugin.js +367 -0
- package/dist/pi-web-plugins/terminal/terminalService.js +436 -0
- package/dist/pi-web-plugins/updates/i18n.js +7 -0
- package/dist/pi-web-plugins/updates/package.json +1 -1
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +143 -140
- package/dist/pi-web-plugins/updates/updatesLogic.js +24 -24
- package/dist/pi-web-plugins/workspace-tasks/config.js +12 -11
- package/dist/pi-web-plugins/workspace-tasks/i18n.js +7 -0
- package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +31 -9
- package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +54 -36
- package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +12 -11
- package/dist/pi-web-plugins/zh-cn/auth.json +9 -0
- package/dist/pi-web-plugins/zh-cn/bundled.json +219 -0
- package/dist/pi-web-plugins/zh-cn/cli.json +230 -0
- package/dist/pi-web-plugins/zh-cn/client.json +225 -0
- package/dist/pi-web-plugins/zh-cn/components.json +1252 -0
- package/dist/pi-web-plugins/zh-cn/config-errors.json +188 -0
- package/dist/pi-web-plugins/zh-cn/files.json +50 -0
- package/dist/pi-web-plugins/zh-cn/machines.json +28 -0
- package/dist/pi-web-plugins/zh-cn/main.json +459 -0
- package/dist/pi-web-plugins/zh-cn/management-errors.json +23 -0
- package/dist/pi-web-plugins/zh-cn/manifest.webmanifest +25 -0
- package/dist/pi-web-plugins/zh-cn/metadata.json +3 -0
- package/dist/pi-web-plugins/zh-cn/package.json +10 -0
- package/dist/pi-web-plugins/zh-cn/pi-web-plugin.js +16 -0
- package/dist/pi-web-plugins/zh-cn/projects.json +6 -0
- package/dist/pi-web-plugins/zh-cn/provider-auth.json +18 -0
- package/dist/pi-web-plugins/zh-cn/routing.json +68 -0
- package/dist/pi-web-plugins/zh-cn/server-main.json +99 -0
- package/dist/pi-web-plugins/zh-cn/server.json +256 -0
- package/dist/piAi.js +4 -0
- package/dist/piAi.js.map +1 -0
- package/dist/piRuntime.js +150 -0
- package/dist/piRuntime.js.map +1 -0
- package/dist/piSdk.js +5 -0
- package/dist/piSdk.js.map +1 -0
- package/dist/piWebUpdate.js +508 -0
- package/dist/piWebUpdate.js.map +1 -0
- package/dist/piWebVersionReport.js +109 -41
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +609 -29
- package/dist/plugin-api.js +1 -0
- package/dist/pluginRecoveryCli.js +27 -25
- package/dist/pluginRecoveryCli.js.map +1 -1
- package/dist/server/activity/workspaceActivityService.js +13 -6
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +155 -77
- package/dist/server/app.js.map +1 -1
- package/dist/server/audit/managementAuditStore.js +6 -1
- package/dist/server/audit/managementAuditStore.js.map +1 -1
- package/dist/server/audit/normalToolAuditCli.js +45 -43
- package/dist/server/audit/normalToolAuditCli.js.map +1 -1
- package/dist/server/browserMessageProjection.js +39 -22
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/configRoutes.js +54 -33
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/deploymentIdentity.js +67 -0
- package/dist/server/deploymentIdentity.js.map +1 -0
- package/dist/server/deploymentIdentityRoutes.js +22 -0
- package/dist/server/deploymentIdentityRoutes.js.map +1 -0
- package/dist/server/diagnostics/nodePtyNativeModule.js +7 -6
- package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -1
- package/dist/server/diagnostics/nodePtySpawnHelper.js +21 -20
- package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -1
- package/dist/server/index.js +10 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/knownAutoInstallPiPackages.js +36 -0
- package/dist/server/knownAutoInstallPiPackages.js.map +1 -0
- package/dist/server/knownPiPackages.js +12 -0
- package/dist/server/knownPiPackages.js.map +1 -0
- package/dist/server/machines/machineClient.js +23 -19
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +131 -31
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +138 -47
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineRoutes.js +18 -13
- package/dist/server/machines/machineRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +40 -18
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/managementEmbed.js +149 -30
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/managementPrivileges.js +21 -0
- package/dist/server/managementPrivileges.js.map +1 -0
- package/dist/server/managementTerminalProxyRoutes.js +124 -0
- package/dist/server/managementTerminalProxyRoutes.js.map +1 -0
- package/dist/server/navigationPreferenceRoutes.js +59 -0
- package/dist/server/navigationPreferenceRoutes.js.map +1 -0
- package/dist/server/normalAuth.js +77 -53
- package/dist/server/normalAuth.js.map +1 -1
- package/dist/server/notices/serverNoticeRoutes.js +43 -0
- package/dist/server/notices/serverNoticeRoutes.js.map +1 -0
- package/dist/server/notices/serverNoticeService.js +26 -0
- package/dist/server/notices/serverNoticeService.js.map +1 -0
- package/dist/server/notices/serverNoticeStore.js +122 -0
- package/dist/server/notices/serverNoticeStore.js.map +1 -0
- package/dist/server/passwordVerifier.js +91 -0
- package/dist/server/passwordVerifier.js.map +1 -0
- package/dist/server/piPackageIdentity.js +25 -0
- package/dist/server/piPackageIdentity.js.map +1 -0
- package/dist/server/piPackageService.js +72 -9
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +212 -35
- package/dist/server/piWebPluginCatalog.js.map +1 -1
- package/dist/server/piWebPluginLifecycle.js +29 -3
- package/dist/server/piWebPluginLifecycle.js.map +1 -1
- package/dist/server/piWebPluginService.js +199 -292
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +57 -100
- package/dist/server/piWebStatus.js.map +1 -1
- package/dist/server/pluginCallbackDrain.js +35 -0
- package/dist/server/pluginCallbackDrain.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +61 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js +47 -57
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendRegistry.js +804 -0
- package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
- package/dist/server/plugins/projectPluginSettings.js +95 -0
- package/dist/server/plugins/projectPluginSettings.js.map +1 -0
- package/dist/server/plugins/serverPluginCatalogCapability.js +106 -0
- package/dist/server/plugins/serverPluginCatalogCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +1088 -108
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
- package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
- package/dist/server/projectTrustRoutes.js +86 -0
- package/dist/server/projectTrustRoutes.js.map +1 -0
- package/dist/server/projects/projectLifecycleService.js +94 -0
- package/dist/server/projects/projectLifecycleService.js.map +1 -0
- package/dist/server/projects/projectService.js +7 -4
- package/dist/server/projects/projectService.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +58 -39
- package/dist/server/realtime/sessionEventHub.js.map +1 -1
- package/dist/server/realtime/sessionEventScope.js +4 -0
- package/dist/server/realtime/sessionEventScope.js.map +1 -1
- package/dist/server/requestCancellation.js +15 -1
- package/dist/server/requestCancellation.js.map +1 -1
- package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
- package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
- package/dist/server/sessiond/autoInstallPiPackages.js +63 -0
- package/dist/server/sessiond/autoInstallPiPackages.js.map +1 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js +410 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +64 -47
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
- package/dist/server/sessiond/pluginCatalogRoutes.js +41 -0
- package/dist/server/sessiond/pluginCatalogRoutes.js.map +1 -0
- package/dist/server/sessiond/projectMutationRoutes.js +39 -0
- package/dist/server/sessiond/projectMutationRoutes.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js +36 -0
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
- package/dist/server/sessiond/sessionDaemonStartup.js +30 -4
- package/dist/server/sessiond/sessionDaemonStartup.js.map +1 -1
- package/dist/server/sessiond/sessionProxyRoutes.js +263 -91
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +5 -0
- package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
- package/dist/server/sessiond/sessiondStateOwnership.js +174 -39
- package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -1
- package/dist/server/sessiond/workspaceCatalogRoutes.js +11 -5
- package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -1
- package/dist/server/sessiond/workspaceNotificationRoutes.js +55 -0
- package/dist/server/sessiond/workspaceNotificationRoutes.js.map +1 -0
- package/dist/server/sessiond/workspaceRemovalRoutes.js +1 -1
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -1
- package/dist/server/sessiond.js +313 -69
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserDomain.js +232 -0
- package/dist/server/sessions/askUserDomain.js.map +1 -0
- package/dist/server/sessions/askUserTool.js +18 -21
- package/dist/server/sessions/askUserTool.js.map +1 -1
- package/dist/server/sessions/attachmentService.js +2 -6
- package/dist/server/sessions/attachmentService.js.map +1 -1
- package/dist/server/sessions/authRoutes.js +32 -19
- package/dist/server/sessions/authRoutes.js.map +1 -1
- package/dist/server/sessions/authService.js +31 -11
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +28 -21
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/builtinExtensionFactories.js +36 -0
- package/dist/server/sessions/builtinExtensionFactories.js.map +1 -0
- package/dist/server/sessions/clientSessionPreview.js +14 -0
- package/dist/server/sessions/clientSessionPreview.js.map +1 -0
- package/dist/server/sessions/companionBackend.js +286 -0
- package/dist/server/sessions/companionBackend.js.map +1 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js +98 -10
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/extensionSessionDispatch.js +76 -0
- package/dist/server/sessions/extensionSessionDispatch.js.map +1 -0
- package/dist/server/sessions/globalProviderPolicy.js +88 -19
- package/dist/server/sessions/globalProviderPolicy.js.map +1 -1
- package/dist/server/sessions/managementAgentTools.js +116 -6
- package/dist/server/sessions/managementAgentTools.js.map +1 -1
- package/dist/server/sessions/managementPermissionSystem.js +36 -14
- package/dist/server/sessions/managementPermissionSystem.js.map +1 -1
- package/dist/server/sessions/managementSandbox.js +7 -18
- package/dist/server/sessions/managementSandbox.js.map +1 -1
- package/dist/server/sessions/mcpAuthFlowService.js +289 -0
- package/dist/server/sessions/mcpAuthFlowService.js.map +1 -0
- package/dist/server/sessions/mcpAuthRoutes.js +104 -0
- package/dist/server/sessions/mcpAuthRoutes.js.map +1 -0
- package/dist/server/sessions/mcpOAuthCredentialAdapter.js +92 -0
- package/dist/server/sessions/mcpOAuthCredentialAdapter.js.map +1 -0
- package/dist/server/sessions/messageHistoryTarget.js +79 -0
- package/dist/server/sessions/messageHistoryTarget.js.map +1 -0
- package/dist/server/sessions/messagePaging.js +2 -2
- package/dist/server/sessions/messagePaging.js.map +1 -1
- package/dist/server/sessions/modelCatalogRefresher.js +4 -3
- package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
- package/dist/server/sessions/oauthLoginFlowService.js +19 -20
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/piSessionEventConnections.js +68 -0
- package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +206 -27
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +2508 -991
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/plainTextTheme.js +1 -1
- package/dist/server/sessions/plainTextTheme.js.map +1 -1
- package/dist/server/sessions/projectAuthService.js +35 -6
- package/dist/server/sessions/projectAuthService.js.map +1 -1
- package/dist/server/sessions/projectSettingsScope.js +88 -33
- package/dist/server/sessions/projectSettingsScope.js.map +1 -1
- package/dist/server/sessions/projectTrust.js +119 -0
- package/dist/server/sessions/projectTrust.js.map +1 -0
- package/dist/server/sessions/queuedMessageMutation.js +170 -0
- package/dist/server/sessions/queuedMessageMutation.js.map +1 -0
- package/dist/server/sessions/relaySkill.js +169 -0
- package/dist/server/sessions/relaySkill.js.map +1 -0
- package/dist/server/sessions/sessionActivityMarker.js +105 -0
- package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
- package/dist/server/sessions/sessionArchiveMapping.js +16 -15
- package/dist/server/sessions/sessionArchiveMapping.js.map +1 -1
- package/dist/server/sessions/sessionArchiveStore.js +5 -1
- package/dist/server/sessions/sessionArchiveStore.js.map +1 -1
- package/dist/server/sessions/sessionArchiveTree.js +2 -2
- package/dist/server/sessions/sessionArchiveTree.js.map +1 -1
- package/dist/server/sessions/sessionCleanup.js +4 -3
- package/dist/server/sessions/sessionCleanup.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +79 -87
- package/dist/server/sessions/sessionCommandService.js.map +1 -1
- package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/sessionFileHeader.js +41 -0
- package/dist/server/sessions/sessionFileHeader.js.map +1 -1
- package/dist/server/sessions/sessionImageDimensions.js +31 -0
- package/dist/server/sessions/sessionImageDimensions.js.map +1 -0
- package/dist/server/sessions/sessionMediaIndex.js +191 -0
- package/dist/server/sessions/sessionMediaIndex.js.map +1 -0
- package/dist/server/sessions/sessionMetadata.js +51 -0
- package/dist/server/sessions/sessionMetadata.js.map +1 -0
- package/dist/server/sessions/sessionModelScope.js +160 -0
- package/dist/server/sessions/sessionModelScope.js.map +1 -0
- package/dist/server/sessions/sessionNameGenerator.js +16 -35
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionNotificationStore.js +14 -11
- package/dist/server/sessions/sessionNotificationStore.js.map +1 -1
- package/dist/server/sessions/sessionReadAccess.js +41 -0
- package/dist/server/sessions/sessionReadAccess.js.map +1 -0
- package/dist/server/sessions/sessionRoutes.js +221 -78
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionSummaryScanner.js +62 -5
- package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
- package/dist/server/sessions/sessionTreeProjection.js +50 -49
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -1
- package/dist/server/sessions/sessionUiEvents.js +22 -10
- package/dist/server/sessions/sessionUiEvents.js.map +1 -1
- package/dist/server/sessions/sessionUnreadStore.js +53 -3
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +14 -6
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +40 -24
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +1 -1
- package/dist/server/sessions/subsessionCompletionDelivery.js +15 -0
- package/dist/server/sessions/subsessionCompletionDelivery.js.map +1 -0
- package/dist/server/sessions/subsessionCompletionInbox.js +134 -0
- package/dist/server/sessions/subsessionCompletionInbox.js.map +1 -0
- package/dist/server/sessions/subsessionProvenance.js +27 -0
- package/dist/server/sessions/subsessionProvenance.js.map +1 -0
- package/dist/server/sessions/transcriptBranchCache.js +81 -0
- package/dist/server/sessions/transcriptBranchCache.js.map +1 -0
- package/dist/server/sessions/transcriptMarkdown.js +48 -0
- package/dist/server/sessions/transcriptMarkdown.js.map +1 -0
- package/dist/server/sessions/transcriptMessages.js +57 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -0
- package/dist/server/startupLogger.js +13 -0
- package/dist/server/startupLogger.js.map +1 -0
- package/dist/server/startupUrls.js +27 -0
- package/dist/server/startupUrls.js.map +1 -0
- package/dist/server/status/machineStatusService.js +15 -7
- package/dist/server/status/machineStatusService.js.map +1 -1
- package/dist/server/storage/navigationPreferenceStore.js +138 -0
- package/dist/server/storage/navigationPreferenceStore.js.map +1 -0
- package/dist/server/storage/piPackageDismissalStore.js +78 -0
- package/dist/server/storage/piPackageDismissalStore.js.map +1 -0
- package/dist/server/terminals/{terminalService.js → managementCommandRunService.js} +98 -102
- package/dist/server/terminals/managementCommandRunService.js.map +1 -0
- package/dist/server/terminals/managementTerminalRoutes.js +91 -0
- package/dist/server/terminals/managementTerminalRoutes.js.map +1 -0
- package/dist/server/terminals/requiredTerminalService.js +103 -0
- package/dist/server/terminals/requiredTerminalService.js.map +1 -0
- package/dist/server/webSocketBridge.js +348 -0
- package/dist/server/webSocketBridge.js.map +1 -1
- package/dist/server/workbench/accessStateStore.js +33 -5
- package/dist/server/workbench/accessStateStore.js.map +1 -1
- package/dist/server/workbench/capabilitySearch.js +3 -2
- package/dist/server/workbench/capabilitySearch.js.map +1 -1
- package/dist/server/workbench/gatewayIntegration.js +40 -13
- package/dist/server/workbench/gatewayIntegration.js.map +1 -1
- package/dist/server/workbench/knowledgeSearch.js +100 -0
- package/dist/server/workbench/knowledgeSearch.js.map +1 -0
- package/dist/server/workbench/mcpClient.js +41 -8
- package/dist/server/workbench/mcpClient.js.map +1 -1
- package/dist/server/workbench/skillSync.js +11 -3
- package/dist/server/workbench/skillSync.js.map +1 -1
- package/dist/server/workbench/workbenchClient.js +130 -7
- package/dist/server/workbench/workbenchClient.js.map +1 -1
- package/dist/server/workbench/workbenchTools.js +196 -14
- package/dist/server/workbench/workbenchTools.js.map +1 -1
- package/dist/server/workingDirectory.js +3 -2
- package/dist/server/workingDirectory.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +53 -21
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +31 -16
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/fileOperationService.js +11 -9
- package/dist/server/workspaces/fileOperationService.js.map +1 -1
- package/dist/server/workspaces/filePreviewService.js +10 -6
- package/dist/server/workspaces/filePreviewService.js.map +1 -1
- package/dist/server/workspaces/fileSuggestions.js +77 -10
- package/dist/server/workspaces/fileSuggestions.js.map +1 -1
- package/dist/server/workspaces/fileTreeService.js +103 -15
- package/dist/server/workspaces/fileTreeService.js.map +1 -1
- package/dist/server/workspaces/normalProjectCwdResolver.js +35 -0
- package/dist/server/workspaces/normalProjectCwdResolver.js.map +1 -0
- package/dist/server/workspaces/pathAccessPolicy.js +24 -18
- package/dist/server/workspaces/pathAccessPolicy.js.map +1 -1
- package/dist/server/workspaces/pathSafety.js +6 -5
- package/dist/server/workspaces/pathSafety.js.map +1 -1
- package/dist/server/workspaces/projectPiWebConfig.js +95 -8
- package/dist/server/workspaces/projectPiWebConfig.js.map +1 -1
- package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
- package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +37 -22
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceCatalog.js +6 -4
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceChangeNotifier.js +12 -0
- package/dist/server/workspaces/workspaceChangeNotifier.js.map +1 -0
- package/dist/server/workspaces/workspaceDeletionRoutes.js +9 -70
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceNotificationCommand.js +64 -0
- package/dist/server/workspaces/workspaceNotificationCommand.js.map +1 -0
- package/dist/server/workspaces/workspaceProviderRegistry.js +128 -24
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server/workspaces/workspaceRemovalService.js +41 -7
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
- package/dist/server/workspaces/workspaceRouteContext.js +1 -1
- package/dist/server/workspaces/workspaceRouteContext.js.map +1 -1
- package/dist/server/workspaces/workspaceRouteErrors.js +4 -2
- package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -1
- package/dist/server/workspaces/workspaceUserError.js +18 -0
- package/dist/server/workspaces/workspaceUserError.js.map +1 -0
- package/dist/server-plugin-api.d.ts +349 -49
- package/dist/server-plugin-api.js +344 -2
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/serverPluginRecovery.js +6 -1
- package/dist/serverPluginRecovery.js.map +1 -1
- package/dist/sessiond/activeAgentProfile.js +27 -2
- package/dist/sessiond/activeAgentProfile.js.map +1 -1
- package/dist/sessiond/config.js +4 -3
- package/dist/sessiond/config.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +75 -19
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/sessiond/sessionDaemonErrors.js +49 -0
- package/dist/sessiond/sessionDaemonErrors.js.map +1 -0
- package/dist/shared/activeAgentProfile.js +2 -34
- 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 +10 -3
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +8 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +77 -11
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/i18n.js +41 -0
- package/dist/shared/i18n.js.map +1 -0
- package/dist/shared/machinePluginIds.js +30 -7
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/machineStatus.js +46 -3
- package/dist/shared/machineStatus.js.map +1 -1
- package/dist/shared/navigationPreferences.js +6 -0
- package/dist/shared/navigationPreferences.js.map +1 -0
- package/dist/shared/piWebConfigParsing.js +118 -49
- package/dist/shared/piWebConfigParsing.js.map +1 -1
- package/dist/shared/piWebStatusParsing.js +33 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +87 -8
- package/dist/shared/pluginApiTypes.js +2 -0
- package/dist/shared/pluginApiTypes.js.map +1 -0
- package/dist/shared/pluginBackendProtocol.js +218 -2
- package/dist/shared/pluginBackendProtocol.js.map +1 -1
- package/dist/shared/pluginCatalogTypes.d.ts +49 -0
- package/dist/shared/pluginCatalogTypes.js +156 -0
- package/dist/shared/pluginCatalogTypes.js.map +1 -0
- package/dist/shared/pluginIds.js +10 -1
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/requiredTerminalPlugin.js +6 -0
- package/dist/shared/requiredTerminalPlugin.js.map +1 -0
- package/dist/shared/serverNoticeContract.js +88 -0
- package/dist/shared/serverNoticeContract.js.map +1 -0
- package/dist/shared/sessionDefaults.js +42 -0
- package/dist/shared/sessionDefaults.js.map +1 -0
- package/dist/shared/sessionMedia.js +12 -0
- package/dist/shared/sessionMedia.js.map +1 -0
- package/dist/shared/streamingBehavior.js +8 -0
- package/dist/shared/streamingBehavior.js.map +1 -0
- package/dist/shared/themePreference.js +24 -0
- package/dist/shared/themePreference.js.map +1 -0
- package/dist/shared/thinkingLevels.js +17 -16
- package/dist/shared/thinkingLevels.js.map +1 -1
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/dist/workspaceNotificationCli.js +95 -0
- package/dist/workspaceNotificationCli.js.map +1 -0
- package/docs/README.md +31 -0
- package/docs/config.md +344 -277
- package/docs/plugins.md +204 -930
- package/examples/session-bridge-plugin/README.md +20 -0
- package/examples/session-bridge-plugin/docs/usage.md +54 -0
- package/examples/session-bridge-plugin/package.json +26 -0
- package/examples/session-bridge-plugin/src/browser/index.ts +87 -0
- package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
- package/examples/session-bridge-plugin/src/companion.ts +65 -0
- package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
- package/examples/session-bridge-plugin/src/server.ts +85 -0
- package/examples/session-bridge-plugin/src/store.ts +80 -0
- package/examples/session-bridge-plugin/tsconfig.json +21 -0
- package/examples/workspace-provider-plugin/README.md +39 -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 +68 -0
- package/examples/workspace-provider-plugin/tsconfig.json +21 -0
- package/extensions/pi-web.ts +3 -1
- package/package.json +59 -36
- package/skills/README.md +28 -2
- package/skills/relay/SKILL.md +24 -89
- package/skills/relay-runner/SKILL.md +74 -0
- package/dist/client/assets/CodeViewer-DCl56K0Z.js +0 -4
- package/dist/client/assets/TerminalPanel-DCvoEjUF.js +0 -187
- package/dist/client/assets/index-CMvms5cF.js +0 -4325
- package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
- package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
- package/dist/pi-web-plugins/relays/package.json +0 -9
- package/dist/pi-web-plugins/relays/pi-web-plugin.js +0 -44
- package/dist/server/git/gitService.js +0 -290
- 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/pendingAskStore.js +0 -278
- package/dist/server/sessions/pendingAskStore.js.map +0 -1
- package/dist/server/terminalProxyRoutes.js +0 -178
- package/dist/server/terminalProxyRoutes.js.map +0 -1
- package/dist/server/terminals/terminalRoutes.js +0 -164
- package/dist/server/terminals/terminalRoutes.js.map +0 -1
- package/dist/server/terminals/terminalService.js.map +0 -1
- package/dist/server/terminals/terminalSize.js +0 -17
- package/dist/server/terminals/terminalSize.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/workspaceService.js +0 -52
- package/dist/server/workspaces/workspaceService.js.map +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
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { createTranslator } from "../shared/i18n.js";
|
|
1
2
|
import { posix as posixPath } from "node:path";
|
|
2
|
-
import {
|
|
3
|
+
import { TextDecoder, TextEncoder } from "node:util";
|
|
4
|
+
import { ownEnvironmentValue } from "../environment.js";
|
|
5
|
+
import { createDevelopmentNativeServicePlan, nativeServiceManagerRefs, nativeServicePrerequisiteNeedsPathAdvice, resolveProductionNativeServicePlan, validateNativeServicePlan, } from "./servicePlan.js";
|
|
3
6
|
export function inferInstalledNativeServiceMode(serviceIds) {
|
|
4
7
|
if (serviceIds.size === 0)
|
|
5
8
|
return "none";
|
|
@@ -11,15 +14,51 @@ export function inferInstalledNativeServiceMode(serviceIds) {
|
|
|
11
14
|
return "development";
|
|
12
15
|
return "ambiguous";
|
|
13
16
|
}
|
|
14
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Select the config path for a caller that is about to probe managed native
|
|
19
|
+
* services. A nonempty caller value is an explicit override and deliberately
|
|
20
|
+
* avoids interpreting installed files. Otherwise every relevant definition
|
|
21
|
+
* must parse to one consistent environment before its persisted path is used.
|
|
22
|
+
*/
|
|
23
|
+
export function selectManagedNativeServiceConfig(backend, definitions, callerConfigPath) {
|
|
24
|
+
if (callerConfigPath !== undefined && callerConfigPath !== "") {
|
|
25
|
+
return { ok: true, value: { source: "caller", configPath: callerConfigPath } };
|
|
26
|
+
}
|
|
27
|
+
if (definitions.length === 0)
|
|
28
|
+
return { ok: true, value: { source: "default" } };
|
|
15
29
|
const parsed = parseConsistentDefinitions(backend, definitions);
|
|
30
|
+
if (!parsed.ok)
|
|
31
|
+
return parsed;
|
|
32
|
+
const firstEnvironment = parsed.value[0]?.environment;
|
|
33
|
+
const installedConfigPath = firstEnvironment === undefined
|
|
34
|
+
? undefined
|
|
35
|
+
: ownEnvironmentValue(firstEnvironment, "PI_WEB_CONFIG");
|
|
36
|
+
if (installedConfigPath === undefined || installedConfigPath === "") {
|
|
37
|
+
return { ok: true, value: { source: "default" } };
|
|
38
|
+
}
|
|
39
|
+
if (!posixPath.isAbsolute(installedConfigPath)) {
|
|
40
|
+
return {
|
|
41
|
+
ok: false,
|
|
42
|
+
message: `Installed service definitions declare relative PI_WEB_CONFIG ${JSON.stringify(installedConfigPath)}; managed config paths must be absolute.`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { ok: true, value: { source: "installed", configPath: installedConfigPath } };
|
|
46
|
+
}
|
|
47
|
+
export function inspectInstalledNativeServiceDefinitionEnvironment(backend, definition) {
|
|
48
|
+
const parsed = backend.kind === "systemd"
|
|
49
|
+
? parseSystemdDefinition(definition)
|
|
50
|
+
: parseLaunchdDefinition(definition);
|
|
51
|
+
return parsed.ok ? { ok: true, value: parsed.value.environment } : parsed;
|
|
52
|
+
}
|
|
53
|
+
export function inspectInstalledProductionServiceContext(backend, definitions, t = createTranslator("en", {})) {
|
|
54
|
+
const parsed = parseConsistentDefinitions(backend, definitions, t);
|
|
16
55
|
if (!parsed.ok)
|
|
17
56
|
return parsed;
|
|
18
57
|
const withWorkingDirectory = parsed.value.find((definition) => definition.workingDirectory !== null);
|
|
19
58
|
if (withWorkingDirectory !== undefined) {
|
|
20
59
|
return {
|
|
21
60
|
ok: false,
|
|
22
|
-
message:
|
|
61
|
+
message: t("doctor.productionCwd", "Installed production service {arg0} unexpectedly has working directory {arg1}.", { arg0: withWorkingDirectory.id, arg1: withWorkingDirectory.workingDirectory ?? "" }),
|
|
23
62
|
};
|
|
24
63
|
}
|
|
25
64
|
return {
|
|
@@ -30,13 +69,13 @@ export function inspectInstalledProductionServiceContext(backend, definitions) {
|
|
|
30
69
|
},
|
|
31
70
|
};
|
|
32
71
|
}
|
|
33
|
-
export function inspectInstalledDevelopmentServiceInput(backend, definitions) {
|
|
34
|
-
const parsed = parseConsistentDefinitions(backend, definitions);
|
|
72
|
+
export function inspectInstalledDevelopmentServiceInput(backend, definitions, t = createTranslator("en", {})) {
|
|
73
|
+
const parsed = parseConsistentDefinitions(backend, definitions, t);
|
|
35
74
|
if (!parsed.ok)
|
|
36
75
|
return parsed;
|
|
37
76
|
const first = parsed.value[0] ?? impossibleMissingDefinition();
|
|
38
77
|
if (first.workingDirectory === null) {
|
|
39
|
-
return { ok: false, message: "Installed development services do not declare a working directory." };
|
|
78
|
+
return { ok: false, message: t("doctor.cwdMissing", "Installed development services do not declare a working directory.") };
|
|
40
79
|
}
|
|
41
80
|
const input = {
|
|
42
81
|
backend,
|
|
@@ -51,7 +90,7 @@ export function inspectInstalledDevelopmentServiceInput(backend, definitions) {
|
|
|
51
90
|
if (expected?.shellCommand !== definition.shellCommand) {
|
|
52
91
|
return {
|
|
53
92
|
ok: false,
|
|
54
|
-
message:
|
|
93
|
+
message: t("doctor.developmentMismatch", "Installed {arg0} service command does not match the canonical development plan.", { arg0: definition.id }),
|
|
55
94
|
};
|
|
56
95
|
}
|
|
57
96
|
}
|
|
@@ -77,14 +116,14 @@ export async function runNativeServiceDoctor(target, dependencies) {
|
|
|
77
116
|
const validation = await validateNativeServicePlan(plan, dependencies.probe);
|
|
78
117
|
return { kind: "plan-validation", scope, plan, validation };
|
|
79
118
|
}
|
|
80
|
-
export function formatNativeServiceDoctorResult(result) {
|
|
119
|
+
export function formatNativeServiceDoctorResult(result, t = createTranslator("en", {})) {
|
|
81
120
|
if (result.kind === "inspection-failure") {
|
|
82
121
|
return {
|
|
83
122
|
ok: false,
|
|
84
123
|
failureKind: "inspection",
|
|
85
124
|
lines: [
|
|
86
|
-
|
|
87
|
-
" Run `pi-web install` or `pi-web install --dev` to replace mixed, partial, or outdated service definitions.",
|
|
125
|
+
t("doctor.inspectionFailed", "✗ Installed native-service plan could not be inspected: {arg0}", { arg0: result.message }),
|
|
126
|
+
t("doctor.replaceDefinitions", " Run `pi-web install` or `pi-web install --dev` to replace mixed, partial, or outdated service definitions."),
|
|
88
127
|
],
|
|
89
128
|
plan: null,
|
|
90
129
|
adviceShell: null,
|
|
@@ -92,26 +131,26 @@ export function formatNativeServiceDoctorResult(result) {
|
|
|
92
131
|
failedPrerequisites: [],
|
|
93
132
|
};
|
|
94
133
|
}
|
|
95
|
-
const lines = [scopeHeading(result.scope)];
|
|
134
|
+
const lines = [scopeHeading(result.scope, t)];
|
|
96
135
|
if (result.kind === "plan-resolution-failure") {
|
|
97
136
|
let infrastructure = false;
|
|
98
137
|
for (const failure of result.failures) {
|
|
99
138
|
if (failure.kind === "probe-infrastructure") {
|
|
100
139
|
infrastructure = true;
|
|
101
|
-
lines.push(
|
|
140
|
+
lines.push(t("doctor.infrastructureFailure", "✗ Native service probe infrastructure failure ({arg0}): {arg1}", { arg0: failure.reason, arg1: failure.message }));
|
|
102
141
|
}
|
|
103
142
|
else if (failure.kind === "entrypoint-inspection-failure") {
|
|
104
143
|
infrastructure = true;
|
|
105
|
-
lines.push(
|
|
144
|
+
lines.push(t("doctor.entrypointFailed", "✗ Could not inspect bundled {arg0} entrypoint {arg1}: {arg2}", { arg0: failure.serviceId, arg1: failure.entrypointPath, arg2: failure.message }));
|
|
106
145
|
}
|
|
107
146
|
else {
|
|
108
|
-
lines.push(
|
|
147
|
+
lines.push(t("doctor.commandMissing", "✗ {arg0} is unavailable to the native service manager, and bundled entrypoint {arg1} is missing.", { arg0: failure.namedCommand, arg1: failure.bundledEntrypointPath }));
|
|
109
148
|
if (failure.namedCommandFailure !== null)
|
|
110
149
|
lines.push(` ${failure.namedCommandFailure}`);
|
|
111
150
|
}
|
|
112
151
|
}
|
|
113
152
|
if (infrastructure)
|
|
114
|
-
lines.push(" This infrastructure failure is not proof of a PATH mismatch.");
|
|
153
|
+
lines.push(t("doctor.notPathProof", " This infrastructure failure is not proof of a PATH mismatch."));
|
|
115
154
|
return {
|
|
116
155
|
ok: false,
|
|
117
156
|
failureKind: infrastructure ? "infrastructure" : "requirements",
|
|
@@ -125,10 +164,10 @@ export function formatNativeServiceDoctorResult(result) {
|
|
|
125
164
|
}
|
|
126
165
|
const configuredOverrides = result.plan.services.filter((service) => service.strategy.kind === "configured-override");
|
|
127
166
|
for (const service of configuredOverrides) {
|
|
128
|
-
lines.push(
|
|
167
|
+
lines.push(t("doctor.overrideSkipped", "! {arg0} uses a configured command override; doctor does not execute arbitrary configured commands.", { arg0: service.description }));
|
|
129
168
|
}
|
|
130
169
|
if (result.validation.ok) {
|
|
131
|
-
lines.push("✓ All verifiable native-service plan requirements are satisfied in the service-manager context.");
|
|
170
|
+
lines.push(t("doctor.requirementsSatisfied", "✓ All verifiable native-service plan requirements are satisfied in the service-manager context."));
|
|
132
171
|
return {
|
|
133
172
|
ok: true,
|
|
134
173
|
failureKind: "none",
|
|
@@ -144,17 +183,17 @@ export function formatNativeServiceDoctorResult(result) {
|
|
|
144
183
|
for (const failure of result.validation.failures) {
|
|
145
184
|
if (failure.kind === "probe-infrastructure") {
|
|
146
185
|
infrastructure = true;
|
|
147
|
-
lines.push(
|
|
186
|
+
lines.push(t("doctor.infrastructureFailure", "✗ Native service probe infrastructure failure ({arg0}): {arg1}", { arg0: failure.reason, arg1: failure.message }));
|
|
148
187
|
}
|
|
149
188
|
else {
|
|
150
189
|
failedPrerequisites.push(failure.prerequisite);
|
|
151
|
-
lines.push(
|
|
190
|
+
lines.push(t("doctor.requirementFailed", "✗ Native service requirement failed: {arg0}", { arg0: failure.prerequisite.description }));
|
|
152
191
|
if (failure.detail !== null && failure.detail !== failure.prerequisite.description)
|
|
153
192
|
lines.push(` ${failure.detail}`);
|
|
154
193
|
}
|
|
155
194
|
}
|
|
156
195
|
if (infrastructure)
|
|
157
|
-
lines.push(" This infrastructure failure is not proof of a PATH mismatch.");
|
|
196
|
+
lines.push(t("doctor.notPathProof", " This infrastructure failure is not proof of a PATH mismatch."));
|
|
158
197
|
return {
|
|
159
198
|
ok: false,
|
|
160
199
|
failureKind: infrastructure ? "infrastructure" : "requirements",
|
|
@@ -166,19 +205,19 @@ export function formatNativeServiceDoctorResult(result) {
|
|
|
166
205
|
failedPrerequisites,
|
|
167
206
|
};
|
|
168
207
|
}
|
|
169
|
-
function scopeHeading(scope) {
|
|
208
|
+
function scopeHeading(scope, t = createTranslator("en", {})) {
|
|
170
209
|
if (scope.kind === "installed-development")
|
|
171
|
-
return "Installed development native-service plan:";
|
|
172
|
-
return
|
|
210
|
+
return t("doctor.installedDev", "Installed development native-service plan:");
|
|
211
|
+
return t("doctor.prospectiveProduction", "Prospective production native-service plan ({arg0}):", { arg0: scope.reason ?? t("doctor.strategyUnknown", "installed strategy is unknown") });
|
|
173
212
|
}
|
|
174
|
-
function parseConsistentDefinitions(backend, definitions) {
|
|
213
|
+
function parseConsistentDefinitions(backend, definitions, t = createTranslator("en", {})) {
|
|
175
214
|
if (definitions.length === 0)
|
|
176
|
-
return { ok: false, message: "No installed service definitions were provided." };
|
|
215
|
+
return { ok: false, message: t("doctor.definitionsMissing", "No installed service definitions were provided.") };
|
|
177
216
|
const parsed = [];
|
|
178
217
|
for (const definition of definitions) {
|
|
179
218
|
const result = backend.kind === "systemd"
|
|
180
|
-
? parseSystemdDefinition(definition)
|
|
181
|
-
: parseLaunchdDefinition(definition);
|
|
219
|
+
? parseSystemdDefinition(definition, t)
|
|
220
|
+
: parseLaunchdDefinition(definition, t);
|
|
182
221
|
if (!result.ok)
|
|
183
222
|
return result;
|
|
184
223
|
parsed.push(result.value);
|
|
@@ -186,13 +225,13 @@ function parseConsistentDefinitions(backend, definitions) {
|
|
|
186
225
|
const first = parsed[0] ?? impossibleMissingDefinition();
|
|
187
226
|
for (const definition of parsed.slice(1)) {
|
|
188
227
|
if (definition.shell.executable !== first.shell.executable) {
|
|
189
|
-
return { ok: false, message: "Installed service definitions use different login shells." };
|
|
228
|
+
return { ok: false, message: t("doctor.shellMismatch", "Installed service definitions use different login shells.") };
|
|
190
229
|
}
|
|
191
230
|
if (!recordsEqual(definition.environment, first.environment)) {
|
|
192
|
-
return { ok: false, message: "Installed service definitions use different environments." };
|
|
231
|
+
return { ok: false, message: t("doctor.environmentMismatch", "Installed service definitions use different environments.") };
|
|
193
232
|
}
|
|
194
233
|
if (definition.workingDirectory !== first.workingDirectory) {
|
|
195
|
-
return { ok: false, message: "Installed service definitions use different working directories." };
|
|
234
|
+
return { ok: false, message: t("doctor.cwdMismatch", "Installed service definitions use different working directories.") };
|
|
196
235
|
}
|
|
197
236
|
}
|
|
198
237
|
return { ok: true, value: parsed };
|
|
@@ -203,7 +242,9 @@ function systemdServiceDirectives(contents) {
|
|
|
203
242
|
let inServiceSection = false;
|
|
204
243
|
let foundServiceSection = false;
|
|
205
244
|
for (const line of contents.split(/\r?\n/u)) {
|
|
206
|
-
|
|
245
|
+
// systemd syntax trims ASCII spaces and tabs here; JavaScript's trim/\s
|
|
246
|
+
// would also consume Unicode whitespace that systemd treats as key text.
|
|
247
|
+
const trimmed = line.replace(/^[ \t]+|[ \t]+$/gu, "");
|
|
207
248
|
if (/^\[[^\]]+\]$/u.test(trimmed)) {
|
|
208
249
|
inServiceSection = trimmed === "[Service]";
|
|
209
250
|
foundServiceSection ||= inServiceSection;
|
|
@@ -211,105 +252,180 @@ function systemdServiceDirectives(contents) {
|
|
|
211
252
|
}
|
|
212
253
|
if (!inServiceSection || trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith(";"))
|
|
213
254
|
continue;
|
|
214
|
-
|
|
255
|
+
// Slice after the recognized prefix so JavaScript-only line separators remain directive data.
|
|
256
|
+
const match = /^[ \t]*([A-Za-z][A-Za-z0-9]*)=/u.exec(line);
|
|
215
257
|
const name = match?.[1];
|
|
216
|
-
|
|
217
|
-
if (name === undefined || value === undefined || !allowed.has(name))
|
|
258
|
+
if (match === null || name === undefined || !allowed.has(name))
|
|
218
259
|
return undefined;
|
|
219
|
-
directives.push({ name, value });
|
|
260
|
+
directives.push({ name, value: line.slice(match[0].length) });
|
|
220
261
|
}
|
|
221
262
|
return foundServiceSection ? directives : undefined;
|
|
222
263
|
}
|
|
223
|
-
function
|
|
264
|
+
function hasSystemdPhysicalLineContinuation(line) {
|
|
265
|
+
let trailingBackslashes = 0;
|
|
266
|
+
for (let index = line.length - 1; index >= 0 && line[index] === "\\"; index -= 1)
|
|
267
|
+
trailingBackslashes += 1;
|
|
268
|
+
return trailingBackslashes % 2 === 1;
|
|
269
|
+
}
|
|
270
|
+
/** Parse PI WEB's bounded ExecStart argument structure, including persisted legacy encodings. */
|
|
271
|
+
function parseSystemdExecStart(value) {
|
|
272
|
+
const environmentPrefix = "/usr/bin/env ";
|
|
273
|
+
const shellOffset = value.startsWith(environmentPrefix) ? environmentPrefix.length : 0;
|
|
274
|
+
const shell = parseSystemdExecStartArgument(value, shellOffset);
|
|
275
|
+
if (shell === undefined)
|
|
276
|
+
return undefined;
|
|
277
|
+
const loginShellSeparator = " -lc ";
|
|
278
|
+
if (!value.startsWith(loginShellSeparator, shell.endOffset))
|
|
279
|
+
return undefined;
|
|
280
|
+
const shellCommandOffset = shell.endOffset + loginShellSeparator.length;
|
|
281
|
+
if (shellCommandOffset >= value.length)
|
|
282
|
+
return undefined;
|
|
283
|
+
return {
|
|
284
|
+
shellArgument: shell.value,
|
|
285
|
+
shellCommandArgument: value.slice(shellCommandOffset),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/** Scan one systemd argument linearly so delimiters inside quoted values remain data. */
|
|
289
|
+
function parseSystemdExecStartArgument(value, offset) {
|
|
290
|
+
if (offset >= value.length || value[offset] === " " || value[offset] === "\t")
|
|
291
|
+
return undefined;
|
|
292
|
+
let quote = null;
|
|
293
|
+
for (let index = offset; index < value.length; index += 1) {
|
|
294
|
+
const character = value[index];
|
|
295
|
+
if (character === "\\") {
|
|
296
|
+
index += 1;
|
|
297
|
+
if (index >= value.length)
|
|
298
|
+
return undefined;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (quote === null && (character === " " || character === "\t")) {
|
|
302
|
+
return { value: value.slice(offset, index), endOffset: index };
|
|
303
|
+
}
|
|
304
|
+
if (character !== "\"" && character !== "'")
|
|
305
|
+
continue;
|
|
306
|
+
if (quote === null)
|
|
307
|
+
quote = character;
|
|
308
|
+
else if (character === quote)
|
|
309
|
+
quote = null;
|
|
310
|
+
}
|
|
311
|
+
return quote === null ? { value: value.slice(offset), endOffset: value.length } : undefined;
|
|
312
|
+
}
|
|
313
|
+
// Keep malformed installed input linear; overlapping escaped/raw regex alternatives can backtrack exponentially.
|
|
314
|
+
function isSingleSystemdQuotedValue(value) {
|
|
315
|
+
if (value.length < 2 || !value.startsWith('"') || !value.endsWith('"'))
|
|
316
|
+
return false;
|
|
317
|
+
const closingQuoteIndex = value.length - 1;
|
|
318
|
+
for (let index = 1; index < closingQuoteIndex; index += 1) {
|
|
319
|
+
const character = value[index];
|
|
320
|
+
if (character === '"')
|
|
321
|
+
return false;
|
|
322
|
+
if (character !== "\\")
|
|
323
|
+
continue;
|
|
324
|
+
index += 1;
|
|
325
|
+
if (index >= closingQuoteIndex)
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
function parseSystemdDefinition(definition, t = createTranslator("en", {})) {
|
|
331
|
+
if (definition.contents.split(/\r?\n/u).some(hasSystemdPhysicalLineContinuation)) {
|
|
332
|
+
return {
|
|
333
|
+
ok: false,
|
|
334
|
+
message: `Installed ${definition.id} systemd unit uses physical-line continuation, which cannot be inspected safely.`,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
224
337
|
const directives = systemdServiceDirectives(definition.contents);
|
|
225
338
|
if (directives === undefined) {
|
|
226
|
-
return { ok: false, message:
|
|
339
|
+
return { ok: false, message: t("doctor.systemd.unrecognized_service_directives", "Installed {arg0} systemd unit has unrecognized service directives.", { arg0: definition.id }) };
|
|
227
340
|
}
|
|
228
341
|
const execStarts = directives.filter((directive) => directive.name === "ExecStart");
|
|
229
342
|
const execStart = execStarts.length === 1
|
|
230
|
-
?
|
|
231
|
-
:
|
|
232
|
-
if (execStart
|
|
343
|
+
? parseSystemdExecStart(execStarts[0]?.value ?? "")
|
|
344
|
+
: undefined;
|
|
345
|
+
if (execStart === undefined) {
|
|
233
346
|
return { ok: false, message: `Installed ${definition.id} systemd unit must have exactly one recognized ExecStart.` };
|
|
234
347
|
}
|
|
235
|
-
const shellExecutable = parseSystemdExecArgument(execStart
|
|
348
|
+
const shellExecutable = parseSystemdExecArgument(execStart.shellArgument);
|
|
236
349
|
if (shellExecutable === undefined) {
|
|
237
|
-
return { ok: false, message:
|
|
350
|
+
return { ok: false, message: t("doctor.systemd.an_unrecognized_login_shell_argument", "Installed {arg0} systemd unit has an unrecognized login shell argument.", { arg0: definition.id }) };
|
|
238
351
|
}
|
|
239
|
-
const shell = installedShell(shellExecutable);
|
|
352
|
+
const shell = installedShell(shellExecutable, t);
|
|
240
353
|
if (!shell.ok)
|
|
241
354
|
return shell;
|
|
242
|
-
const shellCommand = parseSystemdShellCommand(shell.value.name, execStart
|
|
355
|
+
const shellCommand = parseSystemdShellCommand(shell.value.name, execStart.shellCommandArgument);
|
|
243
356
|
if (shellCommand === undefined) {
|
|
244
|
-
return { ok: false, message:
|
|
357
|
+
return { ok: false, message: t("doctor.systemd.an_unrecognized_shell_command", "Installed {arg0} systemd unit has an unrecognized shell command.", { arg0: definition.id }) };
|
|
245
358
|
}
|
|
246
|
-
const environment =
|
|
359
|
+
const environment = new Map();
|
|
247
360
|
for (const directive of directives.filter((item) => item.name === "Environment")) {
|
|
248
361
|
const rawValue = directive.value;
|
|
249
|
-
if (
|
|
362
|
+
if (!isSingleSystemdQuotedValue(rawValue)) {
|
|
250
363
|
return { ok: false, message: `Installed ${definition.id} systemd unit has an unrecognized environment entry.` };
|
|
251
364
|
}
|
|
252
365
|
const assignment = parseSystemdDirectiveValue(rawValue);
|
|
253
366
|
const separator = assignment?.indexOf("=") ?? -1;
|
|
254
367
|
const key = assignment?.slice(0, separator) ?? "";
|
|
255
|
-
if (separator <= 0 ||
|
|
368
|
+
if (separator <= 0 || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key) || environment.has(key)) {
|
|
256
369
|
return { ok: false, message: `Installed ${definition.id} systemd unit has a malformed environment entry.` };
|
|
257
370
|
}
|
|
258
|
-
environment
|
|
371
|
+
environment.set(key, assignment?.slice(separator + 1) ?? "");
|
|
259
372
|
}
|
|
260
373
|
const workingDirectories = directives.filter((directive) => directive.name === "WorkingDirectory");
|
|
261
374
|
if (workingDirectories.length > 1) {
|
|
262
|
-
return { ok: false, message:
|
|
375
|
+
return { ok: false, message: t("doctor.systemd.duplicate_working_directories", "Installed {arg0} systemd unit has duplicate working directories.", { arg0: definition.id }) };
|
|
263
376
|
}
|
|
264
377
|
const rawWorkingDirectory = workingDirectories[0]?.value;
|
|
265
378
|
if (rawWorkingDirectory?.startsWith('"') === true || rawWorkingDirectory?.startsWith("'") === true) {
|
|
266
|
-
return { ok: false, message:
|
|
379
|
+
return { ok: false, message: t("doctor.systemd.an_invalid_quoted_working_directory", "Installed {arg0} systemd unit has an invalid quoted working directory.", { arg0: definition.id }) };
|
|
267
380
|
}
|
|
268
381
|
const workingDirectory = rawWorkingDirectory === undefined
|
|
269
382
|
? null
|
|
270
383
|
: parseSystemdDirectiveValue(rawWorkingDirectory);
|
|
271
384
|
if (workingDirectories.length === 1 && workingDirectory === undefined) {
|
|
272
|
-
return { ok: false, message:
|
|
385
|
+
return { ok: false, message: t("doctor.systemd.a_malformed_working_directory", "Installed {arg0} systemd unit has a malformed working directory.", { arg0: definition.id }) };
|
|
273
386
|
}
|
|
274
387
|
return {
|
|
275
388
|
ok: true,
|
|
276
|
-
value: {
|
|
389
|
+
value: {
|
|
390
|
+
id: definition.id,
|
|
391
|
+
shell: shell.value,
|
|
392
|
+
environment: Object.fromEntries(environment),
|
|
393
|
+
workingDirectory: workingDirectory ?? null,
|
|
394
|
+
shellCommand,
|
|
395
|
+
},
|
|
277
396
|
};
|
|
278
397
|
}
|
|
279
|
-
function parseLaunchdDefinition(definition) {
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
398
|
+
function parseLaunchdDefinition(definition, t = createTranslator("en", {})) {
|
|
399
|
+
const plist = parseLaunchdPlistDocument(definition.contents);
|
|
400
|
+
if (plist === undefined) {
|
|
401
|
+
return { ok: false, message: `Installed ${definition.id} LaunchAgent is not a structurally valid property list.` };
|
|
402
|
+
}
|
|
403
|
+
const label = plistStringValue(plist.get("Label"));
|
|
404
|
+
if (label === undefined) {
|
|
405
|
+
return { ok: false, message: `Installed ${definition.id} LaunchAgent has an unrecognized Label.` };
|
|
406
|
+
}
|
|
407
|
+
const expectedLabel = nativeServiceManagerRefs[definition.id].launchdLabel;
|
|
408
|
+
if (label !== expectedLabel) {
|
|
409
|
+
return {
|
|
410
|
+
ok: false,
|
|
411
|
+
message: `Installed ${definition.id} LaunchAgent declares Label ${JSON.stringify(label)} instead of ${JSON.stringify(expectedLabel)}.`,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
const arguments_ = plistStringArray(plist.get("ProgramArguments"));
|
|
284
415
|
if (arguments_?.length !== 4 || arguments_[0] !== "/usr/bin/env" || arguments_[2] !== "-lc") {
|
|
285
|
-
return { ok: false, message:
|
|
416
|
+
return { ok: false, message: t("doctor.launchd.unrecognized_ProgramArguments", "Installed {arg0} LaunchAgent has unrecognized ProgramArguments.", { arg0: definition.id }) };
|
|
286
417
|
}
|
|
287
|
-
const shell = installedShell(arguments_[1] ?? "");
|
|
418
|
+
const shell = installedShell(arguments_[1] ?? "", t);
|
|
288
419
|
if (!shell.ok)
|
|
289
420
|
return shell;
|
|
290
|
-
const
|
|
291
|
-
const
|
|
292
|
-
if (environmentMatches.length > 1 || environmentKeyCount !== environmentMatches.length) {
|
|
293
|
-
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed environment dictionary.` };
|
|
294
|
-
}
|
|
295
|
-
const environment = environmentMatches.length === 0
|
|
296
|
-
? {}
|
|
297
|
-
: parseXmlStringDictionary(environmentMatches[0]?.[1] ?? "");
|
|
421
|
+
const rawEnvironment = plist.get("EnvironmentVariables");
|
|
422
|
+
const environment = rawEnvironment === undefined ? {} : plistStringDictionary(rawEnvironment);
|
|
298
423
|
if (environment === undefined) {
|
|
299
|
-
return { ok: false, message:
|
|
300
|
-
}
|
|
301
|
-
const contentsWithoutEnvironment = environmentMatches[0]?.[0] === undefined
|
|
302
|
-
? definition.contents
|
|
303
|
-
: definition.contents.replace(environmentMatches[0][0], "");
|
|
304
|
-
const workingDirectoryMatches = [...contentsWithoutEnvironment.matchAll(/<key>WorkingDirectory<\/key>\s*<string>([\s\S]*?)<\/string>/gu)];
|
|
305
|
-
const workingDirectoryKeyCount = [...contentsWithoutEnvironment.matchAll(/<key>WorkingDirectory<\/key>/gu)].length;
|
|
306
|
-
if (workingDirectoryMatches.length > 1 || workingDirectoryKeyCount !== workingDirectoryMatches.length) {
|
|
307
|
-
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed working directory.` };
|
|
424
|
+
return { ok: false, message: t("doctor.launchd.a_malformed_environment_dictionary", "Installed {arg0} LaunchAgent has a malformed environment dictionary.", { arg0: definition.id }) };
|
|
308
425
|
}
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (workingDirectoryMatches.length === 1 && workingDirectory === undefined) {
|
|
426
|
+
const rawWorkingDirectory = plist.get("WorkingDirectory");
|
|
427
|
+
const workingDirectory = rawWorkingDirectory === undefined ? null : plistStringValue(rawWorkingDirectory);
|
|
428
|
+
if (rawWorkingDirectory !== undefined && workingDirectory === undefined) {
|
|
313
429
|
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed working directory.` };
|
|
314
430
|
}
|
|
315
431
|
return {
|
|
@@ -323,10 +439,10 @@ function parseLaunchdDefinition(definition) {
|
|
|
323
439
|
},
|
|
324
440
|
};
|
|
325
441
|
}
|
|
326
|
-
function installedShell(executable) {
|
|
442
|
+
function installedShell(executable, t = createTranslator("en", {})) {
|
|
327
443
|
const name = posixPath.basename(executable).replace(/^-/, "");
|
|
328
444
|
if (name !== "bash" && name !== "zsh" && name !== "fish") {
|
|
329
|
-
return { ok: false, message:
|
|
445
|
+
return { ok: false, message: t("doctor.shellUnsupported", "Installed service definition uses unsupported login shell {arg0}.", { arg0: executable }) };
|
|
330
446
|
}
|
|
331
447
|
return {
|
|
332
448
|
ok: true,
|
|
@@ -334,7 +450,10 @@ function installedShell(executable) {
|
|
|
334
450
|
};
|
|
335
451
|
}
|
|
336
452
|
function parseSystemdExecArgument(value) {
|
|
337
|
-
const
|
|
453
|
+
const quoted = value.startsWith('"') || value.endsWith('"');
|
|
454
|
+
if (quoted ? !isSingleSystemdQuotedValue(value) : /["']/u.test(value))
|
|
455
|
+
return undefined;
|
|
456
|
+
const decoded = decodeSystemdEscapes(quoted ? value.slice(1, -1) : value);
|
|
338
457
|
return decoded === undefined ? undefined : decodeSystemdSubstitutions(decoded, true);
|
|
339
458
|
}
|
|
340
459
|
function parseSystemdDirectiveValue(value) {
|
|
@@ -345,14 +464,20 @@ function parseSystemdEscapedValue(value) {
|
|
|
345
464
|
const quoted = value.startsWith('"') || value.endsWith('"');
|
|
346
465
|
if (quoted && (!value.startsWith('"') || !value.endsWith('"')))
|
|
347
466
|
return undefined;
|
|
348
|
-
return
|
|
467
|
+
return decodeSystemdEscapes(quoted ? value.slice(1, -1) : value);
|
|
349
468
|
}
|
|
350
|
-
function
|
|
351
|
-
|
|
469
|
+
export function decodeSystemdEscapes(value) {
|
|
470
|
+
const bytes = [];
|
|
471
|
+
const encoder = new TextEncoder();
|
|
352
472
|
for (let index = 0; index < value.length; index += 1) {
|
|
353
473
|
const character = value[index];
|
|
354
474
|
if (character !== "\\") {
|
|
355
|
-
|
|
475
|
+
const codePoint = value.codePointAt(index);
|
|
476
|
+
if (codePoint === undefined || codePoint === 0 || (codePoint >= 0xd800 && codePoint <= 0xdfff))
|
|
477
|
+
return undefined;
|
|
478
|
+
bytes.push(...encoder.encode(String.fromCodePoint(codePoint)));
|
|
479
|
+
if (codePoint > 0xffff)
|
|
480
|
+
index += 1;
|
|
356
481
|
continue;
|
|
357
482
|
}
|
|
358
483
|
const escape = value[index + 1];
|
|
@@ -374,23 +499,45 @@ function systemdUnescape(value) {
|
|
|
374
499
|
};
|
|
375
500
|
const simple = simpleEscapes[escape];
|
|
376
501
|
if (simple !== undefined) {
|
|
377
|
-
|
|
502
|
+
bytes.push(...encoder.encode(simple));
|
|
378
503
|
index += 1;
|
|
379
504
|
continue;
|
|
380
505
|
}
|
|
506
|
+
if (/^[0-7]$/u.test(escape)) {
|
|
507
|
+
const encoded = value.slice(index + 1, index + 4);
|
|
508
|
+
if (!/^[0-7]{3}$/u.test(encoded))
|
|
509
|
+
return undefined;
|
|
510
|
+
const decoded = Number.parseInt(encoded, 8);
|
|
511
|
+
if (decoded === 0 || decoded > 0xff)
|
|
512
|
+
return undefined;
|
|
513
|
+
bytes.push(decoded);
|
|
514
|
+
index += 3;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
381
517
|
const length = escape === "x" ? 2 : escape === "u" ? 4 : escape === "U" ? 8 : 0;
|
|
382
518
|
if (length === 0)
|
|
383
519
|
return undefined;
|
|
384
520
|
const encoded = value.slice(index + 2, index + 2 + length);
|
|
385
521
|
if (encoded.length !== length || !new RegExp(`^[0-9a-fA-F]{${String(length)}}$`, "u").test(encoded))
|
|
386
522
|
return undefined;
|
|
387
|
-
const
|
|
388
|
-
if (
|
|
523
|
+
const decoded = Number.parseInt(encoded, 16);
|
|
524
|
+
if (decoded === 0 || decoded > 0x10ffff || (decoded >= 0xd800 && decoded <= 0xdfff))
|
|
389
525
|
return undefined;
|
|
390
|
-
|
|
526
|
+
if (escape === "x")
|
|
527
|
+
bytes.push(decoded);
|
|
528
|
+
else
|
|
529
|
+
bytes.push(...encoder.encode(String.fromCodePoint(decoded)));
|
|
391
530
|
index += length + 1;
|
|
392
531
|
}
|
|
393
|
-
|
|
532
|
+
try {
|
|
533
|
+
// Keep a BOM produced by an escape sequence as U+FEFF. The default
|
|
534
|
+
// decoder behavior strips leading UTF-8 BOM bytes, which could turn an
|
|
535
|
+
// invalid BOM-prefixed environment key into PI_WEB_CONFIG.
|
|
536
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Uint8Array.from(bytes));
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
394
541
|
}
|
|
395
542
|
function decodeSystemdSubstitutions(value, decodeDollars) {
|
|
396
543
|
let result = "";
|
|
@@ -414,7 +561,17 @@ function parseSystemdShellCommand(shell, value) {
|
|
|
414
561
|
return undefined;
|
|
415
562
|
const inner = value.slice(1, -1);
|
|
416
563
|
const unquoted = shell === "fish" ? fishSingleQuoteUnescape(inner) : inner.replaceAll("'\\''", "'");
|
|
417
|
-
|
|
564
|
+
if (legacySystemdShellQuote(shell, unquoted) !== value)
|
|
565
|
+
return undefined;
|
|
566
|
+
// The pre-hardening renderer doubled percent specifiers, but its
|
|
567
|
+
// replacement-string "$$" left dollar characters unchanged.
|
|
568
|
+
return decodeSystemdSubstitutions(unquoted, false);
|
|
569
|
+
}
|
|
570
|
+
function legacySystemdShellQuote(shell, value) {
|
|
571
|
+
const escaped = shell === "fish"
|
|
572
|
+
? value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")
|
|
573
|
+
: value.replaceAll("'", "'\\''");
|
|
574
|
+
return `'${escaped}'`;
|
|
418
575
|
}
|
|
419
576
|
function fishSingleQuoteUnescape(value) {
|
|
420
577
|
let result = "";
|
|
@@ -430,34 +587,181 @@ function fishSingleQuoteUnescape(value) {
|
|
|
430
587
|
}
|
|
431
588
|
return result;
|
|
432
589
|
}
|
|
433
|
-
|
|
590
|
+
const plistXmlDeclaration = '<?xml version="1.0" encoding="UTF-8"?>';
|
|
591
|
+
const plistDoctype = '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">';
|
|
592
|
+
const maximumPlistDepth = 32;
|
|
593
|
+
/** Parse the complete XML plist subset emitted by PI WEB, rejecting fragments and duplicate dictionary keys. */
|
|
594
|
+
function parseLaunchdPlistDocument(contents) {
|
|
595
|
+
const normalizedContents = normalizePlistXml(contents);
|
|
596
|
+
if (normalizedContents === undefined)
|
|
597
|
+
return undefined;
|
|
598
|
+
const cursor = {
|
|
599
|
+
contents: normalizedContents,
|
|
600
|
+
offset: normalizedContents.startsWith("\uFEFF") ? 1 : 0,
|
|
601
|
+
};
|
|
602
|
+
skipPlistWhitespace(cursor);
|
|
603
|
+
if (consumePlistToken(cursor, plistXmlDeclaration))
|
|
604
|
+
skipPlistWhitespace(cursor);
|
|
605
|
+
else if (cursor.contents.startsWith("<?xml", cursor.offset))
|
|
606
|
+
return undefined;
|
|
607
|
+
if (consumePlistToken(cursor, plistDoctype))
|
|
608
|
+
skipPlistWhitespace(cursor);
|
|
609
|
+
else if (cursor.contents.startsWith("<!DOCTYPE", cursor.offset))
|
|
610
|
+
return undefined;
|
|
611
|
+
if (!consumePlistToken(cursor, '<plist version="1.0">'))
|
|
612
|
+
return undefined;
|
|
613
|
+
const root = parsePlistValue(cursor, 0);
|
|
614
|
+
if (root?.kind !== "dictionary")
|
|
615
|
+
return undefined;
|
|
616
|
+
skipPlistWhitespace(cursor);
|
|
617
|
+
if (!consumePlistToken(cursor, "</plist>"))
|
|
618
|
+
return undefined;
|
|
619
|
+
skipPlistWhitespace(cursor);
|
|
620
|
+
return cursor.offset === cursor.contents.length ? root.value : undefined;
|
|
621
|
+
}
|
|
622
|
+
function parsePlistValue(cursor, depth) {
|
|
623
|
+
if (depth > maximumPlistDepth)
|
|
624
|
+
return undefined;
|
|
625
|
+
skipPlistWhitespace(cursor);
|
|
626
|
+
if (cursor.contents.startsWith("<string>", cursor.offset)) {
|
|
627
|
+
const value = parsePlistTextElement(cursor, "string");
|
|
628
|
+
return value === undefined ? undefined : { kind: "string", value };
|
|
629
|
+
}
|
|
630
|
+
for (const tag of ["data", "date", "integer", "real"]) {
|
|
631
|
+
if (!cursor.contents.startsWith(`<${tag}>`, cursor.offset))
|
|
632
|
+
continue;
|
|
633
|
+
const value = parsePlistTextElement(cursor, tag);
|
|
634
|
+
return value === undefined || !isValidPlistScalarValue(tag, value)
|
|
635
|
+
? undefined
|
|
636
|
+
: { kind: "scalar", value };
|
|
637
|
+
}
|
|
638
|
+
if (cursor.contents.startsWith("<array", cursor.offset))
|
|
639
|
+
return parsePlistArray(cursor, depth);
|
|
640
|
+
if (cursor.contents.startsWith("<dict", cursor.offset))
|
|
641
|
+
return parsePlistDictionary(cursor, depth);
|
|
642
|
+
if (consumePlistToken(cursor, "<true/>"))
|
|
643
|
+
return { kind: "boolean", value: true };
|
|
644
|
+
if (consumePlistToken(cursor, "<false/>"))
|
|
645
|
+
return { kind: "boolean", value: false };
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
function parsePlistArray(cursor, depth) {
|
|
649
|
+
if (consumePlistToken(cursor, "<array/>"))
|
|
650
|
+
return { kind: "array", value: [] };
|
|
651
|
+
if (!consumePlistToken(cursor, "<array>"))
|
|
652
|
+
return undefined;
|
|
434
653
|
const values = [];
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if (
|
|
438
|
-
return
|
|
439
|
-
const value =
|
|
654
|
+
for (;;) {
|
|
655
|
+
skipPlistWhitespace(cursor);
|
|
656
|
+
if (consumePlistToken(cursor, "</array>"))
|
|
657
|
+
return { kind: "array", value: values };
|
|
658
|
+
const value = parsePlistValue(cursor, depth + 1);
|
|
440
659
|
if (value === undefined)
|
|
441
660
|
return undefined;
|
|
442
661
|
values.push(value);
|
|
443
|
-
cursor = match.index + match[0].length;
|
|
444
662
|
}
|
|
445
|
-
return contents.slice(cursor).trim() === "" ? values : undefined;
|
|
446
663
|
}
|
|
447
|
-
function
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
664
|
+
function parsePlistDictionary(cursor, depth) {
|
|
665
|
+
if (consumePlistToken(cursor, "<dict/>"))
|
|
666
|
+
return { kind: "dictionary", value: new Map() };
|
|
667
|
+
if (!consumePlistToken(cursor, "<dict>"))
|
|
668
|
+
return undefined;
|
|
669
|
+
const values = new Map();
|
|
670
|
+
for (;;) {
|
|
671
|
+
skipPlistWhitespace(cursor);
|
|
672
|
+
if (consumePlistToken(cursor, "</dict>"))
|
|
673
|
+
return { kind: "dictionary", value: values };
|
|
674
|
+
const key = parsePlistTextElement(cursor, "key");
|
|
675
|
+
if (key === undefined || values.has(key))
|
|
676
|
+
return undefined;
|
|
677
|
+
const value = parsePlistValue(cursor, depth + 1);
|
|
678
|
+
if (value === undefined)
|
|
679
|
+
return undefined;
|
|
680
|
+
values.set(key, value);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function parsePlistTextElement(cursor, tag) {
|
|
684
|
+
const openingTag = `<${tag}>`;
|
|
685
|
+
const closingTag = `</${tag}>`;
|
|
686
|
+
if (!consumePlistToken(cursor, openingTag))
|
|
687
|
+
return undefined;
|
|
688
|
+
const closingOffset = cursor.contents.indexOf(closingTag, cursor.offset);
|
|
689
|
+
if (closingOffset < 0)
|
|
690
|
+
return undefined;
|
|
691
|
+
const encoded = cursor.contents.slice(cursor.offset, closingOffset);
|
|
692
|
+
cursor.offset = closingOffset + closingTag.length;
|
|
693
|
+
return xmlUnescapeStrict(encoded);
|
|
694
|
+
}
|
|
695
|
+
function normalizePlistXml(contents) {
|
|
696
|
+
for (const character of contents) {
|
|
697
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
698
|
+
const isXmlCharacter = codePoint === 0x09
|
|
699
|
+
|| codePoint === 0x0a
|
|
700
|
+
|| codePoint === 0x0d
|
|
701
|
+
|| (codePoint >= 0x20 && codePoint <= 0xd7ff)
|
|
702
|
+
|| (codePoint >= 0xe000 && codePoint <= 0xfffd)
|
|
703
|
+
|| (codePoint >= 0x10000 && codePoint <= 0x10ffff);
|
|
704
|
+
if (!isXmlCharacter)
|
|
705
|
+
return undefined;
|
|
706
|
+
}
|
|
707
|
+
return contents.replace(/\r\n?/gu, "\n");
|
|
708
|
+
}
|
|
709
|
+
function isValidPlistScalarValue(tag, value) {
|
|
710
|
+
if (tag === "data") {
|
|
711
|
+
const compact = value.replace(/[\t\n\r ]/gu, "");
|
|
712
|
+
return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(compact);
|
|
713
|
+
}
|
|
714
|
+
if (tag === "date") {
|
|
715
|
+
const match = /^(\d{4})-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/u.exec(value);
|
|
716
|
+
if (match?.[1] === undefined || Number(match[1]) === 0)
|
|
717
|
+
return false;
|
|
718
|
+
const timestamp = Date.parse(value);
|
|
719
|
+
return Number.isFinite(timestamp)
|
|
720
|
+
&& new Date(timestamp).toISOString() === `${value.slice(0, -1)}.000Z`;
|
|
721
|
+
}
|
|
722
|
+
if (tag === "integer") {
|
|
723
|
+
if (!/^[+-]?(?:0|[1-9][0-9]*)$/u.test(value))
|
|
724
|
+
return false;
|
|
725
|
+
const parsed = BigInt(value);
|
|
726
|
+
return parsed >= -(2n ** 63n) && parsed <= (2n ** 63n) - 1n;
|
|
727
|
+
}
|
|
728
|
+
return /^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/u.test(value)
|
|
729
|
+
&& Number.isFinite(Number(value));
|
|
730
|
+
}
|
|
731
|
+
function skipPlistWhitespace(cursor) {
|
|
732
|
+
while (/[\t\n\r ]/u.test(cursor.contents[cursor.offset] ?? ""))
|
|
733
|
+
cursor.offset += 1;
|
|
734
|
+
}
|
|
735
|
+
function consumePlistToken(cursor, token) {
|
|
736
|
+
if (!cursor.contents.startsWith(token, cursor.offset))
|
|
737
|
+
return false;
|
|
738
|
+
cursor.offset += token.length;
|
|
739
|
+
return true;
|
|
740
|
+
}
|
|
741
|
+
function plistStringValue(value) {
|
|
742
|
+
return value?.kind === "string" ? value.value : undefined;
|
|
743
|
+
}
|
|
744
|
+
function plistStringArray(value) {
|
|
745
|
+
if (value?.kind !== "array")
|
|
746
|
+
return undefined;
|
|
747
|
+
const strings = [];
|
|
748
|
+
for (const item of value.value) {
|
|
749
|
+
if (item.kind !== "string")
|
|
452
750
|
return undefined;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
751
|
+
strings.push(item.value);
|
|
752
|
+
}
|
|
753
|
+
return strings;
|
|
754
|
+
}
|
|
755
|
+
function plistStringDictionary(value) {
|
|
756
|
+
if (value.kind !== "dictionary")
|
|
757
|
+
return undefined;
|
|
758
|
+
const entries = [];
|
|
759
|
+
for (const [key, item] of value.value) {
|
|
760
|
+
if (item.kind !== "string")
|
|
456
761
|
return undefined;
|
|
457
|
-
|
|
458
|
-
cursor = match.index + match[0].length;
|
|
762
|
+
entries.push([key, item.value]);
|
|
459
763
|
}
|
|
460
|
-
return
|
|
764
|
+
return Object.fromEntries(entries);
|
|
461
765
|
}
|
|
462
766
|
function xmlUnescapeStrict(value) {
|
|
463
767
|
if (/[<>]/u.test(value) || /&(?!(?:apos|quot|gt|lt|amp);)/u.test(value))
|
|
@@ -472,7 +776,7 @@ function xmlUnescapeStrict(value) {
|
|
|
472
776
|
function recordsEqual(left, right) {
|
|
473
777
|
const leftEntries = Object.entries(left);
|
|
474
778
|
return leftEntries.length === Object.keys(right).length
|
|
475
|
-
&& leftEntries.every(([key, value]) => right[key] === value);
|
|
779
|
+
&& leftEntries.every(([key, value]) => Object.hasOwn(right, key) && right[key] === value);
|
|
476
780
|
}
|
|
477
781
|
function impossibleMissingDefinition() {
|
|
478
782
|
throw new Error("Expected at least one installed native service definition");
|