@jmfederico/pi-web 1.202608.0 → 1.202608.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +4 -4
  2. package/dist/cli.js +287 -113
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client/assets/CodeViewer-BMWwxG7q.js +4 -0
  5. package/dist/client/assets/{TerminalPanel-fYrKc3jh.js → TerminalPanel-CacQDIYn.js} +6 -6
  6. package/dist/client/assets/index-DUW2xnoV.js +4279 -0
  7. package/dist/client/assets/vendor-editor-core-CXO8gGab.js +12 -0
  8. package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +46 -0
  9. package/dist/client/index.html +3 -3
  10. package/dist/config.js +110 -73
  11. package/dist/config.js.map +1 -1
  12. package/dist/docker/piWebDockerCommandPlan.js +4 -3
  13. package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
  14. package/dist/environment.js +4 -0
  15. package/dist/environment.js.map +1 -0
  16. package/dist/nativeServices/installedServiceDefinitions.js +602 -0
  17. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
  18. package/dist/nativeServices/serviceAction.js +174 -0
  19. package/dist/nativeServices/serviceAction.js.map +1 -0
  20. package/dist/nativeServices/serviceDoctor.js +374 -71
  21. package/dist/nativeServices/serviceDoctor.js.map +1 -1
  22. package/dist/nativeServices/servicePlan.js +1 -1
  23. package/dist/nativeServices/servicePlan.js.map +1 -1
  24. package/dist/{pi-web-plugins → pi-packages}/relays/markdownDocument.js +1 -1
  25. package/dist/pi-packages/relays/package.json +11 -0
  26. package/dist/{pi-web-plugins → pi-packages}/relays/pi-web-plugin.js +4 -4
  27. package/dist/pi-packages/relays/prompts/relay-worktree.md +157 -0
  28. package/dist/pi-packages/relays/prompts/relay.md +152 -0
  29. package/dist/{pi-web-plugins → pi-packages}/relays/relayDiscovery.js +1 -1
  30. package/dist/{pi-web-plugins → pi-packages}/relays/relaysPanelElement.js +1 -1
  31. package/dist/pi-packages/relays/skills/relay/SKILL.md +123 -0
  32. package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
  33. package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
  34. package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
  35. package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
  36. package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
  37. package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
  38. package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
  39. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
  40. package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
  41. package/dist/{server/git/gitService.js → pi-web-plugins/git/git-backend.js} +209 -60
  42. package/dist/pi-web-plugins/git/package.json +16 -0
  43. package/dist/pi-web-plugins/git/server-plugin.js +176 -0
  44. package/dist/pi-web-plugins/info/infoInternals.js +16 -2
  45. package/dist/pi-web-plugins/info/package.json +1 -1
  46. package/dist/pi-web-plugins/info/pi-web-plugin.js +2 -2
  47. package/dist/pi-web-plugins/updates/package.json +1 -1
  48. package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
  49. package/dist/pi-web-plugins/workspace-tasks/package.json +1 -1
  50. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +3 -3
  51. package/dist/piWebVersionReport.js +63 -10
  52. package/dist/piWebVersionReport.js.map +1 -1
  53. package/dist/plugin-api.d.ts +33 -16
  54. package/dist/pluginRecoveryCli.js +172 -0
  55. package/dist/pluginRecoveryCli.js.map +1 -0
  56. package/dist/server/activity/workspaceActivityService.js +27 -19
  57. package/dist/server/activity/workspaceActivityService.js.map +1 -1
  58. package/dist/server/app.js +41 -15
  59. package/dist/server/app.js.map +1 -1
  60. package/dist/server/configRoutes.js +3 -21
  61. package/dist/server/configRoutes.js.map +1 -1
  62. package/dist/server/knownAutoInstallPiPackages.js +30 -0
  63. package/dist/server/knownAutoInstallPiPackages.js.map +1 -0
  64. package/dist/server/machines/machineClient.js +23 -4
  65. package/dist/server/machines/machineClient.js.map +1 -1
  66. package/dist/server/machines/machinePluginProxyRoutes.js +74 -17
  67. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
  68. package/dist/server/machines/machineProxyRoutes.js +165 -8
  69. package/dist/server/machines/machineProxyRoutes.js.map +1 -1
  70. package/dist/server/machines/machineService.js +23 -0
  71. package/dist/server/machines/machineService.js.map +1 -1
  72. package/dist/server/piPackageIdentity.js +25 -0
  73. package/dist/server/piPackageIdentity.js.map +1 -0
  74. package/dist/server/piPackageService.js +69 -8
  75. package/dist/server/piPackageService.js.map +1 -1
  76. package/dist/server/piWebPluginCatalog.js +584 -0
  77. package/dist/server/piWebPluginCatalog.js.map +1 -0
  78. package/dist/server/piWebPluginLifecycle.js +162 -0
  79. package/dist/server/piWebPluginLifecycle.js.map +1 -0
  80. package/dist/server/piWebPluginService.js +159 -261
  81. package/dist/server/piWebPluginService.js.map +1 -1
  82. package/dist/server/piWebStatus.js +41 -9
  83. package/dist/server/piWebStatus.js.map +1 -1
  84. package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
  85. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
  86. package/dist/server/plugins/serverPluginExec.js +184 -0
  87. package/dist/server/plugins/serverPluginExec.js.map +1 -0
  88. package/dist/server/plugins/serverPluginRuntime.js +480 -0
  89. package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
  90. package/dist/server/projectTrustRoutes.js +80 -0
  91. package/dist/server/projectTrustRoutes.js.map +1 -0
  92. package/dist/server/projects/projectService.js +3 -1
  93. package/dist/server/projects/projectService.js.map +1 -1
  94. package/dist/server/realtime/sessionEventHub.js +26 -11
  95. package/dist/server/realtime/sessionEventHub.js.map +1 -1
  96. package/dist/server/requestCancellation.js +32 -0
  97. package/dist/server/requestCancellation.js.map +1 -0
  98. package/dist/server/sessiond/agentProcessEnvironment.js +29 -30
  99. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -1
  100. package/dist/server/sessiond/autoInstallPiPackages.js +63 -0
  101. package/dist/server/sessiond/autoInstallPiPackages.js.map +1 -0
  102. package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
  103. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
  104. package/dist/server/sessiond/sessionDaemonShutdown.js +24 -0
  105. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  106. package/dist/server/sessiond/sessionProxyRoutes.js +2 -2
  107. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  108. package/dist/server/sessiond/sessionServiceDependencies.js +2 -1
  109. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -1
  110. package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
  111. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
  112. package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
  113. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
  114. package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
  115. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
  116. package/dist/server/sessiond.js +284 -98
  117. package/dist/server/sessiond.js.map +1 -1
  118. package/dist/server/sessions/authService.js +15 -54
  119. package/dist/server/sessions/authService.js.map +1 -1
  120. package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
  121. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
  122. package/dist/server/sessions/modelCatalogRefresher.js +4 -3
  123. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -1
  124. package/dist/server/sessions/piSessionManagerGateway.js +18 -67
  125. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -1
  126. package/dist/server/sessions/piSessionService.js +495 -142
  127. package/dist/server/sessions/piSessionService.js.map +1 -1
  128. package/dist/server/sessions/sessionCommandService.js +24 -1
  129. package/dist/server/sessions/sessionCommandService.js.map +1 -1
  130. package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
  131. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
  132. package/dist/server/sessions/sessionFileFormat.js +27 -0
  133. package/dist/server/sessions/sessionFileFormat.js.map +1 -0
  134. package/dist/server/sessions/sessionFileHeader.js +1 -14
  135. package/dist/server/sessions/sessionFileHeader.js.map +1 -1
  136. package/dist/server/sessions/sessionModelScope.js +160 -0
  137. package/dist/server/sessions/sessionModelScope.js.map +1 -0
  138. package/dist/server/sessions/sessionNameGenerator.js +6 -6
  139. package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
  140. package/dist/server/sessions/sessionRoutes.js +55 -0
  141. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  142. package/dist/server/sessions/sessionSummaryScanner.js +96 -224
  143. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -1
  144. package/dist/server/sessions/spawnSubsessionTool.js +5 -9
  145. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
  146. package/dist/server/sessions/spawnTargetResolver.js +1 -1
  147. package/dist/server/status/machineStatusRoutes.js +8 -0
  148. package/dist/server/status/machineStatusRoutes.js.map +1 -0
  149. package/dist/server/status/machineStatusService.js +181 -0
  150. package/dist/server/status/machineStatusService.js.map +1 -0
  151. package/dist/server/status/workspaceAttribution.js +87 -0
  152. package/dist/server/status/workspaceAttribution.js.map +1 -0
  153. package/dist/server/storage/piPackageDismissalStore.js +78 -0
  154. package/dist/server/storage/piPackageDismissalStore.js.map +1 -0
  155. package/dist/server/terminalProxyRoutes.js +2 -1
  156. package/dist/server/terminalProxyRoutes.js.map +1 -1
  157. package/dist/server/workspaceExplorerRoutes.js +20 -13
  158. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  159. package/dist/server/workspaces/fileContentService.js +18 -9
  160. package/dist/server/workspaces/fileContentService.js.map +1 -1
  161. package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
  162. package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
  163. package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
  164. package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
  165. package/dist/server/workspaces/filePreviewService.js +81 -0
  166. package/dist/server/workspaces/filePreviewService.js.map +1 -0
  167. package/dist/server/workspaces/projectWorkspaceCwds.js +0 -5
  168. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -1
  169. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
  170. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
  171. package/dist/server/workspaces/workspaceCatalog.js +47 -0
  172. package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
  173. package/dist/server/workspaces/workspaceContext.js +1 -3
  174. package/dist/server/workspaces/workspaceContext.js.map +1 -1
  175. package/dist/server/workspaces/workspaceDeletionRoutes.js +35 -125
  176. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -1
  177. package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
  178. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
  179. package/dist/server/workspaces/workspaceRemovalService.js +256 -0
  180. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
  181. package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
  182. package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
  183. package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
  184. package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
  185. package/dist/server-plugin-api.d.ts +140 -0
  186. package/dist/server-plugin-api.js +2 -0
  187. package/dist/server-plugin-api.js.map +1 -0
  188. package/dist/serverPluginRecovery.js +138 -0
  189. package/dist/serverPluginRecovery.js.map +1 -0
  190. package/dist/sessiond/activeAgentProfile.js +3 -22
  191. package/dist/sessiond/activeAgentProfile.js.map +1 -1
  192. package/dist/sessiond/config.js +11 -0
  193. package/dist/sessiond/config.js.map +1 -1
  194. package/dist/sessiond/sessionDaemonClient.js +11 -9
  195. package/dist/sessiond/sessionDaemonClient.js.map +1 -1
  196. package/dist/shared/activeAgentProfile.js +2 -41
  197. package/dist/shared/activeAgentProfile.js.map +1 -1
  198. package/dist/shared/activity.js +0 -3
  199. package/dist/shared/activity.js.map +1 -1
  200. package/dist/shared/apiTypes.js +7 -5
  201. package/dist/shared/apiTypes.js.map +1 -1
  202. package/dist/shared/capabilities.js +8 -6
  203. package/dist/shared/capabilities.js.map +1 -1
  204. package/dist/shared/federatedRoutes.js +36 -5
  205. package/dist/shared/federatedRoutes.js.map +1 -1
  206. package/dist/shared/machinePluginIds.js +4 -2
  207. package/dist/shared/machinePluginIds.js.map +1 -1
  208. package/dist/shared/machineStatus.js +94 -0
  209. package/dist/shared/machineStatus.js.map +1 -0
  210. package/dist/shared/piWebStatusParsing.js +33 -0
  211. package/dist/shared/piWebStatusParsing.js.map +1 -1
  212. package/dist/shared/pluginApiTypes.d.ts +150 -0
  213. package/dist/shared/pluginApiTypes.js +3 -0
  214. package/dist/shared/pluginApiTypes.js.map +1 -0
  215. package/dist/shared/pluginBackendProtocol.js +110 -0
  216. package/dist/shared/pluginBackendProtocol.js.map +1 -0
  217. package/dist/shared/pluginIds.js +5 -0
  218. package/dist/shared/pluginIds.js.map +1 -1
  219. package/dist/shared/pluginRecoveryCommands.js +14 -0
  220. package/dist/shared/pluginRecoveryCommands.js.map +1 -0
  221. package/dist/shared/workspaceFiles.js +41 -2
  222. package/dist/shared/workspaceFiles.js.map +1 -1
  223. package/dist/shared/workspaceRemovalProtocol.js +24 -0
  224. package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
  225. package/docs/config.md +105 -52
  226. package/docs/plugins.md +403 -146
  227. package/examples/workspace-provider-plugin/README.md +52 -0
  228. package/examples/workspace-provider-plugin/package.json +25 -0
  229. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  230. package/examples/workspace-provider-plugin/src/server.ts +64 -0
  231. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  232. package/package.json +30 -11
  233. package/server-plugin-api.d.ts +1 -0
  234. package/dist/client/assets/CodeViewer-D91Sp61M.js +0 -4
  235. package/dist/client/assets/UnifiedDiffViewer-BafLWGtF.js +0 -39
  236. package/dist/client/assets/index-CA8q9_o7.js +0 -4025
  237. package/dist/client/assets/vendor-editor-core-vUi74DnD.js +0 -12
  238. package/dist/client/assets/vendor-editor-languages-DoaXUodB.js +0 -46
  239. package/dist/pi-web-plugins/relays/package.json +0 -9
  240. package/dist/plugin-api/unstable.d.ts +0 -22
  241. package/dist/server/activity/workspaceActivityRoutes.js +0 -4
  242. package/dist/server/activity/workspaceActivityRoutes.js.map +0 -1
  243. package/dist/server/git/gitService.js.map +0 -1
  244. package/dist/server/gitRoutes.js +0 -23
  245. package/dist/server/gitRoutes.js.map +0 -1
  246. package/dist/server/sessions/parentSessionLocator.js +0 -75
  247. package/dist/server/sessions/parentSessionLocator.js.map +0 -1
  248. package/dist/server/workspaces/gitWorktreeDiscovery.js +0 -43
  249. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +0 -1
  250. package/dist/server/workspaces/imagePreviewService.js +0 -40
  251. package/dist/server/workspaces/imagePreviewService.js.map +0 -1
  252. package/dist/server/workspaces/workspaceService.js +0 -52
  253. package/dist/server/workspaces/workspaceService.js.map +0 -1
  254. package/dist/shared/apiTypes.d.ts +0 -1217
  255. package/dist/shared/thinkingLevels.d.ts +0 -27
  256. package/plugin-api/unstable.d.ts +0 -1
  257. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/README.md +0 -0
  258. /package/dist/{pi-web-plugins → pi-packages}/relays/vendor/marked.esm.js +0 -0
