@vincenthanxiaodu/pi-web 1.202608.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +185 -0
  3. package/dist/cli.js +1149 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/client/apple-touch-icon.png +0 -0
  6. package/dist/client/assets/CodeViewer-D_Q6ljLV.js +4 -0
  7. package/dist/client/assets/TerminalPanel-CTGixJIz.js +187 -0
  8. package/dist/client/assets/index-C2phil_t.js +5637 -0
  9. package/dist/client/assets/vendor-editor-core-CXO8gGab.js +12 -0
  10. package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +46 -0
  11. package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +1 -0
  12. package/dist/client/assets/vendor-terminal-BrP-ENHg.css +1 -0
  13. package/dist/client/assets/vendor-terminal-D8k4UKM2.js +35 -0
  14. package/dist/client/favicon.svg +11 -0
  15. package/dist/client/index.html +124 -0
  16. package/dist/client/manifest.webmanifest +24 -0
  17. package/dist/client/pwa-icon-192.png +0 -0
  18. package/dist/client/pwa-icon-512.png +0 -0
  19. package/dist/config.js +505 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/docker/piWebDockerCommandPlan.js +212 -0
  22. package/dist/docker/piWebDockerCommandPlan.js.map +1 -0
  23. package/dist/environment.js +4 -0
  24. package/dist/environment.js.map +1 -0
  25. package/dist/nativeServices/installedServiceDefinitions.js +602 -0
  26. package/dist/nativeServices/installedServiceDefinitions.js.map +1 -0
  27. package/dist/nativeServices/serviceAction.js +174 -0
  28. package/dist/nativeServices/serviceAction.js.map +1 -0
  29. package/dist/nativeServices/serviceDoctor.js +783 -0
  30. package/dist/nativeServices/serviceDoctor.js.map +1 -0
  31. package/dist/nativeServices/serviceInstall.js +35 -0
  32. package/dist/nativeServices/serviceInstall.js.map +1 -0
  33. package/dist/nativeServices/servicePlan.js +358 -0
  34. package/dist/nativeServices/servicePlan.js.map +1 -0
  35. package/dist/nativeServices/serviceProbe.js +447 -0
  36. package/dist/nativeServices/serviceProbe.js.map +1 -0
  37. package/dist/nativeServices/serviceRendering.js +132 -0
  38. package/dist/nativeServices/serviceRendering.js.map +1 -0
  39. package/dist/pi-web-plugins/git/browser/git-contract.js +108 -0
  40. package/dist/pi-web-plugins/git/browser/git-panel.js +609 -0
  41. package/dist/pi-web-plugins/git/browser/gitFileList.js +43 -0
  42. package/dist/pi-web-plugins/git/browser/gitFileShared.js +16 -0
  43. package/dist/pi-web-plugins/git/browser/gitFileTree.js +81 -0
  44. package/dist/pi-web-plugins/git/browser/gitFileViewPreference.js +35 -0
  45. package/dist/pi-web-plugins/git/browser/gitRoute.js +42 -0
  46. package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +10 -0
  47. package/dist/pi-web-plugins/git/browser/unifiedDiff.js +301 -0
  48. package/dist/pi-web-plugins/git/git-backend.js +439 -0
  49. package/dist/pi-web-plugins/git/package.json +16 -0
  50. package/dist/pi-web-plugins/git/server-plugin.js +176 -0
  51. package/dist/pi-web-plugins/info/infoInternals.js +222 -0
  52. package/dist/pi-web-plugins/info/package.json +9 -0
  53. package/dist/pi-web-plugins/info/pi-web-plugin.js +48 -0
  54. package/dist/pi-web-plugins/relays/markdownDocument.js +81 -0
  55. package/dist/pi-web-plugins/relays/package.json +9 -0
  56. package/dist/pi-web-plugins/relays/pi-web-plugin.js +44 -0
  57. package/dist/pi-web-plugins/relays/relayDiscovery.js +228 -0
  58. package/dist/pi-web-plugins/relays/relaysPanelElement.js +550 -0
  59. package/dist/pi-web-plugins/relays/vendor/README.md +24 -0
  60. package/dist/pi-web-plugins/relays/vendor/marked.esm.js +76 -0
  61. package/dist/pi-web-plugins/updates/package.json +9 -0
  62. package/dist/pi-web-plugins/updates/pi-web-plugin.js +185 -0
  63. package/dist/pi-web-plugins/updates/updatesLogic.js +97 -0
  64. package/dist/pi-web-plugins/workspace-tasks/config.js +91 -0
  65. package/dist/pi-web-plugins/workspace-tasks/package.json +9 -0
  66. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +48 -0
  67. package/dist/pi-web-plugins/workspace-tasks/taskRunner.js +12 -0
  68. package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +288 -0
  69. package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +47 -0
  70. package/dist/piWebVersionReport.js +278 -0
  71. package/dist/piWebVersionReport.js.map +1 -0
  72. package/dist/plugin-api.d.ts +222 -0
  73. package/dist/pluginRecoveryCli.js +172 -0
  74. package/dist/pluginRecoveryCli.js.map +1 -0
  75. package/dist/server/activeAgentProfileProvider.js +25 -0
  76. package/dist/server/activeAgentProfileProvider.js.map +1 -0
  77. package/dist/server/activity/workspaceActivityService.js +106 -0
  78. package/dist/server/activity/workspaceActivityService.js.map +1 -0
  79. package/dist/server/app.js +218 -0
  80. package/dist/server/app.js.map +1 -0
  81. package/dist/server/browserMessageProjection.js +54 -0
  82. package/dist/server/browserMessageProjection.js.map +1 -0
  83. package/dist/server/configRoutes.js +293 -0
  84. package/dist/server/configRoutes.js.map +1 -0
  85. package/dist/server/diagnostics/nodePtyNativeModule.js +36 -0
  86. package/dist/server/diagnostics/nodePtyNativeModule.js.map +1 -0
  87. package/dist/server/diagnostics/nodePtySpawnHelper.js +135 -0
  88. package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -0
  89. package/dist/server/git/gitEnv.js +15 -0
  90. package/dist/server/git/gitEnv.js.map +1 -0
  91. package/dist/server/goals/goalArchive.js +184 -0
  92. package/dist/server/goals/goalArchive.js.map +1 -0
  93. package/dist/server/goals/goalFile.js +200 -0
  94. package/dist/server/goals/goalFile.js.map +1 -0
  95. package/dist/server/goals/goalStore.js +65 -0
  96. package/dist/server/goals/goalStore.js.map +1 -0
  97. package/dist/server/index.js +7 -0
  98. package/dist/server/index.js.map +1 -0
  99. package/dist/server/machines/machineClient.js +191 -0
  100. package/dist/server/machines/machineClient.js.map +1 -0
  101. package/dist/server/machines/machinePluginProxyRoutes.js +247 -0
  102. package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -0
  103. package/dist/server/machines/machineProxyRoutes.js +318 -0
  104. package/dist/server/machines/machineProxyRoutes.js.map +1 -0
  105. package/dist/server/machines/machineRoutes.js +56 -0
  106. package/dist/server/machines/machineRoutes.js.map +1 -0
  107. package/dist/server/machines/machineService.js +274 -0
  108. package/dist/server/machines/machineService.js.map +1 -0
  109. package/dist/server/machines/machineStore.js +143 -0
  110. package/dist/server/machines/machineStore.js.map +1 -0
  111. package/dist/server/piPackageRoutes.js +91 -0
  112. package/dist/server/piPackageRoutes.js.map +1 -0
  113. package/dist/server/piPackageService.js +101 -0
  114. package/dist/server/piPackageService.js.map +1 -0
  115. package/dist/server/piWebPluginCatalog.js +584 -0
  116. package/dist/server/piWebPluginCatalog.js.map +1 -0
  117. package/dist/server/piWebPluginLifecycle.js +162 -0
  118. package/dist/server/piWebPluginLifecycle.js.map +1 -0
  119. package/dist/server/piWebPluginService.js +224 -0
  120. package/dist/server/piWebPluginService.js.map +1 -0
  121. package/dist/server/piWebReleaseLookupCache.js +37 -0
  122. package/dist/server/piWebReleaseLookupCache.js.map +1 -0
  123. package/dist/server/piWebStatus.js +624 -0
  124. package/dist/server/piWebStatus.js.map +1 -0
  125. package/dist/server/piWebStatusCache.js +45 -0
  126. package/dist/server/piWebStatusCache.js.map +1 -0
  127. package/dist/server/plugins/pluginBackendProxyRoutes.js +69 -0
  128. package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -0
  129. package/dist/server/plugins/serverPluginExec.js +184 -0
  130. package/dist/server/plugins/serverPluginExec.js.map +1 -0
  131. package/dist/server/plugins/serverPluginRuntime.js +480 -0
  132. package/dist/server/plugins/serverPluginRuntime.js.map +1 -0
  133. package/dist/server/projectTrustRoutes.js +80 -0
  134. package/dist/server/projectTrustRoutes.js.map +1 -0
  135. package/dist/server/projects/directorySuggestions.js +207 -0
  136. package/dist/server/projects/directorySuggestions.js.map +1 -0
  137. package/dist/server/projects/projectService.js +33 -0
  138. package/dist/server/projects/projectService.js.map +1 -0
  139. package/dist/server/realtime/sessionEventHub.js +93 -0
  140. package/dist/server/realtime/sessionEventHub.js.map +1 -0
  141. package/dist/server/requestCancellation.js +32 -0
  142. package/dist/server/requestCancellation.js.map +1 -0
  143. package/dist/server/sessiond/agentHttpDispatcher.js +108 -0
  144. package/dist/server/sessiond/agentHttpDispatcher.js.map +1 -0
  145. package/dist/server/sessiond/agentProcessEnvironment.js +61 -0
  146. package/dist/server/sessiond/agentProcessEnvironment.js.map +1 -0
  147. package/dist/server/sessiond/pluginBackendRoutes.js +64 -0
  148. package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -0
  149. package/dist/server/sessiond/sessionDaemonShutdown.js +31 -0
  150. package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -0
  151. package/dist/server/sessiond/sessionProxyRoutes.js +61 -0
  152. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -0
  153. package/dist/server/sessiond/sessionServiceDependencies.js +34 -0
  154. package/dist/server/sessiond/sessionServiceDependencies.js.map +1 -0
  155. package/dist/server/sessiond/sessiondStateOwnership.js +235 -0
  156. package/dist/server/sessiond/sessiondStateOwnership.js.map +1 -0
  157. package/dist/server/sessiond/workspaceCatalogRoutes.js +31 -0
  158. package/dist/server/sessiond/workspaceCatalogRoutes.js.map +1 -0
  159. package/dist/server/sessiond/workspaceRemovalRoutes.js +41 -0
  160. package/dist/server/sessiond/workspaceRemovalRoutes.js.map +1 -0
  161. package/dist/server/sessiond.js +387 -0
  162. package/dist/server/sessiond.js.map +1 -0
  163. package/dist/server/sessions/askUserTool.js +102 -0
  164. package/dist/server/sessions/askUserTool.js.map +1 -0
  165. package/dist/server/sessions/attachmentService.js +119 -0
  166. package/dist/server/sessions/attachmentService.js.map +1 -0
  167. package/dist/server/sessions/authProviderOptions.js +50 -0
  168. package/dist/server/sessions/authProviderOptions.js.map +1 -0
  169. package/dist/server/sessions/authRoutes.js +59 -0
  170. package/dist/server/sessions/authRoutes.js.map +1 -0
  171. package/dist/server/sessions/authService.js +122 -0
  172. package/dist/server/sessions/authService.js.map +1 -0
  173. package/dist/server/sessions/builtinCommands.js +27 -0
  174. package/dist/server/sessions/builtinCommands.js.map +1 -0
  175. package/dist/server/sessions/dockerEnvironmentFacts.js +254 -0
  176. package/dist/server/sessions/dockerEnvironmentFacts.js.map +1 -0
  177. package/dist/server/sessions/editPreview.js +196 -0
  178. package/dist/server/sessions/editPreview.js.map +1 -0
  179. package/dist/server/sessions/extensionDialogWaiters.js +76 -0
  180. package/dist/server/sessions/extensionDialogWaiters.js.map +1 -0
  181. package/dist/server/sessions/globalProviderPolicy.js +167 -0
  182. package/dist/server/sessions/globalProviderPolicy.js.map +1 -0
  183. package/dist/server/sessions/interruptedRunStore.js +147 -0
  184. package/dist/server/sessions/interruptedRunStore.js.map +1 -0
  185. package/dist/server/sessions/messagePaging.js +43 -0
  186. package/dist/server/sessions/messagePaging.js.map +1 -0
  187. package/dist/server/sessions/modelCatalogRefresher.js +193 -0
  188. package/dist/server/sessions/modelCatalogRefresher.js.map +1 -0
  189. package/dist/server/sessions/oauthLoginFlowService.js +346 -0
  190. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -0
  191. package/dist/server/sessions/pendingAskStore.js +275 -0
  192. package/dist/server/sessions/pendingAskStore.js.map +1 -0
  193. package/dist/server/sessions/pendingExtensionDialogStore.js +196 -0
  194. package/dist/server/sessions/pendingExtensionDialogStore.js.map +1 -0
  195. package/dist/server/sessions/piSessionManagerGateway.js +246 -0
  196. package/dist/server/sessions/piSessionManagerGateway.js.map +1 -0
  197. package/dist/server/sessions/piSessionService.js +4059 -0
  198. package/dist/server/sessions/piSessionService.js.map +1 -0
  199. package/dist/server/sessions/plainTextTheme.js +112 -0
  200. package/dist/server/sessions/plainTextTheme.js.map +1 -0
  201. package/dist/server/sessions/sessionArchiveStore.js +273 -0
  202. package/dist/server/sessions/sessionArchiveStore.js.map +1 -0
  203. package/dist/server/sessions/sessionArchiveTree.js +35 -0
  204. package/dist/server/sessions/sessionArchiveTree.js.map +1 -0
  205. package/dist/server/sessions/sessionCleanup.js +157 -0
  206. package/dist/server/sessions/sessionCleanup.js.map +1 -0
  207. package/dist/server/sessions/sessionCommandService.js +336 -0
  208. package/dist/server/sessions/sessionCommandService.js.map +1 -0
  209. package/dist/server/sessions/sessionEnvironmentFacts.js +48 -0
  210. package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -0
  211. package/dist/server/sessions/sessionFileFormat.js +27 -0
  212. package/dist/server/sessions/sessionFileFormat.js.map +1 -0
  213. package/dist/server/sessions/sessionFileHeader.js +104 -0
  214. package/dist/server/sessions/sessionFileHeader.js.map +1 -0
  215. package/dist/server/sessions/sessionModelScope.js +141 -0
  216. package/dist/server/sessions/sessionModelScope.js.map +1 -0
  217. package/dist/server/sessions/sessionNameGenerator.js +90 -0
  218. package/dist/server/sessions/sessionNameGenerator.js.map +1 -0
  219. package/dist/server/sessions/sessionNotificationStore.js +484 -0
  220. package/dist/server/sessions/sessionNotificationStore.js.map +1 -0
  221. package/dist/server/sessions/sessionRoutes.js +744 -0
  222. package/dist/server/sessions/sessionRoutes.js.map +1 -0
  223. package/dist/server/sessions/sessionRuntimeStore.js +2 -0
  224. package/dist/server/sessions/sessionRuntimeStore.js.map +1 -0
  225. package/dist/server/sessions/sessionService.js +2 -0
  226. package/dist/server/sessions/sessionService.js.map +1 -0
  227. package/dist/server/sessions/sessionSummaryScanner.js +491 -0
  228. package/dist/server/sessions/sessionSummaryScanner.js.map +1 -0
  229. package/dist/server/sessions/sessionTreeProjection.js +250 -0
  230. package/dist/server/sessions/sessionTreeProjection.js.map +1 -0
  231. package/dist/server/sessions/sessionUnreadStore.js +520 -0
  232. package/dist/server/sessions/sessionUnreadStore.js.map +1 -0
  233. package/dist/server/sessions/shutdownDrain.js +73 -0
  234. package/dist/server/sessions/shutdownDrain.js.map +1 -0
  235. package/dist/server/sessions/spawnSessionTool.js +48 -0
  236. package/dist/server/sessions/spawnSessionTool.js.map +1 -0
  237. package/dist/server/sessions/spawnSubsessionTool.js +216 -0
  238. package/dist/server/sessions/spawnSubsessionTool.js.map +1 -0
  239. package/dist/server/sessions/spawnTargetResolver.js +23 -0
  240. package/dist/server/sessions/spawnTargetResolver.js.map +1 -0
  241. package/dist/server/sessions/subsessionTranscript.js +235 -0
  242. package/dist/server/sessions/subsessionTranscript.js.map +1 -0
  243. package/dist/server/status/machineStatusRoutes.js +8 -0
  244. package/dist/server/status/machineStatusRoutes.js.map +1 -0
  245. package/dist/server/status/machineStatusService.js +181 -0
  246. package/dist/server/status/machineStatusService.js.map +1 -0
  247. package/dist/server/status/workspaceAttribution.js +87 -0
  248. package/dist/server/status/workspaceAttribution.js.map +1 -0
  249. package/dist/server/storage/projectStore.js +88 -0
  250. package/dist/server/storage/projectStore.js.map +1 -0
  251. package/dist/server/terminalProxyRoutes.js +141 -0
  252. package/dist/server/terminalProxyRoutes.js.map +1 -0
  253. package/dist/server/terminals/terminalRoutes.js +155 -0
  254. package/dist/server/terminals/terminalRoutes.js.map +1 -0
  255. package/dist/server/terminals/terminalService.js +305 -0
  256. package/dist/server/terminals/terminalService.js.map +1 -0
  257. package/dist/server/terminals/terminalSize.js +17 -0
  258. package/dist/server/terminals/terminalSize.js.map +1 -0
  259. package/dist/server/types.js +2 -0
  260. package/dist/server/types.js.map +1 -0
  261. package/dist/server/updates/fleetRoutes.js +111 -0
  262. package/dist/server/updates/fleetRoutes.js.map +1 -0
  263. package/dist/server/updates/restartRoutes.js +48 -0
  264. package/dist/server/updates/restartRoutes.js.map +1 -0
  265. package/dist/server/updates/selfUpdateRoutes.js +145 -0
  266. package/dist/server/updates/selfUpdateRoutes.js.map +1 -0
  267. package/dist/server/webSocketBridge.js +32 -0
  268. package/dist/server/webSocketBridge.js.map +1 -0
  269. package/dist/server/workingDirectory.js +44 -0
  270. package/dist/server/workingDirectory.js.map +1 -0
  271. package/dist/server/workspaceExplorerRoutes.js +147 -0
  272. package/dist/server/workspaceExplorerRoutes.js.map +1 -0
  273. package/dist/server/workspaces/effectivePathAccess.js +8 -0
  274. package/dist/server/workspaces/effectivePathAccess.js.map +1 -0
  275. package/dist/server/workspaces/fileContentService.js +190 -0
  276. package/dist/server/workspaces/fileContentService.js.map +1 -0
  277. package/dist/server/workspaces/filePreviewResponseHeaders.js +18 -0
  278. package/dist/server/workspaces/filePreviewResponseHeaders.js.map +1 -0
  279. package/dist/server/workspaces/filePreviewResponsePolicy.js +55 -0
  280. package/dist/server/workspaces/filePreviewResponsePolicy.js.map +1 -0
  281. package/dist/server/workspaces/filePreviewService.js +81 -0
  282. package/dist/server/workspaces/filePreviewService.js.map +1 -0
  283. package/dist/server/workspaces/fileSuggestions.js +520 -0
  284. package/dist/server/workspaces/fileSuggestions.js.map +1 -0
  285. package/dist/server/workspaces/fileTreeService.js +35 -0
  286. package/dist/server/workspaces/fileTreeService.js.map +1 -0
  287. package/dist/server/workspaces/pathAccessPolicy.js +90 -0
  288. package/dist/server/workspaces/pathAccessPolicy.js.map +1 -0
  289. package/dist/server/workspaces/pathSafety.js +45 -0
  290. package/dist/server/workspaces/pathSafety.js.map +1 -0
  291. package/dist/server/workspaces/projectPiWebConfig.js +61 -0
  292. package/dist/server/workspaces/projectPiWebConfig.js.map +1 -0
  293. package/dist/server/workspaces/projectWorkspaceCwds.js +17 -0
  294. package/dist/server/workspaces/projectWorkspaceCwds.js.map +1 -0
  295. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +402 -0
  296. package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -0
  297. package/dist/server/workspaces/workspaceCatalog.js +47 -0
  298. package/dist/server/workspaces/workspaceCatalog.js.map +1 -0
  299. package/dist/server/workspaces/workspaceContext.js +6 -0
  300. package/dist/server/workspaces/workspaceContext.js.map +1 -0
  301. package/dist/server/workspaces/workspaceDeletionRoutes.js +49 -0
  302. package/dist/server/workspaces/workspaceDeletionRoutes.js.map +1 -0
  303. package/dist/server/workspaces/workspaceProviderRegistry.js +651 -0
  304. package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -0
  305. package/dist/server/workspaces/workspaceRemovalService.js +256 -0
  306. package/dist/server/workspaces/workspaceRemovalService.js.map +1 -0
  307. package/dist/server/workspaces/workspaceRouteErrors.js +7 -0
  308. package/dist/server/workspaces/workspaceRouteErrors.js.map +1 -0
  309. package/dist/server/workspaces/worktreePreRemoveHook.js +39 -0
  310. package/dist/server/workspaces/worktreePreRemoveHook.js.map +1 -0
  311. package/dist/server-plugin-api.d.ts +140 -0
  312. package/dist/server-plugin-api.js +2 -0
  313. package/dist/server-plugin-api.js.map +1 -0
  314. package/dist/serverPluginRecovery.js +138 -0
  315. package/dist/serverPluginRecovery.js.map +1 -0
  316. package/dist/sessiond/activeAgentProfile.js +12 -0
  317. package/dist/sessiond/activeAgentProfile.js.map +1 -0
  318. package/dist/sessiond/config.js +20 -0
  319. package/dist/sessiond/config.js.map +1 -0
  320. package/dist/sessiond/sessionDaemonClient.js +105 -0
  321. package/dist/sessiond/sessionDaemonClient.js.map +1 -0
  322. package/dist/shared/activeAgentProfile.js +37 -0
  323. package/dist/shared/activeAgentProfile.js.map +1 -0
  324. package/dist/shared/activity.js +16 -0
  325. package/dist/shared/activity.js.map +1 -0
  326. package/dist/shared/apiTypes.js +41 -0
  327. package/dist/shared/apiTypes.js.map +1 -0
  328. package/dist/shared/capabilities.js +37 -0
  329. package/dist/shared/capabilities.js.map +1 -0
  330. package/dist/shared/federatedRoutes.js +127 -0
  331. package/dist/shared/federatedRoutes.js.map +1 -0
  332. package/dist/shared/machinePluginIds.js +43 -0
  333. package/dist/shared/machinePluginIds.js.map +1 -0
  334. package/dist/shared/machineStatus.js +94 -0
  335. package/dist/shared/machineStatus.js.map +1 -0
  336. package/dist/shared/piWebStatusParsing.js +141 -0
  337. package/dist/shared/piWebStatusParsing.js.map +1 -0
  338. package/dist/shared/pluginApiTypes.d.ts +150 -0
  339. package/dist/shared/pluginApiTypes.js +3 -0
  340. package/dist/shared/pluginApiTypes.js.map +1 -0
  341. package/dist/shared/pluginBackendProtocol.js +110 -0
  342. package/dist/shared/pluginBackendProtocol.js.map +1 -0
  343. package/dist/shared/pluginIds.js +10 -0
  344. package/dist/shared/pluginIds.js.map +1 -0
  345. package/dist/shared/pluginRecoveryCommands.js +14 -0
  346. package/dist/shared/pluginRecoveryCommands.js.map +1 -0
  347. package/dist/shared/promptAttachments.js +92 -0
  348. package/dist/shared/promptAttachments.js.map +1 -0
  349. package/dist/shared/selfUpdate.js +3 -0
  350. package/dist/shared/selfUpdate.js.map +1 -0
  351. package/dist/shared/thinkingLevels.js +31 -0
  352. package/dist/shared/thinkingLevels.js.map +1 -0
  353. package/dist/shared/workspaceDeletion.js +12 -0
  354. package/dist/shared/workspaceDeletion.js.map +1 -0
  355. package/dist/shared/workspaceFiles.js +42 -0
  356. package/dist/shared/workspaceFiles.js.map +1 -0
  357. package/dist/shared/workspaceRemovalProtocol.js +24 -0
  358. package/dist/shared/workspaceRemovalProtocol.js.map +1 -0
  359. package/docs/assets/favicon.svg +11 -0
  360. package/docs/assets/pi-web-banner.png +0 -0
  361. package/docs/assets/pi-web-desktop.png +0 -0
  362. package/docs/assets/pi-web-mobile.png +0 -0
  363. package/docs/assets/pi-web-tablet.png +0 -0
  364. package/docs/config.md +444 -0
  365. package/docs/plugins.md +1305 -0
  366. package/examples/workspace-provider-plugin/README.md +52 -0
  367. package/examples/workspace-provider-plugin/package.json +25 -0
  368. package/examples/workspace-provider-plugin/src/browser/index.ts +69 -0
  369. package/examples/workspace-provider-plugin/src/server.ts +64 -0
  370. package/examples/workspace-provider-plugin/tsconfig.json +21 -0
  371. package/extensions/README-pi-web-restart-migration.md +45 -0
  372. package/extensions/pi-web.ts +324 -0
  373. package/install.sh +5 -0
  374. package/package.json +173 -0
  375. package/plugin-api.d.ts +1 -0
  376. package/server-plugin-api.d.ts +1 -0
