@chainingintention/pi-web-cn 1.202610.1 → 1.202610.3
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 +60 -106
- package/dist/.plugins-ready +1 -0
- package/dist/cli.js +161 -45
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-CFhaGb5m.js +4357 -0
- package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
- package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
- package/dist/client/index.html +5 -5
- package/dist/config.js +30 -6
- package/dist/config.js.map +1 -1
- package/dist/docker/piWebDockerCommandPlan.js +13 -4
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- package/dist/nativeServices/installedServiceDefinitions.js +23 -10
- package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
- package/dist/nativeServices/serviceAction.js +78 -20
- package/dist/nativeServices/serviceAction.js.map +1 -1
- package/dist/nativeServices/serviceDoctor.js +339 -67
- 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 +146 -0
- package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
- package/dist/pi-packages/captains-log/dist/browser/panel.js +151 -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/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/pi-web-plugin.js +9 -9
- package/dist/pi-packages/relays/prompts/relay.md +1 -1
- package/dist/pi-packages/relays/relaysPanelElement.js +8 -8
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
- 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 +456 -0
- package/dist/pi-web-plugins/files/package.json +15 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +55 -32
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +2 -2
- package/dist/pi-web-plugins/git/git-backend.js +19 -26
- package/dist/pi-web-plugins/git/gitStatusCache.js +48 -0
- package/dist/pi-web-plugins/git/package.json +1 -0
- package/dist/pi-web-plugins/git/server-plugin.js +11 -10
- package/dist/pi-web-plugins/info/infoInternals.js +4 -1
- package/dist/pi-web-plugins/info/package.json +1 -1
- package/dist/pi-web-plugins/info/pi-web-plugin.js +8 -8
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
- package/dist/pi-web-plugins/mermaid/package.json +13 -0
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
- package/dist/pi-web-plugins/terminal/package.json +16 -0
- package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
- package/dist/pi-web-plugins/terminal/terminalService.js +435 -0
- package/dist/pi-web-plugins/updates/package.json +1 -1
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +69 -68
- 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 +47 -24
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +260 -25
- package/dist/plugin-api.js +1 -0
- package/dist/pluginRecoveryCli.js +3 -2
- package/dist/pluginRecoveryCli.js.map +1 -1
- package/dist/server/activity/workspaceActivityService.js +5 -5
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +71 -44
- package/dist/server/app.js.map +1 -1
- package/dist/server/audit/managementAuditStore.js +1 -1
- package/dist/server/browserMessageProjection.js +0 -2
- package/dist/server/browserMessageProjection.js.map +1 -1
- package/dist/server/configRoutes.js +1 -2
- package/dist/server/configRoutes.js.map +1 -1
- package/dist/server/knownPiPackages.js +12 -0
- package/dist/server/knownPiPackages.js.map +1 -0
- package/dist/server/machines/machineClient.js +2 -2
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +120 -21
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +80 -22
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineService.js +22 -0
- package/dist/server/machines/machineService.js.map +1 -1
- package/dist/server/managementEmbed.js +1 -1
- package/dist/server/managementEmbed.js.map +1 -1
- package/dist/server/managementPrivileges.js +7 -1
- package/dist/server/managementPrivileges.js.map +1 -1
- package/dist/server/managementTerminalProxyRoutes.js +124 -0
- package/dist/server/managementTerminalProxyRoutes.js.map +1 -0
- package/dist/server/normalAuth.js +5 -1
- package/dist/server/normalAuth.js.map +1 -1
- package/dist/server/notices/serverNoticeStore.js +50 -6
- package/dist/server/notices/serverNoticeStore.js.map +1 -1
- package/dist/server/piPackageService.js +5 -5
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +211 -34
- 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 +176 -291
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/piWebStatus.js +45 -55
- 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 +63 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js +7 -4
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendRegistry.js +773 -0
- package/dist/server/plugins/pluginBackendRegistry.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 +1049 -107
- 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 +84 -0
- package/dist/server/projects/projectLifecycleService.js.map +1 -0
- package/dist/server/projects/projectService.js +2 -0
- package/dist/server/projects/projectService.js.map +1 -1
- package/dist/server/realtime/sessionEventHub.js +35 -41
- package/dist/server/realtime/sessionEventHub.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/pluginBackendChannelRoutes.js +397 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +19 -9
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
- package/dist/server/sessiond/projectMutationRoutes.js +38 -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 +47 -15
- package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionServiceDependencies.js +4 -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/workspaceRemovalRoutes.js +1 -1
- package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -1
- package/dist/server/sessiond.js +226 -56
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/askUserTool.js +0 -3
- package/dist/server/sessions/askUserTool.js.map +1 -1
- package/dist/server/sessions/attachmentService.js +0 -1
- package/dist/server/sessions/attachmentService.js.map +1 -1
- package/dist/server/sessions/authService.js +8 -1
- package/dist/server/sessions/authService.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +2 -2
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/clientSessionPreview.js +14 -0
- package/dist/server/sessions/clientSessionPreview.js.map +1 -0
- package/dist/server/sessions/dockerEnvironmentFacts.js +82 -9
- package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/managementAgentTools.js +11 -6
- package/dist/server/sessions/managementAgentTools.js.map +1 -1
- package/dist/server/sessions/managementPermissionSystem.js +16 -4
- package/dist/server/sessions/managementPermissionSystem.js.map +1 -1
- 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 +2 -5
- package/dist/server/sessions/oauthLoginFlowService.js.map +1 -1
- package/dist/server/sessions/pendingAskStore.js +0 -3
- package/dist/server/sessions/pendingAskStore.js.map +1 -1
- package/dist/server/sessions/piSessionEventConnections.js +65 -0
- package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
- package/dist/server/sessions/piSessionManagerGateway.js +56 -3
- package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
- package/dist/server/sessions/piSessionService.js +774 -448
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/projectAuthService.js +29 -2
- package/dist/server/sessions/projectAuthService.js.map +1 -1
- package/dist/server/sessions/projectSettingsScope.js +12 -4
- 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/sessionActivityMarker.js +105 -0
- package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
- package/dist/server/sessions/sessionArchiveMapping.js +2 -8
- package/dist/server/sessions/sessionArchiveMapping.js.map +1 -1
- package/dist/server/sessions/sessionCommandService.js +12 -10
- 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/sessionModelScope.js +42 -5
- package/dist/server/sessions/sessionModelScope.js.map +1 -1
- package/dist/server/sessions/sessionNameGenerator.js +6 -6
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +63 -15
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/sessionTreeProjection.js +29 -29
- package/dist/server/sessions/sessionTreeProjection.js.map +1 -1
- package/dist/server/sessions/sessionUiEvents.js +6 -7
- package/dist/server/sessions/sessionUiEvents.js.map +1 -1
- package/dist/server/sessions/sessionUnreadStore.js +26 -1
- package/dist/server/sessions/sessionUnreadStore.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +3 -3
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +5 -5
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/spawnTargetResolver.js +1 -1
- package/dist/server/sessions/transcriptMessages.js +54 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -0
- package/dist/server/status/machineStatusService.js +3 -1
- package/dist/server/status/machineStatusService.js.map +1 -1
- package/dist/server/terminals/{terminalService.js → managementCommandRunService.js} +79 -107
- 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 +339 -0
- package/dist/server/webSocketBridge.js.map +1 -1
- package/dist/server/workbench/gatewayIntegration.js +4 -1
- package/dist/server/workbench/gatewayIntegration.js.map +1 -1
- package/dist/server/workspaceExplorerRoutes.js +13 -12
- package/dist/server/workspaceExplorerRoutes.js.map +1 -1
- package/dist/server/workspaces/fileContentService.js +9 -7
- package/dist/server/workspaces/fileContentService.js.map +1 -1
- package/dist/server/workspaces/fileOperationService.js +4 -3
- package/dist/server/workspaces/fileOperationService.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 +36 -12
- 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/workspaceDeletionRoutes.js +5 -57
- package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +75 -24
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server/workspaces/workspaceRemovalService.js +25 -3
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
- package/dist/server-plugin-api.d.ts +274 -44
- package/dist/server-plugin-api.js +319 -2
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/serverPluginRecovery.js +4 -0
- 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 +29 -8
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- 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 +7 -2
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/capabilities.js +5 -0
- package/dist/shared/capabilities.js.map +1 -1
- package/dist/shared/federatedRoutes.js +49 -9
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machinePluginIds.js +30 -7
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/machineStatus.js +35 -1
- package/dist/shared/machineStatus.js.map +1 -1
- package/dist/shared/piWebStatusParsing.js +29 -0
- package/dist/shared/piWebStatusParsing.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +24 -9
- package/dist/shared/pluginApiTypes.js +2 -0
- package/dist/shared/pluginApiTypes.js.map +1 -0
- package/dist/shared/pluginBackendProtocol.js +163 -2
- package/dist/shared/pluginBackendProtocol.js.map +1 -1
- 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 +41 -0
- package/dist/shared/sessionDefaults.js.map +1 -0
- package/dist/shared/workspaceFiles.js.map +1 -1
- package/docs/config.md +270 -323
- package/docs/plugins.md +171 -956
- 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/package.json +22 -13
- package/skills/README.md +27 -2
- package/skills/relay-runner/SKILL.md +21 -13
- package/dist/client/assets/CodeViewer-CZHnKRwQ.js +0 -4
- package/dist/client/assets/TerminalPanel-B_EaRPHY.js +0 -187
- package/dist/client/assets/index-Bv1v-Jz7.js +0 -4717
- 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/server/git/gitService.js +0 -316
- 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/terminalProxyRoutes.js +0 -181
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serviceAction.js","sourceRoot":"","sources":["../../src/nativeServices/serviceAction.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"serviceAction.js","sourceRoot":"","sources":["../../src/nativeServices/serviceAction.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { posix as posixPath } from "node:path";
|
|
2
2
|
import { TextDecoder, TextEncoder } from "node:util";
|
|
3
|
-
import {
|
|
3
|
+
import { ownEnvironmentValue } from "../environment.js";
|
|
4
|
+
import { createDevelopmentNativeServicePlan, nativeServiceManagerRefs, nativeServicePrerequisiteNeedsPathAdvice, resolveProductionNativeServicePlan, validateNativeServicePlan, } from "./servicePlan.js";
|
|
4
5
|
export function inferInstalledNativeServiceMode(serviceIds) {
|
|
5
6
|
if (serviceIds.size === 0)
|
|
6
7
|
return "none";
|
|
@@ -12,6 +13,42 @@ export function inferInstalledNativeServiceMode(serviceIds) {
|
|
|
12
13
|
return "development";
|
|
13
14
|
return "ambiguous";
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Select the config path for a caller that is about to probe managed native
|
|
18
|
+
* services. A nonempty caller value is an explicit override and deliberately
|
|
19
|
+
* avoids interpreting installed files. Otherwise every relevant definition
|
|
20
|
+
* must parse to one consistent environment before its persisted path is used.
|
|
21
|
+
*/
|
|
22
|
+
export function selectManagedNativeServiceConfig(backend, definitions, callerConfigPath) {
|
|
23
|
+
if (callerConfigPath !== undefined && callerConfigPath !== "") {
|
|
24
|
+
return { ok: true, value: { source: "caller", configPath: callerConfigPath } };
|
|
25
|
+
}
|
|
26
|
+
if (definitions.length === 0)
|
|
27
|
+
return { ok: true, value: { source: "default" } };
|
|
28
|
+
const parsed = parseConsistentDefinitions(backend, definitions);
|
|
29
|
+
if (!parsed.ok)
|
|
30
|
+
return parsed;
|
|
31
|
+
const firstEnvironment = parsed.value[0]?.environment;
|
|
32
|
+
const installedConfigPath = firstEnvironment === undefined
|
|
33
|
+
? undefined
|
|
34
|
+
: ownEnvironmentValue(firstEnvironment, "PI_WEB_CONFIG");
|
|
35
|
+
if (installedConfigPath === undefined || installedConfigPath === "") {
|
|
36
|
+
return { ok: true, value: { source: "default" } };
|
|
37
|
+
}
|
|
38
|
+
if (!posixPath.isAbsolute(installedConfigPath)) {
|
|
39
|
+
return {
|
|
40
|
+
ok: false,
|
|
41
|
+
message: `Installed service definitions declare relative PI_WEB_CONFIG ${JSON.stringify(installedConfigPath)}; managed config paths must be absolute.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { ok: true, value: { source: "installed", configPath: installedConfigPath } };
|
|
45
|
+
}
|
|
46
|
+
export function inspectInstalledNativeServiceDefinitionEnvironment(backend, definition) {
|
|
47
|
+
const parsed = backend.kind === "systemd"
|
|
48
|
+
? parseSystemdDefinition(definition)
|
|
49
|
+
: parseLaunchdDefinition(definition);
|
|
50
|
+
return parsed.ok ? { ok: true, value: parsed.value.environment } : parsed;
|
|
51
|
+
}
|
|
15
52
|
export function inspectInstalledProductionServiceContext(backend, definitions) {
|
|
16
53
|
const parsed = parseConsistentDefinitions(backend, definitions);
|
|
17
54
|
if (!parsed.ok)
|
|
@@ -172,10 +209,6 @@ function scopeHeading(scope) {
|
|
|
172
209
|
return "Installed development native-service plan:";
|
|
173
210
|
return `Prospective production native-service plan (${scope.reason ?? "installed strategy is unknown"}):`;
|
|
174
211
|
}
|
|
175
|
-
export function inspectInstalledNativeServiceDefinitionEnvironment(backend, definition) {
|
|
176
|
-
const parsed = backend.kind === "systemd" ? parseSystemdDefinition(definition) : parseLaunchdDefinition(definition);
|
|
177
|
-
return parsed.ok ? { ok: true, value: parsed.value.environment } : parsed;
|
|
178
|
-
}
|
|
179
212
|
function parseConsistentDefinitions(backend, definitions) {
|
|
180
213
|
if (definitions.length === 0)
|
|
181
214
|
return { ok: false, message: "No installed service definitions were provided." };
|
|
@@ -208,7 +241,9 @@ function systemdServiceDirectives(contents) {
|
|
|
208
241
|
let inServiceSection = false;
|
|
209
242
|
let foundServiceSection = false;
|
|
210
243
|
for (const line of contents.split(/\r?\n/u)) {
|
|
211
|
-
|
|
244
|
+
// systemd syntax trims ASCII spaces and tabs here; JavaScript's trim/\s
|
|
245
|
+
// would also consume Unicode whitespace that systemd treats as key text.
|
|
246
|
+
const trimmed = line.replace(/^[ \t]+|[ \t]+$/gu, "");
|
|
212
247
|
if (/^\[[^\]]+\]$/u.test(trimmed)) {
|
|
213
248
|
inServiceSection = trimmed === "[Service]";
|
|
214
249
|
foundServiceSection ||= inServiceSection;
|
|
@@ -216,53 +251,124 @@ function systemdServiceDirectives(contents) {
|
|
|
216
251
|
}
|
|
217
252
|
if (!inServiceSection || trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith(";"))
|
|
218
253
|
continue;
|
|
219
|
-
|
|
254
|
+
// Slice after the recognized prefix so JavaScript-only line separators remain directive data.
|
|
255
|
+
const match = /^[ \t]*([A-Za-z][A-Za-z0-9]*)=/u.exec(line);
|
|
220
256
|
const name = match?.[1];
|
|
221
|
-
|
|
222
|
-
if (name === undefined || value === undefined || !allowed.has(name))
|
|
257
|
+
if (match === null || name === undefined || !allowed.has(name))
|
|
223
258
|
return undefined;
|
|
224
|
-
directives.push({ name, value });
|
|
259
|
+
directives.push({ name, value: line.slice(match[0].length) });
|
|
225
260
|
}
|
|
226
261
|
return foundServiceSection ? directives : undefined;
|
|
227
262
|
}
|
|
263
|
+
function hasSystemdPhysicalLineContinuation(line) {
|
|
264
|
+
let trailingBackslashes = 0;
|
|
265
|
+
for (let index = line.length - 1; index >= 0 && line[index] === "\\"; index -= 1)
|
|
266
|
+
trailingBackslashes += 1;
|
|
267
|
+
return trailingBackslashes % 2 === 1;
|
|
268
|
+
}
|
|
269
|
+
/** Parse PI WEB's bounded ExecStart argument structure, including persisted legacy encodings. */
|
|
270
|
+
function parseSystemdExecStart(value) {
|
|
271
|
+
const environmentPrefix = "/usr/bin/env ";
|
|
272
|
+
const shellOffset = value.startsWith(environmentPrefix) ? environmentPrefix.length : 0;
|
|
273
|
+
const shell = parseSystemdExecStartArgument(value, shellOffset);
|
|
274
|
+
if (shell === undefined)
|
|
275
|
+
return undefined;
|
|
276
|
+
const loginShellSeparator = " -lc ";
|
|
277
|
+
if (!value.startsWith(loginShellSeparator, shell.endOffset))
|
|
278
|
+
return undefined;
|
|
279
|
+
const shellCommandOffset = shell.endOffset + loginShellSeparator.length;
|
|
280
|
+
if (shellCommandOffset >= value.length)
|
|
281
|
+
return undefined;
|
|
282
|
+
return {
|
|
283
|
+
shellArgument: shell.value,
|
|
284
|
+
shellCommandArgument: value.slice(shellCommandOffset),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/** Scan one systemd argument linearly so delimiters inside quoted values remain data. */
|
|
288
|
+
function parseSystemdExecStartArgument(value, offset) {
|
|
289
|
+
if (offset >= value.length || value[offset] === " " || value[offset] === "\t")
|
|
290
|
+
return undefined;
|
|
291
|
+
let quote = null;
|
|
292
|
+
for (let index = offset; index < value.length; index += 1) {
|
|
293
|
+
const character = value[index];
|
|
294
|
+
if (character === "\\") {
|
|
295
|
+
index += 1;
|
|
296
|
+
if (index >= value.length)
|
|
297
|
+
return undefined;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
if (quote === null && (character === " " || character === "\t")) {
|
|
301
|
+
return { value: value.slice(offset, index), endOffset: index };
|
|
302
|
+
}
|
|
303
|
+
if (character !== "\"" && character !== "'")
|
|
304
|
+
continue;
|
|
305
|
+
if (quote === null)
|
|
306
|
+
quote = character;
|
|
307
|
+
else if (character === quote)
|
|
308
|
+
quote = null;
|
|
309
|
+
}
|
|
310
|
+
return quote === null ? { value: value.slice(offset), endOffset: value.length } : undefined;
|
|
311
|
+
}
|
|
312
|
+
// Keep malformed installed input linear; overlapping escaped/raw regex alternatives can backtrack exponentially.
|
|
313
|
+
function isSingleSystemdQuotedValue(value) {
|
|
314
|
+
if (value.length < 2 || !value.startsWith('"') || !value.endsWith('"'))
|
|
315
|
+
return false;
|
|
316
|
+
const closingQuoteIndex = value.length - 1;
|
|
317
|
+
for (let index = 1; index < closingQuoteIndex; index += 1) {
|
|
318
|
+
const character = value[index];
|
|
319
|
+
if (character === '"')
|
|
320
|
+
return false;
|
|
321
|
+
if (character !== "\\")
|
|
322
|
+
continue;
|
|
323
|
+
index += 1;
|
|
324
|
+
if (index >= closingQuoteIndex)
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
228
329
|
function parseSystemdDefinition(definition) {
|
|
330
|
+
if (definition.contents.split(/\r?\n/u).some(hasSystemdPhysicalLineContinuation)) {
|
|
331
|
+
return {
|
|
332
|
+
ok: false,
|
|
333
|
+
message: `Installed ${definition.id} systemd unit uses physical-line continuation, which cannot be inspected safely.`,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
229
336
|
const directives = systemdServiceDirectives(definition.contents);
|
|
230
337
|
if (directives === undefined) {
|
|
231
338
|
return { ok: false, message: `Installed ${definition.id} systemd unit has unrecognized service directives.` };
|
|
232
339
|
}
|
|
233
340
|
const execStarts = directives.filter((directive) => directive.name === "ExecStart");
|
|
234
341
|
const execStart = execStarts.length === 1
|
|
235
|
-
?
|
|
236
|
-
:
|
|
237
|
-
if (execStart
|
|
342
|
+
? parseSystemdExecStart(execStarts[0]?.value ?? "")
|
|
343
|
+
: undefined;
|
|
344
|
+
if (execStart === undefined) {
|
|
238
345
|
return { ok: false, message: `Installed ${definition.id} systemd unit must have exactly one recognized ExecStart.` };
|
|
239
346
|
}
|
|
240
|
-
const shellExecutable = parseSystemdExecArgument(execStart
|
|
347
|
+
const shellExecutable = parseSystemdExecArgument(execStart.shellArgument);
|
|
241
348
|
if (shellExecutable === undefined) {
|
|
242
349
|
return { ok: false, message: `Installed ${definition.id} systemd unit has an unrecognized login shell argument.` };
|
|
243
350
|
}
|
|
244
351
|
const shell = installedShell(shellExecutable);
|
|
245
352
|
if (!shell.ok)
|
|
246
353
|
return shell;
|
|
247
|
-
const shellCommand = parseSystemdShellCommand(shell.value.name, execStart
|
|
354
|
+
const shellCommand = parseSystemdShellCommand(shell.value.name, execStart.shellCommandArgument);
|
|
248
355
|
if (shellCommand === undefined) {
|
|
249
356
|
return { ok: false, message: `Installed ${definition.id} systemd unit has an unrecognized shell command.` };
|
|
250
357
|
}
|
|
251
|
-
const
|
|
358
|
+
const environment = new Map();
|
|
252
359
|
for (const directive of directives.filter((item) => item.name === "Environment")) {
|
|
253
360
|
const rawValue = directive.value;
|
|
254
|
-
if (
|
|
361
|
+
if (!isSingleSystemdQuotedValue(rawValue)) {
|
|
255
362
|
return { ok: false, message: `Installed ${definition.id} systemd unit has an unrecognized environment entry.` };
|
|
256
363
|
}
|
|
257
364
|
const assignment = parseSystemdDirectiveValue(rawValue);
|
|
258
365
|
const separator = assignment?.indexOf("=") ?? -1;
|
|
259
366
|
const key = assignment?.slice(0, separator) ?? "";
|
|
260
|
-
if (separator <= 0 ||
|
|
367
|
+
if (separator <= 0 || !/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key) || environment.has(key)) {
|
|
261
368
|
return { ok: false, message: `Installed ${definition.id} systemd unit has a malformed environment entry.` };
|
|
262
369
|
}
|
|
263
|
-
|
|
370
|
+
environment.set(key, assignment?.slice(separator + 1) ?? "");
|
|
264
371
|
}
|
|
265
|
-
const environment = Object.fromEntries(environmentEntries);
|
|
266
372
|
const workingDirectories = directives.filter((directive) => directive.name === "WorkingDirectory");
|
|
267
373
|
if (workingDirectories.length > 1) {
|
|
268
374
|
return { ok: false, message: `Installed ${definition.id} systemd unit has duplicate working directories.` };
|
|
@@ -279,43 +385,46 @@ function parseSystemdDefinition(definition) {
|
|
|
279
385
|
}
|
|
280
386
|
return {
|
|
281
387
|
ok: true,
|
|
282
|
-
value: {
|
|
388
|
+
value: {
|
|
389
|
+
id: definition.id,
|
|
390
|
+
shell: shell.value,
|
|
391
|
+
environment: Object.fromEntries(environment),
|
|
392
|
+
workingDirectory: workingDirectory ?? null,
|
|
393
|
+
shellCommand,
|
|
394
|
+
},
|
|
283
395
|
};
|
|
284
396
|
}
|
|
285
397
|
function parseLaunchdDefinition(definition) {
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
398
|
+
const plist = parseLaunchdPlistDocument(definition.contents);
|
|
399
|
+
if (plist === undefined) {
|
|
400
|
+
return { ok: false, message: `Installed ${definition.id} LaunchAgent is not a structurally valid property list.` };
|
|
401
|
+
}
|
|
402
|
+
const label = plistStringValue(plist.get("Label"));
|
|
403
|
+
if (label === undefined) {
|
|
404
|
+
return { ok: false, message: `Installed ${definition.id} LaunchAgent has an unrecognized Label.` };
|
|
405
|
+
}
|
|
406
|
+
const expectedLabel = nativeServiceManagerRefs[definition.id].launchdLabel;
|
|
407
|
+
if (label !== expectedLabel) {
|
|
408
|
+
return {
|
|
409
|
+
ok: false,
|
|
410
|
+
message: `Installed ${definition.id} LaunchAgent declares Label ${JSON.stringify(label)} instead of ${JSON.stringify(expectedLabel)}.`,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
const arguments_ = plistStringArray(plist.get("ProgramArguments"));
|
|
290
414
|
if (arguments_?.length !== 4 || arguments_[0] !== "/usr/bin/env" || arguments_[2] !== "-lc") {
|
|
291
415
|
return { ok: false, message: `Installed ${definition.id} LaunchAgent has unrecognized ProgramArguments.` };
|
|
292
416
|
}
|
|
293
417
|
const shell = installedShell(arguments_[1] ?? "");
|
|
294
418
|
if (!shell.ok)
|
|
295
419
|
return shell;
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
if (environmentMatches.length > 1 || environmentKeyCount !== environmentMatches.length) {
|
|
299
|
-
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed environment dictionary.` };
|
|
300
|
-
}
|
|
301
|
-
const environment = environmentMatches.length === 0
|
|
302
|
-
? {}
|
|
303
|
-
: parseXmlStringDictionary(environmentMatches[0]?.[1] ?? "");
|
|
420
|
+
const rawEnvironment = plist.get("EnvironmentVariables");
|
|
421
|
+
const environment = rawEnvironment === undefined ? {} : plistStringDictionary(rawEnvironment);
|
|
304
422
|
if (environment === undefined) {
|
|
305
423
|
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed environment dictionary.` };
|
|
306
424
|
}
|
|
307
|
-
const
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
const workingDirectoryMatches = [...contentsWithoutEnvironment.matchAll(/<key>WorkingDirectory<\/key>\s*<string>([\s\S]*?)<\/string>/gu)];
|
|
311
|
-
const workingDirectoryKeyCount = [...contentsWithoutEnvironment.matchAll(/<key>WorkingDirectory<\/key>/gu)].length;
|
|
312
|
-
if (workingDirectoryMatches.length > 1 || workingDirectoryKeyCount !== workingDirectoryMatches.length) {
|
|
313
|
-
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed working directory.` };
|
|
314
|
-
}
|
|
315
|
-
const workingDirectory = workingDirectoryMatches[0]?.[1] === undefined
|
|
316
|
-
? null
|
|
317
|
-
: xmlUnescapeStrict(workingDirectoryMatches[0][1]);
|
|
318
|
-
if (workingDirectoryMatches.length === 1 && workingDirectory === undefined) {
|
|
425
|
+
const rawWorkingDirectory = plist.get("WorkingDirectory");
|
|
426
|
+
const workingDirectory = rawWorkingDirectory === undefined ? null : plistStringValue(rawWorkingDirectory);
|
|
427
|
+
if (rawWorkingDirectory !== undefined && workingDirectory === undefined) {
|
|
319
428
|
return { ok: false, message: `Installed ${definition.id} LaunchAgent has a malformed working directory.` };
|
|
320
429
|
}
|
|
321
430
|
return {
|
|
@@ -340,7 +449,10 @@ function installedShell(executable) {
|
|
|
340
449
|
};
|
|
341
450
|
}
|
|
342
451
|
function parseSystemdExecArgument(value) {
|
|
343
|
-
const
|
|
452
|
+
const quoted = value.startsWith('"') || value.endsWith('"');
|
|
453
|
+
if (quoted ? !isSingleSystemdQuotedValue(value) : /["']/u.test(value))
|
|
454
|
+
return undefined;
|
|
455
|
+
const decoded = decodeSystemdEscapes(quoted ? value.slice(1, -1) : value);
|
|
344
456
|
return decoded === undefined ? undefined : decodeSystemdSubstitutions(decoded, true);
|
|
345
457
|
}
|
|
346
458
|
function parseSystemdDirectiveValue(value) {
|
|
@@ -417,6 +529,9 @@ export function decodeSystemdEscapes(value) {
|
|
|
417
529
|
index += length + 1;
|
|
418
530
|
}
|
|
419
531
|
try {
|
|
532
|
+
// Keep a BOM produced by an escape sequence as U+FEFF. The default
|
|
533
|
+
// decoder behavior strips leading UTF-8 BOM bytes, which could turn an
|
|
534
|
+
// invalid BOM-prefixed environment key into PI_WEB_CONFIG.
|
|
420
535
|
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(Uint8Array.from(bytes));
|
|
421
536
|
}
|
|
422
537
|
catch {
|
|
@@ -445,7 +560,17 @@ function parseSystemdShellCommand(shell, value) {
|
|
|
445
560
|
return undefined;
|
|
446
561
|
const inner = value.slice(1, -1);
|
|
447
562
|
const unquoted = shell === "fish" ? fishSingleQuoteUnescape(inner) : inner.replaceAll("'\\''", "'");
|
|
448
|
-
|
|
563
|
+
if (legacySystemdShellQuote(shell, unquoted) !== value)
|
|
564
|
+
return undefined;
|
|
565
|
+
// The pre-hardening renderer doubled percent specifiers, but its
|
|
566
|
+
// replacement-string "$$" left dollar characters unchanged.
|
|
567
|
+
return decodeSystemdSubstitutions(unquoted, false);
|
|
568
|
+
}
|
|
569
|
+
function legacySystemdShellQuote(shell, value) {
|
|
570
|
+
const escaped = shell === "fish"
|
|
571
|
+
? value.replaceAll("\\", "\\\\").replaceAll("'", "\\'")
|
|
572
|
+
: value.replaceAll("'", "'\\''");
|
|
573
|
+
return `'${escaped}'`;
|
|
449
574
|
}
|
|
450
575
|
function fishSingleQuoteUnescape(value) {
|
|
451
576
|
let result = "";
|
|
@@ -461,34 +586,181 @@ function fishSingleQuoteUnescape(value) {
|
|
|
461
586
|
}
|
|
462
587
|
return result;
|
|
463
588
|
}
|
|
464
|
-
|
|
589
|
+
const plistXmlDeclaration = '<?xml version="1.0" encoding="UTF-8"?>';
|
|
590
|
+
const plistDoctype = '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">';
|
|
591
|
+
const maximumPlistDepth = 32;
|
|
592
|
+
/** Parse the complete XML plist subset emitted by PI WEB, rejecting fragments and duplicate dictionary keys. */
|
|
593
|
+
function parseLaunchdPlistDocument(contents) {
|
|
594
|
+
const normalizedContents = normalizePlistXml(contents);
|
|
595
|
+
if (normalizedContents === undefined)
|
|
596
|
+
return undefined;
|
|
597
|
+
const cursor = {
|
|
598
|
+
contents: normalizedContents,
|
|
599
|
+
offset: normalizedContents.startsWith("\uFEFF") ? 1 : 0,
|
|
600
|
+
};
|
|
601
|
+
skipPlistWhitespace(cursor);
|
|
602
|
+
if (consumePlistToken(cursor, plistXmlDeclaration))
|
|
603
|
+
skipPlistWhitespace(cursor);
|
|
604
|
+
else if (cursor.contents.startsWith("<?xml", cursor.offset))
|
|
605
|
+
return undefined;
|
|
606
|
+
if (consumePlistToken(cursor, plistDoctype))
|
|
607
|
+
skipPlistWhitespace(cursor);
|
|
608
|
+
else if (cursor.contents.startsWith("<!DOCTYPE", cursor.offset))
|
|
609
|
+
return undefined;
|
|
610
|
+
if (!consumePlistToken(cursor, '<plist version="1.0">'))
|
|
611
|
+
return undefined;
|
|
612
|
+
const root = parsePlistValue(cursor, 0);
|
|
613
|
+
if (root?.kind !== "dictionary")
|
|
614
|
+
return undefined;
|
|
615
|
+
skipPlistWhitespace(cursor);
|
|
616
|
+
if (!consumePlistToken(cursor, "</plist>"))
|
|
617
|
+
return undefined;
|
|
618
|
+
skipPlistWhitespace(cursor);
|
|
619
|
+
return cursor.offset === cursor.contents.length ? root.value : undefined;
|
|
620
|
+
}
|
|
621
|
+
function parsePlistValue(cursor, depth) {
|
|
622
|
+
if (depth > maximumPlistDepth)
|
|
623
|
+
return undefined;
|
|
624
|
+
skipPlistWhitespace(cursor);
|
|
625
|
+
if (cursor.contents.startsWith("<string>", cursor.offset)) {
|
|
626
|
+
const value = parsePlistTextElement(cursor, "string");
|
|
627
|
+
return value === undefined ? undefined : { kind: "string", value };
|
|
628
|
+
}
|
|
629
|
+
for (const tag of ["data", "date", "integer", "real"]) {
|
|
630
|
+
if (!cursor.contents.startsWith(`<${tag}>`, cursor.offset))
|
|
631
|
+
continue;
|
|
632
|
+
const value = parsePlistTextElement(cursor, tag);
|
|
633
|
+
return value === undefined || !isValidPlistScalarValue(tag, value)
|
|
634
|
+
? undefined
|
|
635
|
+
: { kind: "scalar", value };
|
|
636
|
+
}
|
|
637
|
+
if (cursor.contents.startsWith("<array", cursor.offset))
|
|
638
|
+
return parsePlistArray(cursor, depth);
|
|
639
|
+
if (cursor.contents.startsWith("<dict", cursor.offset))
|
|
640
|
+
return parsePlistDictionary(cursor, depth);
|
|
641
|
+
if (consumePlistToken(cursor, "<true/>"))
|
|
642
|
+
return { kind: "boolean", value: true };
|
|
643
|
+
if (consumePlistToken(cursor, "<false/>"))
|
|
644
|
+
return { kind: "boolean", value: false };
|
|
645
|
+
return undefined;
|
|
646
|
+
}
|
|
647
|
+
function parsePlistArray(cursor, depth) {
|
|
648
|
+
if (consumePlistToken(cursor, "<array/>"))
|
|
649
|
+
return { kind: "array", value: [] };
|
|
650
|
+
if (!consumePlistToken(cursor, "<array>"))
|
|
651
|
+
return undefined;
|
|
465
652
|
const values = [];
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
return
|
|
470
|
-
const value =
|
|
653
|
+
for (;;) {
|
|
654
|
+
skipPlistWhitespace(cursor);
|
|
655
|
+
if (consumePlistToken(cursor, "</array>"))
|
|
656
|
+
return { kind: "array", value: values };
|
|
657
|
+
const value = parsePlistValue(cursor, depth + 1);
|
|
471
658
|
if (value === undefined)
|
|
472
659
|
return undefined;
|
|
473
660
|
values.push(value);
|
|
474
|
-
cursor = match.index + match[0].length;
|
|
475
661
|
}
|
|
476
|
-
return contents.slice(cursor).trim() === "" ? values : undefined;
|
|
477
662
|
}
|
|
478
|
-
function
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
663
|
+
function parsePlistDictionary(cursor, depth) {
|
|
664
|
+
if (consumePlistToken(cursor, "<dict/>"))
|
|
665
|
+
return { kind: "dictionary", value: new Map() };
|
|
666
|
+
if (!consumePlistToken(cursor, "<dict>"))
|
|
667
|
+
return undefined;
|
|
668
|
+
const values = new Map();
|
|
669
|
+
for (;;) {
|
|
670
|
+
skipPlistWhitespace(cursor);
|
|
671
|
+
if (consumePlistToken(cursor, "</dict>"))
|
|
672
|
+
return { kind: "dictionary", value: values };
|
|
673
|
+
const key = parsePlistTextElement(cursor, "key");
|
|
674
|
+
if (key === undefined || values.has(key))
|
|
675
|
+
return undefined;
|
|
676
|
+
const value = parsePlistValue(cursor, depth + 1);
|
|
677
|
+
if (value === undefined)
|
|
678
|
+
return undefined;
|
|
679
|
+
values.set(key, value);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
function parsePlistTextElement(cursor, tag) {
|
|
683
|
+
const openingTag = `<${tag}>`;
|
|
684
|
+
const closingTag = `</${tag}>`;
|
|
685
|
+
if (!consumePlistToken(cursor, openingTag))
|
|
686
|
+
return undefined;
|
|
687
|
+
const closingOffset = cursor.contents.indexOf(closingTag, cursor.offset);
|
|
688
|
+
if (closingOffset < 0)
|
|
689
|
+
return undefined;
|
|
690
|
+
const encoded = cursor.contents.slice(cursor.offset, closingOffset);
|
|
691
|
+
cursor.offset = closingOffset + closingTag.length;
|
|
692
|
+
return xmlUnescapeStrict(encoded);
|
|
693
|
+
}
|
|
694
|
+
function normalizePlistXml(contents) {
|
|
695
|
+
for (const character of contents) {
|
|
696
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
697
|
+
const isXmlCharacter = codePoint === 0x09
|
|
698
|
+
|| codePoint === 0x0a
|
|
699
|
+
|| codePoint === 0x0d
|
|
700
|
+
|| (codePoint >= 0x20 && codePoint <= 0xd7ff)
|
|
701
|
+
|| (codePoint >= 0xe000 && codePoint <= 0xfffd)
|
|
702
|
+
|| (codePoint >= 0x10000 && codePoint <= 0x10ffff);
|
|
703
|
+
if (!isXmlCharacter)
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
return contents.replace(/\r\n?/gu, "\n");
|
|
707
|
+
}
|
|
708
|
+
function isValidPlistScalarValue(tag, value) {
|
|
709
|
+
if (tag === "data") {
|
|
710
|
+
const compact = value.replace(/[\t\n\r ]/gu, "");
|
|
711
|
+
return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(compact);
|
|
712
|
+
}
|
|
713
|
+
if (tag === "date") {
|
|
714
|
+
const match = /^(\d{4})-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/u.exec(value);
|
|
715
|
+
if (match?.[1] === undefined || Number(match[1]) === 0)
|
|
716
|
+
return false;
|
|
717
|
+
const timestamp = Date.parse(value);
|
|
718
|
+
return Number.isFinite(timestamp)
|
|
719
|
+
&& new Date(timestamp).toISOString() === `${value.slice(0, -1)}.000Z`;
|
|
720
|
+
}
|
|
721
|
+
if (tag === "integer") {
|
|
722
|
+
if (!/^[+-]?(?:0|[1-9][0-9]*)$/u.test(value))
|
|
723
|
+
return false;
|
|
724
|
+
const parsed = BigInt(value);
|
|
725
|
+
return parsed >= -(2n ** 63n) && parsed <= (2n ** 63n) - 1n;
|
|
726
|
+
}
|
|
727
|
+
return /^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/u.test(value)
|
|
728
|
+
&& Number.isFinite(Number(value));
|
|
729
|
+
}
|
|
730
|
+
function skipPlistWhitespace(cursor) {
|
|
731
|
+
while (/[\t\n\r ]/u.test(cursor.contents[cursor.offset] ?? ""))
|
|
732
|
+
cursor.offset += 1;
|
|
733
|
+
}
|
|
734
|
+
function consumePlistToken(cursor, token) {
|
|
735
|
+
if (!cursor.contents.startsWith(token, cursor.offset))
|
|
736
|
+
return false;
|
|
737
|
+
cursor.offset += token.length;
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
function plistStringValue(value) {
|
|
741
|
+
return value?.kind === "string" ? value.value : undefined;
|
|
742
|
+
}
|
|
743
|
+
function plistStringArray(value) {
|
|
744
|
+
if (value?.kind !== "array")
|
|
745
|
+
return undefined;
|
|
746
|
+
const strings = [];
|
|
747
|
+
for (const item of value.value) {
|
|
748
|
+
if (item.kind !== "string")
|
|
483
749
|
return undefined;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
750
|
+
strings.push(item.value);
|
|
751
|
+
}
|
|
752
|
+
return strings;
|
|
753
|
+
}
|
|
754
|
+
function plistStringDictionary(value) {
|
|
755
|
+
if (value.kind !== "dictionary")
|
|
756
|
+
return undefined;
|
|
757
|
+
const entries = [];
|
|
758
|
+
for (const [key, item] of value.value) {
|
|
759
|
+
if (item.kind !== "string")
|
|
487
760
|
return undefined;
|
|
488
|
-
entries.
|
|
489
|
-
cursor = match.index + match[0].length;
|
|
761
|
+
entries.push([key, item.value]);
|
|
490
762
|
}
|
|
491
|
-
return
|
|
763
|
+
return Object.fromEntries(entries);
|
|
492
764
|
}
|
|
493
765
|
function xmlUnescapeStrict(value) {
|
|
494
766
|
if (/[<>]/u.test(value) || /&(?!(?:apos|quot|gt|lt|amp);)/u.test(value))
|
|
@@ -503,7 +775,7 @@ function xmlUnescapeStrict(value) {
|
|
|
503
775
|
function recordsEqual(left, right) {
|
|
504
776
|
const leftEntries = Object.entries(left);
|
|
505
777
|
return leftEntries.length === Object.keys(right).length
|
|
506
|
-
&& leftEntries.every(([key, value]) => right[key] === value);
|
|
778
|
+
&& leftEntries.every(([key, value]) => Object.hasOwn(right, key) && right[key] === value);
|
|
507
779
|
}
|
|
508
780
|
function impossibleMissingDefinition() {
|
|
509
781
|
throw new Error("Expected at least one installed native service definition");
|