@@ -0,0 +1,162 @@
1
+ import { PI_WEB_PLUGIN_LIFECYCLE_VERSION, } from "../shared/apiTypes.js";
2
+ import { PI_WEB_PLUGIN_RECOVERY_COMMANDS, pluginDisableRecoveryCommand } from "../shared/pluginRecoveryCommands.js";
3
+ /** Pure desired/active reconciliation shared by the manifest and diagnostics API. */
4
+ export function reconcilePiWebPluginLifecycle(desired, runtime, browserModuleUrl, desiredSafeStart) {
5
+ const activeSnapshot = runtime.status === "available" ? runtime.snapshot : undefined;
6
+ const desiredById = new Map(desired.plugins.map((plugin) => [plugin.id, plugin]));
7
+ const recordsById = new Map(activeSnapshot?.records.map((record) => [record.pluginId, record]) ?? []);
8
+ const healthById = new Map(activeSnapshot?.health.map((inspection) => [inspection.pluginId, inspection]) ?? []);
9
+ const diagnostics = publicDiagnostics(desired.diagnostics, activeSnapshot?.diagnostics ?? []);
10
+ const conflictIds = new Set(diagnostics.flatMap((diagnostic) => diagnostic.kind === "conflict" && diagnostic.pluginId !== undefined ? [diagnostic.pluginId] : []));
11
+ const pluginIds = new Set([...desiredById.keys(), ...recordsById.keys()]);
12
+ const browserPlugins = [];
13
+ const activeSafeStart = activeSnapshot?.safeStart ?? "off";
14
+ const effectiveDesiredSafeStart = desiredSafeStart ?? activeSafeStart;
15
+ const plugins = [...pluginIds]
16
+ .sort((left, right) => left.localeCompare(right))
17
+ .map((pluginId) => {
18
+ const plugin = desiredById.get(pluginId);
19
+ const record = recordsById.get(pluginId);
20
+ const server = plugin?.serverModule !== undefined || record !== undefined
21
+ ? serverInfo(pluginId, plugin, record, healthById.get(pluginId), runtime.status, activeSafeStart, effectiveDesiredSafeStart)
22
+ : undefined;
23
+ if (plugin?.browserModule !== undefined && shouldPublishBrowserPlugin(plugin, server)) {
24
+ browserPlugins.push({
25
+ plugin,
26
+ ...(server?.activeRevision === undefined ? {} : { backendRevision: server.activeRevision }),
27
+ });
28
+ }
29
+ if (plugin !== undefined) {
30
+ return {
31
+ id: plugin.id,
32
+ ...(plugin.browserModule === undefined ? {} : { module: browserModuleUrl(plugin) }),
33
+ source: plugin.source,
34
+ scope: plugin.scope,
35
+ machineSpecific: plugin.machineSpecific,
36
+ enabled: plugin.enabled,
37
+ discovered: true,
38
+ conflict: conflictIds.has(plugin.id),
39
+ ...(server === undefined ? {} : { server }),
40
+ };
41
+ }
42
+ if (record === undefined || server === undefined)
43
+ throw new Error(`Missing active server plugin record: ${pluginId}`);
44
+ return {
45
+ id: record.pluginId,
46
+ source: record.source,
47
+ scope: record.scope,
48
+ machineSpecific: record.machineSpecific,
49
+ enabled: false,
50
+ discovered: false,
51
+ conflict: conflictIds.has(record.pluginId),
52
+ server,
53
+ };
54
+ });
55
+ const safeStartRestartRequired = desiredSafeStart === undefined
56
+ ? false
57
+ : runtime.status === "available"
58
+ ? desiredSafeStart !== (activeSnapshot?.safeStart ?? "off")
59
+ : desiredSafeStart !== "off";
60
+ const restartRequired = safeStartRestartRequired || plugins.some((plugin) => plugin.server?.restartRequired === true);
61
+ return {
62
+ response: {
63
+ lifecycleVersion: PI_WEB_PLUGIN_LIFECYCLE_VERSION,
64
+ plugins,
65
+ diagnostics,
66
+ serverRuntime: {
67
+ status: runtime.status,
68
+ ...(activeSnapshot?.safeStart === undefined ? {} : { safeStart: activeSnapshot.safeStart }),
69
+ ...(desiredSafeStart === undefined ? {} : { desiredSafeStart }),
70
+ restartRequired,
71
+ ...(runtime.status === "available" ? {} : { message: runtime.message }),
72
+ recovery: PI_WEB_PLUGIN_RECOVERY_COMMANDS,
73
+ },
74
+ },
75
+ browserPlugins: Object.freeze(browserPlugins),
76
+ };
77
+ }
78
+ function serverInfo(pluginId, desired, active, health, runtimeStatus, activeSafeStart, desiredSafeStart) {
79
+ const staleRevision = revisionsAreStale(desired, active);
80
+ const state = runtimeStatus !== "available"
81
+ ? "unknown"
82
+ : active?.state ?? (desired !== undefined && !serverEnabledForSafeStart(desired, activeSafeStart) ? "disabled" : "missing");
83
+ const restartRequired = runtimeStatus === "available" && serverRestartRequired(desired, active, staleRevision, desiredSafeStart);
84
+ const phase = active?.phase ?? health?.phase;
85
+ const message = active?.message ?? health?.error;
86
+ return {
87
+ state,
88
+ ...(desired?.serverModule === undefined ? {} : { desiredRevision: desired.serverModule.revision }),
89
+ ...(active === undefined ? {} : { activeRevision: active.moduleRevision }),
90
+ ...(phase === undefined ? {} : { phase }),
91
+ ...(message === undefined ? {} : { message }),
92
+ ...(health === undefined ? {} : {
93
+ health: {
94
+ status: health.health.status,
95
+ ...(health.health.message === undefined ? {} : { message: health.health.message }),
96
+ },
97
+ }),
98
+ staleRevision,
99
+ restartRequired,
100
+ disableCommand: pluginDisableRecoveryCommand(pluginId),
101
+ };
102
+ }
103
+ function serverRestartRequired(desired, active, staleRevision, desiredSafeStart) {
104
+ if (desired === undefined) {
105
+ if (active === undefined)
106
+ return false;
107
+ return active.state !== "disabled";
108
+ }
109
+ const shouldBeActive = serverEnabledForSafeStart(desired, desiredSafeStart);
110
+ if (active === undefined)
111
+ return shouldBeActive;
112
+ if (!shouldBeActive)
113
+ return active.state !== "disabled";
114
+ if (staleRevision)
115
+ return true;
116
+ return active.state !== "active";
117
+ }
118
+ function serverEnabledForSafeStart(plugin, safeStart) {
119
+ return plugin.enabled
120
+ && safeStart !== "none"
121
+ && (safeStart !== "bundled-only" || plugin.scope === "bundled");
122
+ }
123
+ function shouldPublishBrowserPlugin(plugin, server) {
124
+ if (plugin.browserModule === undefined)
125
+ return false;
126
+ if (server === undefined)
127
+ return plugin.enabled;
128
+ if (plugin.serverModule === undefined)
129
+ return false;
130
+ return server.state === "active"
131
+ && !server.staleRevision
132
+ && server.health?.status !== "unhealthy";
133
+ }
134
+ function revisionsAreStale(desired, active) {
135
+ if (desired === undefined)
136
+ return false;
137
+ if (active === undefined)
138
+ return false;
139
+ const browserRevision = desired.browserModule?.revision;
140
+ return desired.serverModule?.revision !== active.moduleRevision
141
+ || (browserRevision !== undefined && browserRevision !== active.browserRevision)
142
+ || desired.settingsRevision !== active.settingsRevision
143
+ || desired.source !== active.source
144
+ || desired.scope !== active.scope
145
+ || desired.machineSpecific !== active.machineSpecific;
146
+ }
147
+ function publicDiagnostics(desired, active) {
148
+ return [
149
+ ...desired.map((diagnostic) => publicDiagnostic(diagnostic, "desired")),
150
+ ...active.map((diagnostic) => publicDiagnostic(diagnostic, "active")),
151
+ ];
152
+ }
153
+ function publicDiagnostic(diagnostic, snapshot) {
154
+ return {
155
+ kind: diagnostic.code === "duplicate-id" ? "conflict" : "discovery",
156
+ snapshot,
157
+ source: diagnostic.source,
158
+ message: diagnostic.message,
159
+ ...(diagnostic.pluginId === undefined ? {} : { pluginId: diagnostic.pluginId }),
160
+ };
161
+ }
162
+ //# sourceMappingURL=piWebPluginLifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"piWebPluginLifecycle.js","sourceRoot":"","sources":["../../src/server/piWebPluginLifecycle.ts"],"names":[],"mappings":"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"}
@@ -1,156 +1,184 @@
1
- import { existsSync } from "node:fs";
2
- import { readdir, readFile, realpath, stat } from "node:fs/promises";
3
- import { dirname, join, relative, resolve, sep } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { DefaultPackageManager, SettingsManager } from "@earendil-works/pi-coding-agent";
6
- import { loadPiWebConfig, piWebDataDir } from "../config.js";
1
+ import { PI_WEB_PLUGIN_LIFECYCLE_VERSION } from "../shared/apiTypes.js";
7
2
  import { isPiWebPluginId } from "../shared/pluginIds.js";