@@ -0,0 +1,550 @@
1
+ // Generated from pi-web-plugins/relays/relaysPanelElement.ts. Do not edit directly.
2
+ import { isMarkdownDocumentPath, renderRelayDocumentHtml } from "./markdownDocument.js";
3
+ import { ancestorDirectoryPaths, collectDirectoryPaths, defaultRelayDocument, flattenRelayTree, listRelayDocumentTree, listWorkspaceRelays, readRelayDocument, RELAYS_ROOT, } from "./relayDiscovery.js";
4
+ export const relaysPanelTagName = "pi-web-relays-panel";
5
+ export function defineRelaysPanelElement() {
6
+ if (!customElements.get(relaysPanelTagName))
7
+ customElements.define(relaysPanelTagName, PiWebRelaysPanel);
8
+ }
9
+ /**
10
+ * Read-only relay browser: relay picker (auto-opens a single relay), one tab
11
+ * per relay document, and a document viewer rendering markdown documents as
12
+ * sanitized HTML and everything else as preformatted text. Documents in
13
+ * subfolders appear as folder chips that expand inline into the tab strip
14
+ * (accordion: expanding one folder collapses its siblings at the same level);
15
+ * an expanded folder wraps its chip and children in a group so nested tabs
16
+ * stay visually contained. Collapsing a folder keeps the selected document
17
+ * open and highlights the folder instead. All async loads flow through
18
+ * scanToken so stale responses for a previous workspace or selection never
19
+ * overwrite newer state.
20
+ *
21
+ * Rendering is region-scoped: the toolbar, tab strip, and viewer are
22
+ * persistent elements built once, and each async stage re-renders only its
23
+ * own region. Clicking a tab never rebuilds the strip — it toggles the
24
+ * active marker in place and re-renders the viewer — so the strip's
25
+ * horizontal scroll position and keyboard focus survive document switches.
26
+ */
27
+ class PiWebRelaysPanel extends HTMLElement {
28
+ contextValue;
29
+ listing;
30
+ selectedRelayPath;
31
+ documents;
32
+ selectedDocumentPath;
33
+ documentContent;
34
+ /** Directory paths currently expanded in the tab strip; an accordion spine, cleared on relay/workspace switch. */
35
+ expandedDirs = new Set();
36
+ scanToken = 0;
37
+ root;
38
+ toolbar;
39
+ tabStrip;
40
+ viewer;
41
+ constructor() {
42
+ super();
43
+ this.root = this.attachShadow({ mode: "open" });
44
+ this.root.innerHTML = `
45
+ ${relaysStyles()}
46
+ <section class="toolbar" hidden></section>
47
+ <nav class="document-tabs" aria-label="Relay documents" hidden></nav>
48
+ <section class="viewer"><div class="empty">Select a workspace.</div></section>
49
+ `;
50
+ this.toolbar = requiredRegion(this.root, ".toolbar");
51
+ this.tabStrip = requiredRegion(this.root, "nav.document-tabs");
52
+ this.viewer = requiredRegion(this.root, ".viewer");
53
+ // Listeners bind once against the persistent regions and delegate to
54
+ // whichever controls the latest region render produced.
55
+ this.toolbar.addEventListener("click", (event) => {
56
+ const button = event.target instanceof Element ? event.target.closest("button[data-refresh]") : null;
57
+ if (button !== null)
58
+ this.refresh();
59
+ });
60
+ this.toolbar.addEventListener("change", (event) => {
61
+ const picker = event.target;
62
+ if (!(picker instanceof HTMLSelectElement) || !picker.matches("select[data-relay-picker]"))
63
+ return;
64
+ const context = this.contextValue;
65
+ if (context !== undefined)
66
+ void this.openRelay(context, picker.value);
67
+ });
68
+ this.tabStrip.addEventListener("click", (event) => {
69
+ const element = event.target instanceof Element ? event.target : null;
70
+ const chip = element?.closest("button[data-directory-path]") ?? null;
71
+ if (chip !== null) {
72
+ const directoryPath = chip.getAttribute("data-directory-path");
73
+ if (directoryPath !== null)
74
+ this.toggleDirectory(directoryPath);
75
+ return;
76
+ }
77
+ const tab = element?.closest("button[data-document-path]") ?? null;
78
+ if (tab === null)
79
+ return;
80
+ const documentPath = tab.getAttribute("data-document-path");
81
+ const context = this.contextValue;
82
+ if (context !== undefined && documentPath !== null)
83
+ void this.openDocument(context, documentPath);
84
+ });
85
+ }
86
+ set context(value) {
87
+ const previousKey = this.contextValue === undefined ? undefined : contextKey(this.contextValue);
88
+ const nextKey = value === undefined ? undefined : contextKey(value);
89
+ this.contextValue = value;
90
+ // Parent app updates should not rescan or re-render this panel for the
91
+ // same workspace (mirrors the workspace-tasks panel).
92
+ if (previousKey === nextKey)
93
+ return;
94
+ // A different workspace starts with every folder collapsed.
95
+ this.expandedDirs = new Set();
96
+ if (value === undefined) {
97
+ this.resetScanState();
98
+ this.renderAll();
99
+ return;
100
+ }
101
+ void this.scan(value, {});
102
+ }
103
+ /** Rescan relays, then reload the selected relay's documents and the open document. */
104
+ async scan(context, selection) {
105
+ const token = ++this.scanToken;
106
+ this.resetScanState();
107
+ this.renderAll();
108
+ const listing = await listWorkspaceRelays(context.files);
109
+ if (!this.isCurrentScan(context, token))
110
+ return;
111
+ this.listing = listing;
112
+ // listWorkspaceRelays returns most recently modified first, so the first
113
+ // relay is the default pre-selection.
114
+ const relay = listing.kind === "loaded"
115
+ ? listing.relays.find((candidate) => candidate.path === selection.relayPath) ?? listing.relays[0]
116
+ : undefined;
117
+ this.selectedRelayPath = relay?.path;
118
+ this.renderToolbar();
119
+ if (relay === undefined) {
120
+ this.renderViewer();
121
+ return;
122
+ }
123
+ await this.loadDocuments(context, token, relay.path, selection.documentPath);
124
+ }
125
+ async openRelay(context, relayPath) {
126
+ const token = ++this.scanToken;
127
+ this.selectedRelayPath = relayPath;
128
+ this.expandedDirs = new Set();
129
+ await this.loadDocuments(context, token, relayPath, undefined);
130
+ }
131
+ /** Expand or collapse a folder chip; the accordion rule keeps at most one expanded folder per level. */
132
+ toggleDirectory(directoryPath) {
133
+ if (this.documents?.kind !== "loaded")
134
+ return;
135
+ if (this.expandedDirs.has(directoryPath)) {
136
+ for (const path of [...this.expandedDirs]) {
137
+ if (path === directoryPath || path.startsWith(`${directoryPath}/`))
138
+ this.expandedDirs.delete(path);
139
+ }
140
+ }
141
+ else {
142
+ this.collapseSiblingDirectories(directoryPath);
143
+ this.expandedDirs.add(directoryPath);
144
+ }
145
+ this.renderTabs();
146
+ const chip = this.findDirectoryChip(directoryPath);
147
+ // The innerHTML rebuild destroyed the clicked button; give focus back so
148
+ // keyboard users stay on the control they just toggled.
149
+ chip?.focus();
150
+ if (this.expandedDirs.has(directoryPath))
151
+ this.scrollExpandedChildrenIntoView(chip);
152
+ }
153
+ /** Expanding a folder collapses its siblings (same parent) and everything expanded under them. */
154
+ collapseSiblingDirectories(directoryPath) {
155
+ const parentPath = directoryPath.slice(0, directoryPath.lastIndexOf("/"));
156
+ for (const expanded of [...this.expandedDirs]) {
157
+ if (expanded.slice(0, expanded.lastIndexOf("/")) !== parentPath)
158
+ continue;
159
+ this.expandedDirs.delete(expanded);
160
+ for (const path of [...this.expandedDirs]) {
161
+ if (path.startsWith(`${expanded}/`))
162
+ this.expandedDirs.delete(path);
163
+ }
164
+ }
165
+ }
166
+ /** Expand every ancestor of a freshly chosen default document so it is visible. */
167
+ revealDocument(documentPath) {
168
+ const documents = this.documents;
169
+ if (documents?.kind !== "loaded")
170
+ return;
171
+ for (const path of ancestorDirectoryPaths(documents.tree, documentPath))
172
+ this.expandedDirs.add(path);
173
+ }
174
+ /** Drop expansion state for directories that vanished since the last scan. */
175
+ pruneExpandedDirs(tree) {
176
+ const existing = collectDirectoryPaths(tree);
177
+ for (const path of [...this.expandedDirs]) {
178
+ if (!existing.has(path))
179
+ this.expandedDirs.delete(path);
180
+ }
181
+ }
182
+ findDirectoryChip(directoryPath) {
183
+ return [...this.tabStrip.querySelectorAll("button[data-directory-path]")]
184
+ .find((candidate) => candidate instanceof HTMLElement && candidate.getAttribute("data-directory-path") === directoryPath);
185
+ }
186
+ scrollExpandedChildrenIntoView(chip) {
187
+ const firstChild = chip?.nextElementSibling;
188
+ if (firstChild instanceof HTMLElement)
189
+ firstChild.scrollIntoView({ inline: "nearest", block: "nearest" });
190
+ }
191
+ async openDocument(context, documentPath) {
192
+ const token = ++this.scanToken;
193
+ this.selectedDocumentPath = documentPath;
194
+ // The tab set is unchanged: toggle the active marker on the mounted
195
+ // buttons instead of rebuilding the strip, so its scroll position and
196
+ // focus stay put. A different document starts reading from the top.
197
+ this.updateActiveTab();
198
+ this.viewer.scrollTop = 0;
199
+ await this.loadDocumentContent(context, token, documentPath);
200
+ }
201
+ async loadDocuments(context, token, relayPath, preferredDocumentPath) {
202
+ this.documents = undefined;
203
+ this.selectedDocumentPath = undefined;
204
+ this.documentContent = undefined;
205
+ this.renderTabs();
206
+ this.renderViewer();
207
+ const documents = await listRelayDocumentTree(context.files, relayPath);
208
+ if (!this.isCurrentScan(context, token))
209
+ return;
210
+ this.documents = documents;
211
+ const files = documents.kind === "loaded" ? flattenRelayTree(documents.tree) : [];
212
+ const preferred = files.find((candidate) => candidate.path === preferredDocumentPath);
213
+ const document = preferred ?? defaultRelayDocument(documents.kind === "loaded" ? documents.tree : []);
214
+ this.selectedDocumentPath = document?.path;
215
+ if (documents.kind === "loaded")
216
+ this.pruneExpandedDirs(documents.tree);
217
+ // A freshly chosen default must be visible; a restored preferred pick keeps
218
+ // whatever the user expanded (a hidden pick is surfaced by the folder highlight).
219
+ if (preferred === undefined && document !== undefined)
220
+ this.revealDocument(document.path);
221
+ this.renderTabs();
222
+ if (document === undefined) {
223
+ this.renderViewer();
224
+ return;
225
+ }
226
+ await this.loadDocumentContent(context, token, document.path);
227
+ }
228
+ async loadDocumentContent(context, token, documentPath) {
229
+ this.documentContent = undefined;
230
+ this.renderViewer();
231
+ const content = await readRelayDocument(context.files, documentPath);
232
+ if (!this.isCurrentScan(context, token))
233
+ return;
234
+ this.documentContent = content;
235
+ this.renderViewer();
236
+ }
237
+ refresh() {
238
+ const context = this.contextValue;
239
+ if (context === undefined)
240
+ return;
241
+ void this.scan(context, { relayPath: this.selectedRelayPath, documentPath: this.selectedDocumentPath });
242
+ }
243
+ resetScanState() {
244
+ this.listing = undefined;
245
+ this.selectedRelayPath = undefined;
246
+ this.documents = undefined;
247
+ this.selectedDocumentPath = undefined;
248
+ this.documentContent = undefined;
249
+ }
250
+ isCurrentScan(context, token) {
251
+ return token === this.scanToken && this.contextValue !== undefined && contextKey(this.contextValue) === contextKey(context);
252
+ }
253
+ renderAll() {
254
+ this.renderToolbar();
255
+ this.renderTabs();
256
+ this.renderViewer();
257
+ }
258
+ renderToolbar() {
259
+ if (this.contextValue === undefined) {
260
+ this.toolbar.hidden = true;
261
+ this.toolbar.replaceChildren();
262
+ return;
263
+ }
264
+ this.toolbar.hidden = false;
265
+ this.toolbar.innerHTML = `
266
+ <strong>Relays</strong>
267
+ <span class="toolbar-actions">
268
+ ${this.renderRelayPicker()}
269
+ <button class="icon-button" data-refresh aria-label="Refresh" title="Refresh">${refreshIconSvg()}</button>
270
+ </span>
271
+ `;
272
+ }
273
+ renderRelayPicker() {
274
+ const listing = this.listing;
275
+ if (listing?.kind !== "loaded" || listing.relays.length === 0)
276
+ return "";
277
+ // A single relay opens immediately; a one-option picker would be noise.
278
+ if (listing.relays.length === 1) {
279
+ const relay = listing.relays[0];
280
+ return relay === undefined ? "" : `<span class="relay-name" title="${escapeAttr(relay.path)}">${escapeHtml(relay.name)}</span>`;
281
+ }
282
+ const options = listing.relays.map((relay) => {
283
+ const selected = relay.path === this.selectedRelayPath ? " selected" : "";
284
+ return `<option value="${escapeAttr(relay.path)}"${selected}>${escapeHtml(relay.name)}</option>`;
285
+ }).join("");
286
+ return `<select data-relay-picker aria-label="Relay">${options}</select>`;
287
+ }
288
+ renderTabs() {
289
+ const documents = this.documents;
290
+ if (documents?.kind !== "loaded" || documents.documentCount === 0) {
291
+ this.tabStrip.hidden = true;
292
+ // A new tab set starts at the left edge, not at the previous set's offset.
293
+ this.tabStrip.replaceChildren();
294
+ this.tabStrip.scrollLeft = 0;
295
+ return;
296
+ }
297
+ this.tabStrip.hidden = false;
298
+ const containsActivePath = this.selectedDocumentPath === undefined
299
+ ? undefined
300
+ : collapsedAncestorOfSelectedFile(documents.tree, this.selectedDocumentPath, this.expandedDirs);
301
+ // The strip element itself persists across re-renders, so replacing its
302
+ // buttons keeps the container's horizontal scroll position.
303
+ this.tabStrip.innerHTML = this.renderStripNodes(documents.tree, containsActivePath);
304
+ }
305
+ /**
306
+ * Depth-first strip markup: an expanded folder wraps its chip and children
307
+ * in one group element so nested tabs read as contained by their folder.
308
+ * Collapsed folders (and folders without listed children) render as a bare chip.
309
+ */
310
+ renderStripNodes(nodes, containsActivePath) {
311
+ return nodes.map((node) => {
312
+ if (node.kind === "file")
313
+ return this.renderFileTab(node);
314
+ const chip = this.renderDirectoryChip(node, containsActivePath);
315
+ if (!this.expandedDirs.has(node.path) || node.children.length === 0)
316
+ return chip;
317
+ return `<span class="directory-group">${chip}${this.renderStripNodes(node.children, containsActivePath)}</span>`;
318
+ }).join("");
319
+ }
320
+ renderFileTab(file) {
321
+ const active = file.path === this.selectedDocumentPath;
322
+ return `<button class="document-tab${active ? " active" : ""}" data-document-path="${escapeAttr(file.path)}" title="${escapeAttr(file.relativePath)}"${active ? ' aria-current="true"' : ""}>${escapeHtml(file.name)}</button>`;
323
+ }
324
+ renderDirectoryChip(directory, containsActivePath) {
325
+ const expanded = this.expandedDirs.has(directory.path);
326
+ const containsActive = directory.path === containsActivePath;
327
+ const title = containsActive ? "Contains the open document" : directory.relativePath;
328
+ return `<button class="document-tab directory-tab${containsActive ? " contains-active" : ""}" data-directory-path="${escapeAttr(directory.path)}" title="${escapeAttr(title)}" aria-expanded="${expanded ? "true" : "false"}">${chevronSvg()}${escapeHtml(directory.name)}</button>`;
329
+ }
330
+ /** Move the active marker between the mounted tab buttons without rebuilding them. */
331
+ updateActiveTab() {
332
+ for (const tab of this.tabStrip.querySelectorAll("button[data-document-path]")) {
333
+ const active = tab.getAttribute("data-document-path") === this.selectedDocumentPath;
334
+ tab.classList.toggle("active", active);
335
+ if (active)
336
+ tab.setAttribute("aria-current", "true");
337
+ else
338
+ tab.removeAttribute("aria-current");
339
+ }
340
+ // The clicked tab is visible by definition, so no folder highlight remains.
341
+ for (const chip of this.tabStrip.querySelectorAll("button[data-directory-path]"))
342
+ chip.classList.remove("contains-active");
343
+ }
344
+ renderViewer() {
345
+ if (this.contextValue === undefined) {
346
+ this.viewer.innerHTML = `<div class="empty">Select a workspace.</div>`;
347
+ return;
348
+ }
349
+ this.viewer.innerHTML = this.renderViewerContent();
350
+ }
351
+ renderViewerContent() {
352
+ const listing = this.listing;
353
+ if (listing === undefined)
354
+ return `<p class="muted">Scanning ${escapeHtml(RELAYS_ROOT)}…</p>`;
355
+ if (listing.kind === "unavailable")
356
+ return renderErrorState("Could not scan workspace relays.", listing.detail);
357
+ if (listing.kind === "missing" || listing.relays.length === 0)
358
+ return renderEmptyState();
359
+ return this.renderSelectedRelay();
360
+ }
361
+ renderSelectedRelay() {
362
+ const documents = this.documents;
363
+ if (documents === undefined)
364
+ return `<p class="muted">Loading relay documents…</p>`;
365
+ if (documents.kind === "unavailable")
366
+ return renderErrorState("Could not list this relay's documents.", documents.detail);
367
+ if (documents.kind === "missing") {
368
+ return `<div class="empty-state"><strong>This relay no longer exists.</strong><p>Click Refresh to rescan ${escapeHtml(RELAYS_ROOT)}.</p></div>`;
369
+ }
370
+ const partialNotice = documents.partial
371
+ ? `<div class="status info">Some nested content is not listed — this relay tree is deeper or larger than the panel lists.</div>`
372
+ : "";
373
+ if (documents.documentCount === 0) {
374
+ return `${partialNotice}
375
+ <div class="empty-state">
376
+ <strong>This relay has no documents yet.</strong>
377
+ <p>Relay packets usually contain <code>status.md</code>, <code>charter.md</code>, and <code>log.md</code>.</p>
378
+ </div>
379
+ `;
380
+ }
381
+ return `${partialNotice}${this.renderSelectedDocument()}`;
382
+ }
383
+ renderSelectedDocument() {
384
+ const documentPath = this.selectedDocumentPath;
385
+ const content = this.documentContent;
386
+ if (documentPath === undefined)
387
+ return `<p class="muted">Select a document.</p>`;
388
+ if (content === undefined)
389
+ return `<p class="muted">Loading ${escapeHtml(documentName(documentPath))}…</p>`;
390
+ if (content.kind === "unavailable")
391
+ return renderErrorState("Could not read this document.", content.detail);
392
+ if (content.kind === "missing") {
393
+ return `<div class="empty-state"><strong>This document no longer exists.</strong><p>Click Refresh to rescan the relay.</p></div>`;
394
+ }
395
+ if (content.binary) {
396
+ return `<div class="empty-state"><strong>Binary file: ${escapeHtml(documentName(documentPath))}</strong><p>Binary documents have no text preview.</p></div>`;
397
+ }
398
+ const truncation = content.truncated
399
+ ? `<div class="status info">This document is truncated — only the beginning is shown.</div>`
400
+ : "";
401
+ if (isMarkdownDocumentPath(documentPath)) {
402
+ return `${truncation}<div class="document markdown">${renderRelayDocumentHtml(content.content)}</div>`;
403
+ }
404
+ return `${truncation}<pre class="document">${escapeHtml(content.content)}</pre>`;
405
+ }
406
+ }
407
+ /** Shell regions come from a literal template; absence means the template broke. */
408
+ function requiredRegion(root, selector) {
409
+ const element = root.querySelector(selector);
410
+ if (!(element instanceof HTMLElement))
411
+ throw new Error(`relays panel shell is missing ${selector}`);
412
+ return element;
413
+ }
414
+ /** Reload glyph matching the app's own refresh control (AppRefreshControl). */
415
+ function refreshIconSvg() {
416
+ return `
417
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
418
+ <path d="M20 6v5h-5"></path>
419
+ <path d="M4 18v-5h5"></path>
420
+ <path d="M18.2 9A7 7 0 0 0 6.1 6.8L4 9"></path>
421
+ <path d="M5.8 15a7 7 0 0 0 12.1 2.2L20 15"></path>
422
+ </svg>
423
+ `;
424
+ }
425
+ /**
426
+ * The folder chip to highlight when the selected document is hidden: the first
427
+ * collapsed ancestor walking from the relay root. Undefined when every
428
+ * ancestor is expanded (the tab is visible) or the file is not in the tree.
429
+ */
430
+ export function collapsedAncestorOfSelectedFile(tree, filePath, expandedPaths) {
431
+ let hiddenAncestor;
432
+ const visit = (entries) => {
433
+ for (const entry of entries) {
434
+ if (entry.kind === "file") {
435
+ if (entry.path === filePath)
436
+ return true;
437
+ continue;
438
+ }
439
+ if (hiddenAncestor === undefined && !expandedPaths.has(entry.path))
440
+ hiddenAncestor = entry.path;
441
+ if (visit(entry.children))
442
+ return true;
443
+ if (hiddenAncestor === entry.path)
444
+ hiddenAncestor = undefined;
445
+ }
446
+ return false;
447
+ };
448
+ return visit(tree) ? hiddenAncestor : undefined;
449
+ }
450
+ function chevronSvg() {
451
+ return `<svg class="chevron" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M6 4l4 4-4 4"></path></svg>`;
452
+ }
453
+ function contextKey(context) {
454
+ return `${context.machine.id}:${context.workspace.projectId}:${context.workspace.id}`;
455
+ }
456
+ function documentName(path) {
457
+ return path.slice(path.lastIndexOf("/") + 1);
458
+ }
459
+ function renderEmptyState() {
460
+ return `
461
+ <div class="empty-state">
462
+ <strong>No relays in this workspace.</strong>
463
+ <p>Relay packets live in <code>${escapeHtml(RELAYS_ROOT)}/&lt;name&gt;/</code>. This workspace has none yet.</p>
464
+ </div>
465
+ `;
466
+ }
467
+ function renderErrorState(message, detail) {
468
+ return `<div class="status error"><strong>${escapeHtml(message)}</strong><pre>${escapeHtml(detail)}</pre></div>`;
469
+ }
470
+ function relaysStyles() {
471
+ return `
472
+ <style>
473
+ :host { display: contents; }
474
+ /* Toolbar and tab strip are panel chrome: they must never flex-shrink
475
+ (the app container is a fixed-height flex column; with shrink enabled
476
+ the viewer's huge content basis starves them down to a sliver once a
477
+ tall document renders). The viewer absorbs all shrinking instead. */
478
+ .toolbar { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--pi-border-muted); }
479
+ .toolbar[hidden], .document-tabs[hidden] { display: none; }
480
+ .toolbar-actions { display: inline-flex; align-items: center; flex-wrap: nowrap; justify-content: flex-end; gap: 8px; min-width: 0; }
481
+ .relay-name { min-width: 0; color: var(--pi-text-secondary); overflow-wrap: anywhere; }
482
+ /* Bottom padding (not viewer margin) so the gap below the tabs persists
483
+ when the viewer's content scrolls up against its top edge. */
484
+ .document-tabs { flex: 0 0 auto; display: flex; flex-wrap: nowrap; gap: 6px; padding: 8px 12px; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scrollbar-width: thin; }
485
+ .viewer { flex: 1 1 auto; box-sizing: border-box; display: grid; align-content: start; gap: 12px; min-height: 0; overflow: auto; padding: 12px; }
486
+ /* Grid children default to min-width: auto; without these caps a wide code
487
+ block or table would silently stretch the whole viewer track. */
488
+ .viewer > * { box-sizing: border-box; min-width: 0; max-width: 100%; }
489
+ button, select { border: 1px solid var(--pi-border); border-radius: 7px; background: var(--pi-surface); color: var(--pi-text); font: inherit; }
490
+ button { cursor: pointer; padding: 6px 10px; }
491
+ button.icon-button { flex: 0 0 auto; display: inline-grid; place-items: center; width: 30px; height: 30px; padding: 0; }
492
+ button.icon-button svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; }
493
+ select { min-width: 0; max-width: 240px; padding: 5px 6px; }
494
+ .document-tab { flex: 0 0 auto; white-space: nowrap; font-size: 12px; padding: 4px 10px; }
495
+ .document-tab.active { border-color: var(--pi-accent-border); background: var(--pi-accent); color: var(--pi-bg); }
496
+ /* An expanded folder wraps its chip and children in one rounded group so
497
+ nested tabs read as contained by their folder; nested groups stack.
498
+ No inner padding: the wrapper's border lines sit flush on the button
499
+ row (a continuous edge), and the -1px vertical margins cancel the
500
+ border's height so expanding never grows the strip or its neighbors. */
501
+ .directory-group { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; min-width: 0; border: 1px solid var(--pi-border-muted); border-radius: 10px; background: var(--pi-bg-overlay-soft); padding: 0; margin: -1px 0; }
502
+ /* Folder chips interleave with file tabs; the chevron rotates while expanded. */
503
+ .directory-tab { display: inline-flex; align-items: center; gap: 5px; }
504
+ .directory-tab .chevron { flex: 0 0 auto; width: 9px; height: 9px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; transition: transform 0.12s ease; }
505
+ .directory-tab[aria-expanded="true"] .chevron { transform: rotate(90deg); }
506
+ /* The selection survives a folder collapse: the nearest collapsed ancestor
507
+ highlights instead, without taking the fill reserved for the open document. */
508
+ .directory-tab.contains-active { border-color: var(--pi-accent-border); color: var(--pi-accent); }
509
+ .directory-tab.contains-active::after { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--pi-accent); }
510
+ code, pre { border: 1px solid var(--pi-border-muted); border-radius: 6px; background: var(--pi-bg); color: var(--pi-text-secondary); font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
511
+ code { padding: 2px 5px; }
512
+ pre { margin: 0; overflow: auto; padding: 8px; white-space: pre-wrap; overflow-wrap: anywhere; }
513
+ .document.markdown { line-height: 1.5; overflow-wrap: anywhere; }
514
+ .document.markdown p, .document.markdown ul, .document.markdown ol, .document.markdown pre, .document.markdown blockquote, .document.markdown .table-scroll { margin: 0 0 10px; }
515
+ .document.markdown > :last-child { margin-bottom: 0; }
516
+ .document.markdown h1, .document.markdown h2, .document.markdown h3, .document.markdown h4 { line-height: 1.25; margin: 14px 0 8px; }
517
+ .document.markdown h1:first-child, .document.markdown h2:first-child, .document.markdown h3:first-child, .document.markdown h4:first-child { margin-top: 0; }
518
+ .document.markdown h1 { font-size: 18px; }
519
+ .document.markdown h2 { font-size: 16px; }
520
+ .document.markdown h3 { font-size: 14px; }
521
+ .document.markdown h4 { font-size: 13px; }
522
+ .document.markdown ul, .document.markdown ol { padding-left: 22px; }
523
+ .document.markdown li + li { margin-top: 3px; }
524
+ .document.markdown pre { white-space: pre; overflow-wrap: normal; }
525
+ .document.markdown pre code { border: 0; background: transparent; padding: 0; }
526
+ .document.markdown img { box-sizing: border-box; max-width: 100%; }
527
+ .document.markdown blockquote { border-left: 3px solid var(--pi-border-muted); color: var(--pi-muted); padding-left: 10px; }
528
+ .document.markdown a { color: var(--pi-accent); }
529
+ .document.markdown .table-scroll { max-width: 100%; overflow-x: auto; }
530
+ /* Cells wrap at word boundaries only: a wide table keeps its natural width
531
+ and scrolls inside .table-scroll instead of being squeezed unreadably. */
532
+ .document.markdown table { border-collapse: collapse; overflow-wrap: normal; }
533
+ .document.markdown th, .document.markdown td { border: 1px solid var(--pi-border-muted); padding: 4px 8px; }
534
+ .status pre { margin-top: 8px; }
535
+ .muted { color: var(--pi-muted); }
536
+ .empty-state { border: 1px dashed var(--pi-border-muted); border-radius: 8px; color: var(--pi-muted); padding: 12px; }
537
+ .empty-state p { margin: 6px 0 0; }
538
+ .status { border: 1px solid var(--pi-border); border-radius: 8px; padding: 10px; }
539
+ .status.info { border-color: var(--pi-accent-border); background: var(--pi-bg-overlay-soft); }
540
+ .status.error { border-color: var(--pi-danger); color: var(--pi-danger); }
541
+ .empty { padding: 16px; color: var(--pi-muted); }
542
+ </style>
543
+ `;
544
+ }
545
+ function escapeHtml(value) {
546
+ return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
547
+ }
548
+ function escapeAttr(value) {
549
+ return escapeHtml(value).replaceAll('"', "&quot;");
550
+ }
@@ -0,0 +1,24 @@
1
+ # Vendored dependencies
2
+
3
+ ## marked (`marked.esm.js`)
4
+
5
+ - **Source:** `node_modules/marked/lib/marked.esm.js` — marked v18.0.6, the
6
+ version the repo pins (`"marked": "^18.0.6"` in the root `package.json`).
7
+ - **License:** MIT — copyright (c) 2018-2026 MarkedJS, (c) 2011-2018
8
+ Christopher Jeffrey. The attribution header at the top of `marked.esm.js` is
9
+ preserved; see also `node_modules/marked/LICENSE.md`.
10
+ - **Why vendored:** bundled plugins load in the browser as standalone ES
11
+ modules and cannot resolve bare package specifiers. The plugin therefore
12
+ ships the exact marked build the repo already depends on, rather than
13
+ hand-rolling a markdown subset or adding a markdown helper to the plugin API.
14
+ - **Local modification:** the trailing `//# sourceMappingURL=marked.esm.js.map`
15
+ comment was removed because the (much larger) source map is not vendored.
16
+ Everything else is byte-identical to the published file.
17
+ - **`marked.esm.d.ts`** is a hand-written minimal declaration covering only the
18
+ surface `markdownDocument.ts` uses. The plugin build
19
+ (`scripts/build-plugins.mjs`) skips `.d.ts` files and copies `.js` assets
20
+ verbatim, so the vendored module ships as-is.
21
+
22
+ **Updating:** after a `marked` upgrade in the root `package.json`, copy the new
23
+ `node_modules/marked/lib/marked.esm.js` here, re-apply the sourceMappingURL
24
+ removal, and update this note.