8
- export class DefaultPiPackageProvider {
9
- constructor(cwd, agentDir) {
10
- this.cwd = cwd;
11
- this.agentDir = agentDir;
12
- }
13
- listPackages() {
14
- return this.createPackageManager().listConfiguredPackages();
15
- }
16
- getInstalledPath(source, scope) {
17
- return this.createPackageManager().getInstalledPath(source, scope);
18
- }
19
- createPackageManager() {
20
- return new DefaultPackageManager({
21
- cwd: this.cwd,
22
- agentDir: this.agentDir,
23
- settingsManager: SettingsManager.create(this.cwd, this.agentDir),
24
- });
25
- }
26
- }
3
+ import { PiWebPluginCatalog, readPiWebPluginPackageArtifact, } from "./piWebPluginCatalog.js";
4
+ import { reconcilePiWebPluginLifecycle } from "./piWebPluginLifecycle.js";
5
+ import { WorkspaceCatalogProtocolError } from "./workspaces/workspaceCatalog.js";
6
+ export { DefaultPiPackageProvider, PiWebPluginCatalog, } from "./piWebPluginCatalog.js";
7
+ const BROWSER_ARTIFACT_CACHE_MAX_ENTRIES = 32;
8
+ const BROWSER_ARTIFACT_CACHE_MAX_BYTES = 64 * 1024 * 1024;
9
+ /** Browser manifest and asset adapter over the process-neutral package catalog. */
27
10
  export class PiWebPluginService {
28
11
  constructor(options = {}) {
29
- const cwd = options.cwd ?? process.cwd();
30
- this.roots = options.roots ?? defaultPluginRoots(cwd);
31
- this.agentDir = options.agentDir;
32
- this.agentDirProvider = options.agentDirProvider;
33
- const packageProvider = options.packageProvider;
34
- this.staticPackageProvider = packageProvider === false || packageProvider === undefined ? undefined : packageProvider;
35
- this.packageProviderForAgentDir = packageProvider === false || packageProvider !== undefined
36
- ? undefined
37
- : (agentDir) => new DefaultPiPackageProvider(cwd, agentDir);
38
- this.configProvider = options.configProvider ?? (() => loadPiWebConfig({ cwd }).config);
12
+ this.browserArtifacts = new Map();
13
+ this.browserArtifactBytes = 0;
14
+ this.catalog = options.catalog ?? new PiWebPluginCatalog(options);
15
+ this.runtimeProvider = options.runtimeProvider;
16
+ this.recoveryProvider = options.recoveryProvider;
39
17
  }
40
18
  async manifest() {
41
- return {
42
- plugins: (await this.plugins()).plugins
43
- .filter((plugin) => plugin.enabled)
44
- .map((plugin) => ({ id: plugin.id, module: plugin.module, source: plugin.source, scope: plugin.scope, machineSpecific: plugin.machineSpecific })),
45
- };
19
+ const lifecycle = await this.lifecycle();
20
+ const plugins = [];
21
+ for (const { plugin, backendRevision } of lifecycle.browserPlugins) {
22
+ const artifact = await this.captureBrowserArtifact(plugin, backendRevision);
23
+ if (artifact === undefined)
24
+ continue;
25
+ plugins.push({
26
+ id: plugin.id,
27
+ module: browserModuleUrl(plugin),
28
+ ...(backendRevision === undefined ? {} : { backendRevision }),
29
+ source: plugin.source,
30
+ scope: plugin.scope,
31
+ machineSpecific: plugin.machineSpecific,
32
+ });
33
+ }
34
+ return { lifecycleVersion: PI_WEB_PLUGIN_LIFECYCLE_VERSION, plugins };
46
35
  }
47
36
  async plugins() {
48
- const config = await this.configProvider();
49
- const plugins = await this.discoverPlugins();
50
- return { plugins: plugins.map((plugin) => this.pluginInfo(plugin, config)) };
37
+ return (await this.lifecycle()).response;
51
38
  }
52
- async readAsset(pluginId, assetPath) {
39
+ async readAsset(pluginId, assetPath, browserRevision) {
53
40
  if (!isPiWebPluginId(pluginId))
54
41
  return undefined;
55
- const plugin = await this.findPlugin(pluginId);
56
- if (plugin === undefined)
42
+ let artifact = this.browserArtifacts.get(pluginId);
43
+ if (artifact !== undefined && !await this.cachedArtifactIsActive(artifact))
44
+ artifact = undefined;
45
+ artifact ??= await this.loadBrowserArtifact(pluginId);
46
+ if (artifact === undefined)
57
47
  return undefined;
58
- const resolved = resolve(plugin.root, assetPath);
59
- const [realRoot, realAsset] = await Promise.all([
60
- realpath(plugin.root),
61
- realpath(resolved).catch(() => undefined),
62
- ]);
63
- if (realAsset === undefined || !isWithin(realRoot, realAsset))
48
+ if (browserRevision !== undefined && assetPath === artifact.entryPath && artifact.revision !== browserRevision)
64
49
  return undefined;
65
- const assetStat = await stat(realAsset).catch(() => undefined);
66
- if (assetStat?.isFile() !== true)
50
+ const content = artifact.files.get(assetPath);
51
+ if (content === undefined)
67
52
  return undefined;
68
- return { content: await readFile(realAsset), contentType: contentTypeFor(realAsset) };
69
- }
70
- pluginInfo(plugin, config) {
71
- return {
72
- id: plugin.id,
73
- module: `/pi-web-plugins/${encodeURIComponent(plugin.id)}/${plugin.entryFile}?${pluginModuleQuery(plugin)}`,
74
- source: plugin.source,
75
- scope: plugin.scope,
76
- machineSpecific: plugin.machineSpecific,
77
- enabled: config.plugins?.[plugin.id]?.enabled !== false,
78
- };
53
+ this.touchBrowserArtifact(artifact);
54
+ return { content, contentType: contentTypeFor(assetPath) };
79
55
  }
80
- async discoverPlugins() {
81
- const records = new Map();
82
- for (const plugin of await this.discoverLocalPlugins())
83
- addUnique(records, plugin);
84
- const packageProvider = await this.currentPackageProvider();
85
- if (packageProvider !== undefined) {
86
- for (const plugin of await this.discoverPiPackagePlugins(packageProvider))
87
- addUnique(records, plugin);
56
+ async loadBrowserArtifact(pluginId) {
57
+ try {
58
+ const lifecycle = await this.lifecycle();
59
+ const browserPlugin = lifecycle.browserPlugins.find(({ plugin }) => plugin.id === pluginId);
60
+ if (browserPlugin === undefined)
61
+ return undefined;
62
+ return await this.captureBrowserArtifact(browserPlugin.plugin, browserPlugin.backendRevision);
63
+ }
64
+ catch (error) {
65
+ const localPlugin = await this.catalog.browserPlugin(pluginId);
66
+ if (localPlugin?.serverModule !== undefined)
67
+ throw error;
68
+ return localPlugin === undefined ? undefined : await this.captureBrowserArtifact(localPlugin);
88
69
  }
89
- return [...records.values()].sort((left, right) => left.id.localeCompare(right.id));
90
70
  }
91
- async findPlugin(pluginId) {
92
- const localPlugin = (await this.discoverLocalPlugins()).find((candidate) => candidate.id === pluginId);
93
- if (localPlugin !== undefined)
94
- return localPlugin;
95
- const packageProvider = await this.currentPackageProvider();
96
- if (packageProvider === undefined)
71
+ async captureBrowserArtifact(plugin, backendRevision) {
72
+ const module = plugin.browserModule;
73
+ if (module === undefined)
97
74
  return undefined;
98
- const records = new Map();
99
- for (const plugin of await this.discoverPiPackagePlugins(packageProvider))
100
- addUnique(records, plugin);
101
- return records.get(pluginId);
102
- }
103
- async currentPackageProvider() {
104
- if (this.staticPackageProvider !== undefined)
105
- return this.staticPackageProvider;
106
- if (this.packageProviderForAgentDir === undefined)
75
+ const browserRoot = plugin.browserRoot;
76
+ if (browserRoot === undefined)
77
+ throw new Error(`PI WEB plugin has no browser root: ${plugin.id}`);
78
+ const cached = this.browserArtifacts.get(plugin.id);
79
+ if (cached !== undefined) {
80
+ const matches = cached.revision === module.revision
81
+ && cached.entryPath === module.path
82
+ && cached.entryFilePath === module.filePath
83
+ && cached.browserRootPath === browserRoot.path
84
+ && cached.browserRootDirectoryPath === browserRoot.directoryPath
85
+ && cached.packageRoot === plugin.packageRoot
86
+ && cached.backendRevision === backendRevision;
87
+ if (matches) {
88
+ this.touchBrowserArtifact(cached);
89
+ return cached;
90
+ }
91
+ }
92
+ const packageArtifact = await readPiWebPluginPackageArtifact(plugin.packageRoot, browserRoot).catch(() => undefined);
93
+ if (packageArtifact?.revision !== module.revision || !packageArtifact.files.has(module.path))
107
94
  return undefined;
108
- return this.packageProviderForAgentDir(await this.currentAgentDir());
109
- }
110
- async currentAgentDir() {
111
- if (this.agentDirProvider !== undefined)
112
- return await this.agentDirProvider();
113
- if (this.agentDir !== undefined)
114
- return this.agentDir;
115
- throw new Error("Pi package plugin discovery requires an explicit active agent directory");
116
- }
117
- async discoverLocalPlugins() {
118
- const plugins = [];
119
- for (const root of this.roots)
120
- plugins.push(...await discoverLocalRoot(root));
121
- return plugins;
95
+ const artifact = {
96
+ pluginId: plugin.id,
97
+ revision: module.revision,
98
+ entryPath: module.path,
99
+ entryFilePath: module.filePath,
100
+ browserRootPath: browserRoot.path,
101
+ browserRootDirectoryPath: browserRoot.directoryPath,
102
+ packageRoot: plugin.packageRoot,
103
+ ...(backendRevision === undefined ? {} : { backendRevision }),
104
+ files: packageArtifact.files,
105
+ byteLength: packageArtifact.byteLength,
106
+ };
107
+ this.cacheBrowserArtifact(artifact);
108
+ return artifact;
109
+ }
110
+ async cachedArtifactIsActive(artifact) {
111
+ if (artifact.backendRevision === undefined)
112
+ return true;
113
+ const runtime = await this.loadRuntime();
114
+ if (runtime.status !== "available")
115
+ return false;
116
+ const record = runtime.snapshot.records.find(({ pluginId }) => pluginId === artifact.pluginId);
117
+ const health = runtime.snapshot.health.find(({ pluginId }) => pluginId === artifact.pluginId);
118
+ return record?.state === "active"
119
+ && record.moduleRevision === artifact.backendRevision
120
+ && record.browserRevision === artifact.revision
121
+ && health?.health.status !== "unhealthy";
122
+ }
123
+ cacheBrowserArtifact(artifact) {
124
+ const current = this.browserArtifacts.get(artifact.pluginId);
125
+ if (current !== undefined) {
126
+ this.browserArtifacts.delete(artifact.pluginId);
127
+ this.browserArtifactBytes -= current.byteLength;
128
+ }
129
+ while (this.browserArtifacts.size >= BROWSER_ARTIFACT_CACHE_MAX_ENTRIES
130
+ || this.browserArtifactBytes + artifact.byteLength > BROWSER_ARTIFACT_CACHE_MAX_BYTES) {
131
+ const oldest = this.browserArtifacts.values().next().value;
132
+ if (oldest === undefined)
133
+ break;
134
+ this.browserArtifacts.delete(oldest.pluginId);
135
+ this.browserArtifactBytes -= oldest.byteLength;
136
+ }
137
+ this.browserArtifacts.set(artifact.pluginId, artifact);
138
+ this.browserArtifactBytes += artifact.byteLength;
139
+ }
140
+ touchBrowserArtifact(artifact) {
141
+ if (this.browserArtifacts.get(artifact.pluginId) !== artifact)
142
+ return;
143
+ this.browserArtifacts.delete(artifact.pluginId);
144
+ this.browserArtifacts.set(artifact.pluginId, artifact);
145
+ }
146
+ async lifecycle() {
147
+ const desired = await this.catalog.snapshot();
148
+ const [runtime, desiredSafeStart] = await Promise.all([this.loadRuntime(), this.loadDesiredSafeStart()]);
149
+ return reconcilePiWebPluginLifecycle(desired, runtime, browserModuleUrl, desiredSafeStart);
150
+ }
151
+ async loadDesiredSafeStart() {
152
+ if (this.recoveryProvider === undefined)
153
+ return undefined;
154
+ return (await this.recoveryProvider()).safeStart ?? "off";
122
155
  }
123
- async discoverPiPackagePlugins(packageProvider) {
124
- const plugins = [];
125
- for (const configuredPackage of packageProvider.listPackages()) {
126
- const root = configuredPackage.installedPath ?? packageProvider.getInstalledPath(configuredPackage.source, configuredPackage.scope);
127
- if (root === undefined)
128
- continue;
129
- try {
130
- plugins.push(...await discoverPackageRoot(root, configuredPackage));
131
- }
132
- catch (error) {
133
- warnInvalidPlugin(configuredPackage.source, error);
134
- }
156
+ async loadRuntime() {
157
+ if (this.runtimeProvider === undefined) {
158
+ return { status: "unavailable", message: "Session daemon server-plugin runtime is unavailable" };
159
+ }
160
+ try {
161
+ return { status: "available", snapshot: await this.runtimeProvider.providerRuntime() };
162
+ }
163
+ catch (error) {
164
+ const message = error instanceof Error ? error.message : String(error);
165
+ return {
166
+ status: error instanceof WorkspaceCatalogProtocolError ? "incompatible" : "unavailable",
167
+ message,
168
+ };
135
169
  }
136
- return plugins;
137
170
  }
138
171
  }
139
- function defaultPluginRoots(cwd) {
140
- const moduleDir = dirname(fileURLToPath(import.meta.url));
141
- const packageRoot = join(moduleDir, "..", "..");
142
- return [
143
- { path: bundledPluginRoot(packageRoot), source: "bundled", scope: "bundled" },
144
- ...sourceCheckoutPluginRoots(cwd),
145
- { path: join(piWebDataDir(), "plugins"), source: "local", scope: "local" },
146
- ];
172
+ function browserModuleUrl(plugin) {
173
+ const browserModule = plugin.browserModule;
174
+ if (browserModule === undefined)
175
+ throw new Error(`PI WEB plugin has no browser module: ${plugin.id}`);
176
+ const path = browserModule.path.split("/").map((segment) => encodeURIComponent(segment)).join("/");
177
+ return `/pi-web-plugins/${encodeURIComponent(plugin.id)}/${path}?${pluginModuleQuery(plugin.id, browserModule.revision)}`;
147
178
  }
148
- function bundledPluginRoot(packageRoot) {
149
- return join(packageRoot, "dist", "pi-web-plugins");
150
- }
151
- function pluginModuleQuery(plugin) {
152
- const params = new URLSearchParams({ v: plugin.version });
153
- const dockerMode = plugin.id === "updates" ? dockerModeFromEnv() : undefined;
179
+ function pluginModuleQuery(pluginId, revision) {
180
+ const params = new URLSearchParams({ v: revision });
181
+ const dockerMode = pluginId === "updates" ? dockerModeFromEnv() : undefined;
154
182
  if (dockerMode !== undefined)
155
183
  params.set("piWebDockerMode", dockerMode);
156
184
  return params.toString();
@@ -179,136 +207,9 @@ function isTruthyEnv(key) {
179
207
  const value = process.env[key];
180
208
  return value !== undefined && value !== "" && value !== "0" && value.toLowerCase() !== "false";
181
209
  }
182
- function sourceCheckoutPluginRoots(cwd) {
183
- const pluginsRoot = join(cwd, "plugins");
184
- if (!existsSync(join(cwd, "src", "server", "index.ts")) || !existsSync(pluginsRoot))
185
- return [];
186
- return [{ path: pluginsRoot, source: "dev", scope: "local" }];
187
- }
188
- async function discoverLocalRoot(root) {
189
- if (!existsSync(root.path))
190
- return [];
191
- const entries = await readdir(root.path, { withFileTypes: true }).catch(() => []);
192
- const plugins = [];
193
- for (const entry of entries) {
194
- if (!isPiWebPluginId(entry.name))
195
- continue;
196
- const pluginRoot = join(root.path, entry.name);
197
- const pluginStat = entry.isDirectory() ? undefined : entry.isSymbolicLink() ? await stat(pluginRoot).catch(() => undefined) : undefined;
198
- if (!entry.isDirectory() && pluginStat?.isDirectory() !== true)
199
- continue;
200
- try {
201
- plugins.push(...await discoverLocalPlugin(pluginRoot, root));
202
- }
203
- catch (error) {
204
- warnInvalidPlugin(pluginRoot, error);
205
- }
206
- }
207
- return plugins;
208
- }
209
- async function discoverLocalPlugin(root, localRoot) {
210
- const config = await readPiWebPackageConfig(root);
211
- if (config === undefined)
212
- return [];
213
- const plugins = await discoverPluginEntries(root, config);
214
- return plugins.map((plugin) => ({ ...plugin, source: localRoot.source, scope: localRoot.scope }));
215
- }
216
- async function discoverPackageRoot(root, configuredPackage) {
217
- const config = await readPiWebPackageConfig(root);
218
- if (config === undefined)
219
- return [];
220
- const plugins = await discoverPluginEntries(root, config);
221
- return plugins.map((plugin) => ({ ...plugin, source: configuredPackage.source, scope: configuredPackage.scope }));
222
- }
223
- async function discoverPluginEntries(root, config) {
224
- const plugins = [];
225
- for (const entry of config.plugins) {
226
- if (!isSafeRelativePath(entry.module))
227
- throw new Error(`Unsafe PI WEB plugin module path for ${entry.id}: ${entry.module}`);
228
- const entryPath = join(root, entry.module);
229
- const entryStat = await stat(entryPath).catch(() => undefined);
230
- if (entryStat?.isFile() !== true)
231
- throw new Error(`PI WEB plugin module not found for ${entry.id}: ${entry.module}`);
232
- plugins.push({ id: entry.id, root, entryFile: entry.module, version: String(Math.floor(entryStat.mtimeMs)), machineSpecific: entry.machineSpecific });
233
- }
234
- return plugins;
235
- }
236
- async function readPiWebPackageConfig(root) {
237
- const packagePath = join(root, "package.json");
238
- const content = await readFile(packagePath, "utf8").catch(() => undefined);
239
- if (content === undefined)
240
- return undefined;
241
- const parsed = JSON.parse(content);
242
- if (!isRecord(parsed))
243
- return undefined;
244
- const piWeb = parsed["piWeb"];
245
- if (!isRecord(piWeb))
246
- return undefined;
247
- const plugins = parsePluginEntries(piWeb, packagePath);
248
- if (plugins.length === 0)
249
- return undefined;
250
- return { plugins };
251
- }
252
- function parsePluginEntries(piWeb, packagePath) {
253
- if (piWeb["plugin"] !== undefined)
254
- throw new Error(`Unsupported PI WEB plugin metadata in ${packagePath}: use piWeb.plugins with { id, module, machineSpecific? } entries`);
255
- const plugins = piWeb["plugins"];
256
- if (plugins === undefined)
257
- return [];
258
- if (!Array.isArray(plugins))
259
- throw new Error(`PI WEB plugins must be an array in ${packagePath}`);
260
- return plugins.map((entry, index) => {
261
- if (!isRecord(entry))
262
- throw new Error(`PI WEB plugin entry ${String(index + 1)} must be an object in ${packagePath}`);
263
- const id = entry["id"];
264
- const module = entry["module"];
265
- if (typeof id !== "string" || !isPiWebPluginId(id))
266
- throw new Error(`Invalid PI WEB plugin id in ${packagePath}: ${String(id)}`);
267
- if (typeof module !== "string" || module === "")
268
- throw new Error(`Invalid PI WEB plugin module for ${id} in ${packagePath}`);
269
- return { id, module, machineSpecific: parseMachineSpecific(entry["machineSpecific"], packagePath, id) };
270
- });
271
- }
272
- function parseMachineSpecific(value, packagePath, pluginId) {
273
- if (value === undefined)
274
- return false;
275
- if (typeof value !== "boolean")
276
- throw new Error(`Invalid PI WEB plugin machineSpecific value for ${pluginId} in ${packagePath}: ${formatUnknownValue(value)}`);
277
- return value;
278
- }
279
- function formatUnknownValue(value) {
280
- if (typeof value === "string")
281
- return value;
282
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol" || typeof value === "function" || value === null || value === undefined)
283
- return String(value);
284
- try {
285
- return JSON.stringify(value);
286
- }
287
- catch {
288
- return Object.prototype.toString.call(value);
289
- }
290
- }
291
- function addUnique(records, plugin) {
292
- if (records.has(plugin.id)) {
293
- warnInvalidPlugin(plugin.source, `Duplicate PI WEB plugin id: ${plugin.id}`);
294
- return;
295
- }
296
- records.set(plugin.id, plugin);
297
- }
298
- function warnInvalidPlugin(source, error) {
299
- const message = error instanceof Error ? error.message : String(error);
300
- console.warn(`Skipping PI WEB plugin from ${source}: ${message}`);
301
- }
302
- function isSafeRelativePath(path) {
303
- return path !== "" && !path.includes("..") && !path.startsWith("/");
304
- }
305
- function isWithin(root, candidate) {
306
- const rel = relative(root, candidate);
307
- return rel === "" || (!rel.startsWith("..") && !rel.startsWith(sep));
308
- }
309
210
  function contentTypeFor(path) {
310
211
  const lowerPath = path.toLowerCase();
311
- if (lowerPath.endsWith(".js"))
212
+ if (lowerPath.endsWith(".js") || lowerPath.endsWith(".mjs"))
312
213
  return "application/javascript; charset=utf-8";
313
214
  if (lowerPath.endsWith(".json"))
314
215
  return "application/json; charset=utf-8";
@@ -320,7 +221,4 @@ function contentTypeFor(path) {
320
221
  return "image/svg+xml";
321
222
  return "application/octet-stream";
322
223
  }
323
- function isRecord(value) {
324
- return typeof value === "object" && value !== null && !Array.isArray(value);
325
- }
326
224
  //# sourceMappingURL=piWebPluginService.js.map