@theruansilva/omp-web 2.0.1 → 2.2.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 (209) hide show
  1. package/dist/cli.js +83 -3
  2. package/dist/cli.js.map +1 -1
  3. package/dist/client/assets/CodeViewer-B61IQaP0.js +4 -0
  4. package/dist/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  5. package/dist/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  6. package/dist/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  7. package/dist/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  8. package/dist/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  9. package/dist/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  10. package/dist/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  11. package/dist/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  12. package/dist/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  13. package/dist/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  14. package/dist/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  15. package/dist/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  16. package/dist/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  17. package/dist/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  18. package/dist/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  19. package/dist/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  20. package/dist/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  21. package/dist/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  22. package/dist/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  23. package/dist/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  24. package/dist/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  25. package/dist/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  26. package/dist/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  27. package/dist/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  28. package/dist/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  29. package/dist/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  30. package/dist/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  31. package/dist/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  32. package/dist/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  33. package/dist/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  34. package/dist/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  35. package/dist/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  36. package/dist/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  37. package/dist/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  38. package/dist/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  39. package/dist/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  40. package/dist/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  41. package/dist/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  42. package/dist/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  43. package/dist/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  44. package/dist/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  45. package/dist/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  46. package/dist/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  47. package/dist/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  48. package/dist/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  49. package/dist/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  50. package/dist/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  51. package/dist/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  52. package/dist/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  53. package/dist/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  54. package/dist/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  55. package/dist/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  56. package/dist/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  57. package/dist/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  58. package/dist/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  59. package/dist/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  60. package/dist/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  61. package/dist/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  62. package/dist/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  63. package/dist/client/assets/{TerminalPanel-Cb2CgFtQ.js → TerminalPanel-BcxMZQKk.js} +8 -8
  64. package/dist/client/assets/{UnifiedDiffViewer-Db5Yxwri.js → UnifiedDiffViewer-BnFQlZDg.js} +2 -2
  65. package/dist/client/assets/abnfDiagram-O67JEVCF-BCWHgm_-.js +1 -0
  66. package/dist/client/assets/arc-BUpZciy9.js +1 -0
  67. package/dist/client/assets/architectureDiagram-NJMV4G6O-CVWCwiqh.js +36 -0
  68. package/dist/client/assets/blockDiagram-BEXU5L5S-W5WSJa_J.js +140 -0
  69. package/dist/client/assets/c4Diagram-YGBWAQC7-BWpacECA.js +38 -0
  70. package/dist/client/assets/channel-ChkqWR3Z.js +1 -0
  71. package/dist/client/assets/chunk-2Q5K7J3B-D0TV4T28.js +1 -0
  72. package/dist/client/assets/chunk-3FUC2YCW-BlkJ2B9v.js +2 -0
  73. package/dist/client/assets/chunk-5VM5RSS4-LRh3HbZr.js +15 -0
  74. package/dist/client/assets/chunk-GTNCS2PH-CBiTovUf.js +88 -0
  75. package/dist/client/assets/chunk-GWA4HPMP-Dnu3R9yI.js +1 -0
  76. package/dist/client/assets/chunk-JWPE2WC7-C2yjQs6c.js +1 -0
  77. package/dist/client/assets/chunk-NETBCI7D-Dxos4L3P.js +1 -0
  78. package/dist/client/assets/chunk-WEXAMYUT-DrLvxPom.js +1 -0
  79. package/dist/client/assets/chunk-XXDRQBXY-Cub_nego.js +1 -0
  80. package/dist/client/assets/classDiagram-v2-NBCMYWYE-CIe2lhk7.js +217 -0
  81. package/dist/client/assets/cose-bilkent-JH36ORCC-DOGZoTZg.js +1 -0
  82. package/dist/client/assets/cynefin-OW5HDTMX-DXF0a63d.js +166 -0
  83. package/dist/client/assets/cynefinDiagram-VND7K2PF-C_Qmhc8V.js +62 -0
  84. package/dist/client/assets/cytoscape.esm-BB4DxJjf.js +321 -0
  85. package/dist/client/assets/dagre-6A5THRUB-CEhDrD4X.js +4 -0
  86. package/dist/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  87. package/dist/client/assets/diagram-22UHCM2B-CgN81sDZ.js +200 -0
  88. package/dist/client/assets/diagram-3UASUU5V-Dk1gOSew.js +24 -0
  89. package/dist/client/assets/diagram-ATOU4E4O-C_tcqhr3.js +3 -0
  90. package/dist/client/assets/diagram-CDSNMT55-Dj-WM3vk.js +30 -0
  91. package/dist/client/assets/diagram-MLGK6HIB-CDowox_d.js +24 -0
  92. package/dist/client/assets/diagram-MPIPVDR6-CaThzNCZ.js +41 -0
  93. package/dist/client/assets/ebnfDiagram-ZINNZB2B-BZbLdwcz.js +1 -0
  94. package/dist/client/assets/elk-276RUBZZ-DGku1nk-.js +27 -0
  95. package/dist/client/assets/erDiagram-OPXOYQCR-D0nJ53i7.js +99 -0
  96. package/dist/client/assets/flowDiagram-KWPJA3E3-DeB8-O7Y.js +213 -0
  97. package/dist/client/assets/ganttDiagram-FUAMR5RP-CBcW8sXy.js +292 -0
  98. package/dist/client/assets/gitGraphDiagram-X574FWY7-CC-b8FLq.js +106 -0
  99. package/dist/client/assets/graph-D8UmR1vK.js +1 -0
  100. package/dist/client/assets/index-CyN73uMY.js +5664 -0
  101. package/dist/client/assets/{index-DGBYb2j9.css → index-DSGiUJZL.css} +1 -1
  102. package/dist/client/assets/infoDiagram-VRGFBTTK-DL3QMN01.js +2 -0
  103. package/dist/client/assets/init-Gi6I4Gst.js +1 -0
  104. package/dist/client/assets/ishikawaDiagram-OU5B5YK6-Brt56LtP.js +70 -0
  105. package/dist/client/assets/journeyDiagram-ZHPQQLJL-Df8VwbDr.js +139 -0
  106. package/dist/client/assets/kanban-definition-PNTS6WVX-cVbdWfVg.js +89 -0
  107. package/dist/client/assets/katex-HP8lGamR.js +257 -0
  108. package/dist/client/assets/linear-DugbH7BO.js +1 -0
  109. package/dist/client/assets/mindmap-definition-NLK3R4M7-BDqwqJMJ.js +96 -0
  110. package/dist/client/assets/ordinal-Cboi1Yqb.js +1 -0
  111. package/dist/client/assets/pegDiagram-GJSIUBJH-w6CrSKt0.js +1 -0
  112. package/dist/client/assets/pieDiagram-5QR66LMP-D0utByOI.js +39 -0
  113. package/dist/client/assets/quadrantDiagram-O4NWA36T-DIAIplx2.js +7 -0
  114. package/dist/client/assets/railroadDiagram-XR7U4H2S-BX9P-jy7.js +1 -0
  115. package/dist/client/assets/requirementDiagram-PLB6GJNP-C55JlmRI.js +84 -0
  116. package/dist/client/assets/sankeyDiagram-IPEJSGJF-VeHnKI-a.js +40 -0
  117. package/dist/client/assets/sequenceDiagram-PO4LG4MO-CTUoXviF.js +169 -0
  118. package/dist/client/assets/sizeCapture-INFHLROL-DBKjJDUZ.js +1 -0
  119. package/dist/client/assets/stateDiagram-v2-GCMORJYK-DzonJh3D.js +291 -0
  120. package/dist/client/assets/swimlanes-2SLR337P-Cnsd5mvJ.js +1 -0
  121. package/dist/client/assets/swimlanesDiagram-TC7HE7FX-iyBCc4Xc.js +8 -0
  122. package/dist/client/assets/timeline-definition-EJHVYXUP-C9nhaxck.js +120 -0
  123. package/dist/client/assets/usecaseDiagram-POWQR4AR-jfXBWSzR.js +370 -0
  124. package/dist/client/assets/vendor-editor-core-D-5q2tVX.js +49 -0
  125. package/dist/client/assets/vendor-editor-languages-B6WRt0aC.js +26 -0
  126. package/dist/client/assets/vennDiagram-UO4OBE2U-BigR6haV.js +34 -0
  127. package/dist/client/assets/wardleyDiagram-VNRHLVJA-BXJexKTM.js +78 -0
  128. package/dist/client/assets/xychartDiagram-PMCCYNJV-HL5cuxsZ.js +7 -0
  129. package/dist/client/index.html +4 -4
  130. package/dist/config.js +11 -12
  131. package/dist/config.js.map +1 -1
  132. package/dist/omp-web-plugins/mcps/mcpsLogic.js +78 -0
  133. package/dist/omp-web-plugins/mcps/mcpsPanelElement.js +417 -0
  134. package/dist/omp-web-plugins/mcps/omp-web-plugin.js +45 -0
  135. package/dist/omp-web-plugins/mcps/package.json +9 -0
  136. package/dist/omp-web-plugins/mcps/types.js +2 -0
  137. package/dist/plugin-api.d.ts +1 -0
  138. package/dist/server/app.js +24 -1
  139. package/dist/server/app.js.map +1 -1
  140. package/dist/server/configRoutes.js +6 -0
  141. package/dist/server/configRoutes.js.map +1 -1
  142. package/dist/server/index.js +0 -0
  143. package/dist/server/machines/machineService.js +3 -0
  144. package/dist/server/machines/machineService.js.map +1 -1
  145. package/dist/server/mcpRoutes.js +73 -0
  146. package/dist/server/mcpRoutes.js.map +1 -0
  147. package/dist/server/push/PushNotificationService.js +16 -3
  148. package/dist/server/push/PushNotificationService.js.map +1 -1
  149. package/dist/server/security.js +172 -0
  150. package/dist/server/security.js.map +1 -0
  151. package/dist/server/sessiond/sessionProxyRoutes.js +11 -5
  152. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -1
  153. package/dist/server/sessiond.js +28 -1
  154. package/dist/server/sessiond.js.map +1 -1
  155. package/dist/server/sessions/piAgentSession.js +2 -1
  156. package/dist/server/sessions/piAgentSession.js.map +1 -1
  157. package/dist/server/sessions/piSessionService.js +168 -16
  158. package/dist/server/sessions/piSessionService.js.map +1 -1
  159. package/dist/server/sessions/schedulePrompt/scheduler.js +17 -13
  160. package/dist/server/sessions/schedulePrompt/scheduler.js.map +1 -1
  161. package/dist/server/sessions/schedulePrompt/tool.js +13 -1
  162. package/dist/server/sessions/schedulePrompt/tool.js.map +1 -1
  163. package/dist/server/sessions/sessionEventMapper.js +36 -2
  164. package/dist/server/sessions/sessionEventMapper.js.map +1 -1
  165. package/dist/server/sessions/sessionRoutes.js +3 -0
  166. package/dist/server/sessions/sessionRoutes.js.map +1 -1
  167. package/dist/server/workspaceExplorerRoutes.js +16 -9
  168. package/dist/server/workspaceExplorerRoutes.js.map +1 -1
  169. package/dist/server/workspaces/fileContentService.js +9 -4
  170. package/dist/server/workspaces/fileContentService.js.map +1 -1
  171. package/dist/server/workspaces/imagePreviewService.js +63 -13
  172. package/dist/server/workspaces/imagePreviewService.js.map +1 -1
  173. package/dist/shared/apiTypes.d.ts +18 -1
  174. package/dist/shared/federatedRoutes.js +2 -0
  175. package/dist/shared/federatedRoutes.js.map +1 -1
  176. package/docs/config.md +16 -1
  177. package/package.json +5 -1
  178. package/dist/client/assets/CodeViewer-Mp4aTsTY.js +0 -4
  179. package/dist/client/assets/index-Df4Ex27j.js +0 -4427
  180. package/dist/client/assets/vendor-editor-core-D9EJm1dE.js +0 -10
  181. package/dist/client/assets/vendor-editor-languages-N6dcxgRO.js +0 -26
  182. package/dist/client-react/assets/index-BQ6zLLfz.js +0 -51
  183. package/dist/client-react/assets/index-Befs78HG.css +0 -1
  184. package/dist/client-react/index.html +0 -16
  185. package/dist/docker/piWebDockerCommandPlan.js +0 -211
  186. package/dist/docker/piWebDockerCommandPlan.js.map +0 -1
  187. package/dist/pi-web-plugins/info/package.json +0 -9
  188. package/dist/pi-web-plugins/info/pi-web-plugin.js +0 -51
  189. package/dist/pi-web-plugins/schedule-prompts/package.json +0 -9
  190. package/dist/pi-web-plugins/schedule-prompts/pi-web-plugin.js +0 -43
  191. package/dist/pi-web-plugins/schedule-prompts/schedulePromptsPanelElement.js +0 -226
  192. package/dist/pi-web-plugins/schedule-prompts/types.js +0 -2
  193. package/dist/pi-web-plugins/updates/package.json +0 -9
  194. package/dist/pi-web-plugins/updates/pi-web-plugin.js +0 -173
  195. package/dist/pi-web-plugins/updates/updatesLogic.js +0 -97
  196. package/dist/piWebVersionReport.js +0 -225
  197. package/dist/piWebVersionReport.js.map +0 -1
  198. package/dist/server/diagnostics/nodePtySpawnHelper.js +0 -130
  199. package/dist/server/diagnostics/nodePtySpawnHelper.js.map +0 -1
  200. package/dist/server/piWebPluginService.js +0 -283
  201. package/dist/server/piWebPluginService.js.map +0 -1
  202. package/dist/server/piWebStatus.js +0 -602
  203. package/dist/server/piWebStatus.js.map +0 -1
  204. package/dist/server/piWebStatusCache.js +0 -32
  205. package/dist/server/piWebStatusCache.js.map +0 -1
  206. package/dist/server/workspaces/projectPiWebConfig.js +0 -61
  207. package/dist/server/workspaces/projectPiWebConfig.js.map +0 -1
  208. package/dist/shared/piWebStatusParsing.js +0 -102
  209. package/dist/shared/piWebStatusParsing.js.map +0 -1
@@ -0,0 +1,78 @@
1
+ // Generated from omp-web-plugins/mcps/mcpsLogic.ts. Do not edit directly.
2
+ export const PROJECT_MCP_FILES = [".mcp.json", "mcp.json", ".omp/mcp.json"];
3
+ export function isMcpConfigFile(value) {
4
+ return (typeof value === "object" &&
5
+ value !== null &&
6
+ "mcpServers" in value &&
7
+ typeof value.mcpServers === "object" &&
8
+ value.mcpServers !== null);
9
+ }
10
+ export function parseMcpConfigFile(raw) {
11
+ try {
12
+ const parsed = JSON.parse(raw);
13
+ if (isMcpConfigFile(parsed)) {
14
+ return parsed;
15
+ }
16
+ return { mcpServers: {} };
17
+ }
18
+ catch {
19
+ return { mcpServers: {} };
20
+ }
21
+ }
22
+ export function formatTransport(config) {
23
+ if (config.type !== undefined)
24
+ return config.type;
25
+ if (config.url !== undefined)
26
+ return "http";
27
+ return "stdio";
28
+ }
29
+ export function formatCommandOrUrl(config) {
30
+ if (config.url !== undefined && config.url !== "") {
31
+ return config.url;
32
+ }
33
+ const cmd = config.command ?? "";
34
+ const args = config.args?.join(" ") ?? "";
35
+ return [cmd, args].filter(Boolean).join(" ");
36
+ }
37
+ export function isServerEnabled(config) {
38
+ return config.enabled !== false;
39
+ }
40
+ export function countActiveServers(state) {
41
+ let count = 0;
42
+ for (const server of Object.values(state.projectServers)) {
43
+ if (isServerEnabled(server))
44
+ count++;
45
+ }
46
+ for (const server of Object.values(state.globalServers)) {
47
+ if (isServerEnabled(server))
48
+ count++;
49
+ }
50
+ return count;
51
+ }
52
+ export function countFailedServers(state) {
53
+ if (!state.checkResults)
54
+ return 0;
55
+ let count = 0;
56
+ for (const check of Object.values(state.checkResults)) {
57
+ if (check.status === "failed")
58
+ count++;
59
+ }
60
+ return count;
61
+ }
62
+ export function toggleServerInConfig(raw, serverName) {
63
+ try {
64
+ const parsed = JSON.parse(raw);
65
+ if (isMcpConfigFile(parsed) && parsed.mcpServers !== undefined) {
66
+ const servers = parsed.mcpServers;
67
+ const target = servers[serverName];
68
+ if (target !== undefined) {
69
+ target.enabled = !isServerEnabled(target);
70
+ return JSON.stringify(parsed, null, 2) + "\n";
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ // Return original on parse error
76
+ }
77
+ return raw;
78
+ }
@@ -0,0 +1,417 @@
1
+ // Generated from omp-web-plugins/mcps/mcpsPanelElement.ts. Do not edit directly.
2
+ import { countActiveServers, countFailedServers, formatCommandOrUrl, formatTransport, isServerEnabled, parseMcpConfigFile, PROJECT_MCP_FILES, toggleServerInConfig, } from "./mcpsLogic.js";
3
+ export const mcpsPanelTagName = "omp-web-mcps-panel";
4
+ const panelCache = new Map();
5
+ export function defineMcpsPanelElement() {
6
+ if (!customElements.get(mcpsPanelTagName)) {
7
+ customElements.define(mcpsPanelTagName, OmpWebMcpsPanel);
8
+ }
9
+ }
10
+ export function mcpsBadge(context) {
11
+ const cached = panelCache.get(cacheKeyForContext(context));
12
+ if (cached === undefined || "kind" in cached)
13
+ return undefined;
14
+ const count = countActiveServers(cached);
15
+ return count > 0 ? String(count) : undefined;
16
+ }
17
+ class OmpWebMcpsPanel extends HTMLElement {
18
+ contextValue;
19
+ root;
20
+ pollTimer;
21
+ currentKey;
22
+ isChecking = false;
23
+ constructor() {
24
+ super();
25
+ this.root = this.attachShadow({ mode: "open" });
26
+ }
27
+ set context(value) {
28
+ const prevKey = this.currentKey;
29
+ const newKey = value ? cacheKeyForContext(value) : undefined;
30
+ this.contextValue = value;
31
+ this.currentKey = newKey;
32
+ if (value && newKey !== prevKey) {
33
+ void this.loadServers(value);
34
+ }
35
+ else {
36
+ this.render();
37
+ }
38
+ }
39
+ connectedCallback() {
40
+ this.render();
41
+ this.pollTimer = setInterval(() => {
42
+ if (this.contextValue)
43
+ void this.loadServers(this.contextValue, false);
44
+ }, 60000);
45
+ }
46
+ disconnectedCallback() {
47
+ if (this.pollTimer !== undefined) {
48
+ clearInterval(this.pollTimer);
49
+ this.pollTimer = undefined;
50
+ }
51
+ }
52
+ async loadServers(context, forceCheck = false) {
53
+ const key = cacheKeyForContext(context);
54
+ if (this.isChecking)
55
+ return;
56
+ let projectFile;
57
+ let projectServers = {};
58
+ for (const fileName of PROJECT_MCP_FILES) {
59
+ try {
60
+ const response = await context.files.readFile(fileName);
61
+ const content = typeof response.content === "string"
62
+ ? response.content
63
+ : new TextDecoder().decode(response.content);
64
+ const parsed = parseMcpConfigFile(content);
65
+ if (parsed.mcpServers !== undefined) {
66
+ projectFile = fileName;
67
+ projectServers = parsed.mcpServers;
68
+ break;
69
+ }
70
+ }
71
+ catch {
72
+ // Continue checking next candidate file
73
+ }
74
+ }
75
+ let globalPath;
76
+ let globalServers = {};
77
+ try {
78
+ const response = await context.apiFetch("/mcps");
79
+ if (response.ok) {
80
+ const data = (await response.json());
81
+ if (data.servers) {
82
+ globalServers = data.servers;
83
+ globalPath = data.path;
84
+ }
85
+ }
86
+ }
87
+ catch {
88
+ // Ignore network errors fetching global servers
89
+ }
90
+ const cached = panelCache.get(key);
91
+ const prevCheckResults = cached && !("kind" in cached) ? cached.checkResults : undefined;
92
+ const shouldCheck = forceCheck || prevCheckResults === undefined;
93
+ const state = {
94
+ projectFile,
95
+ projectServers,
96
+ globalPath,
97
+ globalServers,
98
+ checkResults: prevCheckResults,
99
+ isChecking: shouldCheck,
100
+ lastLoaded: new Date().toISOString(),
101
+ };
102
+ panelCache.set(key, state);
103
+ this.render();
104
+ if (!shouldCheck)
105
+ return;
106
+ // Probe server connection status
107
+ const allServers = {};
108
+ for (const [name, config] of Object.entries(projectServers)) {
109
+ if (isServerEnabled(config))
110
+ allServers[name] = config;
111
+ }
112
+ for (const [name, config] of Object.entries(globalServers)) {
113
+ if (isServerEnabled(config))
114
+ allServers[name] = config;
115
+ }
116
+ if (Object.keys(allServers).length > 0) {
117
+ this.isChecking = true;
118
+ try {
119
+ const checkRes = await context.apiFetch("/mcps/check", {
120
+ method: "POST",
121
+ headers: { "content-type": "application/json" },
122
+ body: JSON.stringify({ servers: allServers }),
123
+ });
124
+ if (checkRes.ok) {
125
+ const checkData = (await checkRes.json());
126
+ state.checkResults = checkData.results;
127
+ }
128
+ }
129
+ catch {
130
+ // Ignore check probe errors
131
+ }
132
+ finally {
133
+ this.isChecking = false;
134
+ }
135
+ }
136
+ state.isChecking = false;
137
+ panelCache.set(key, state);
138
+ this.render();
139
+ }
140
+ render() {
141
+ const context = this.contextValue;
142
+ if (context === undefined) {
143
+ this.root.innerHTML = `${panelStyles()}<section class="empty">Select a workspace.</section>`;
144
+ return;
145
+ }
146
+ const key = cacheKeyForContext(context);
147
+ const cached = panelCache.get(key);
148
+ const state = cached ?? { kind: "loading" };
149
+ const isLoading = "kind" in state && state.kind === "loading";
150
+ let activeCount = 0;
151
+ let failedCount = 0;
152
+ if (!("kind" in state)) {
153
+ activeCount = countActiveServers(state);
154
+ failedCount = countFailedServers(state);
155
+ }
156
+ this.root.innerHTML = `
157
+ ${panelStyles()}
158
+ <section class="toolbar">
159
+ <div class="toolbar-title">
160
+ <strong>MCP Servers</strong>
161
+ ${!isLoading ? `
162
+ <span class="badge ${activeCount > 0 ? "active" : ""}">
163
+ ${activeCount} active
164
+ ${failedCount > 0 ? `<span class="badge-failed">· ${failedCount} failed</span>` : ""}
165
+ </span>
166
+ ` : ""}
167
+ </div>
168
+ <button class="secondary" data-refresh ${isLoading || this.isChecking ? "disabled" : ""}>Refresh</button>
169
+ </section>
170
+ <section class="viewer">
171
+ ${this.renderContent(state)}
172
+ </section>
173
+ `;
174
+ this.attachEventListeners(context, state);
175
+ }
176
+ renderContent(state) {
177
+ if ("kind" in state) {
178
+ if (state.kind === "loading") {
179
+ return `<p class="muted">Loading MCP configuration…</p>`;
180
+ }
181
+ return `<div class="status error">${escapeHtml(state.message)}</div>`;
182
+ }
183
+ const hasProjectServers = Object.keys(state.projectServers).length > 0;
184
+ let html = "";
185
+ // Project Section
186
+ html += `
187
+ <section class="server-group">
188
+ <div class="group-header">
189
+ <span class="group-title">Workspace Servers</span>
190
+ ${state.projectFile !== undefined ? `<span class="group-path">${escapeHtml(state.projectFile)}</span>` : ""}
191
+ </div>
192
+ `;
193
+ if (hasProjectServers) {
194
+ html += `<div class="servers-list">`;
195
+ for (const [name, config] of Object.entries(state.projectServers)) {
196
+ html += this.renderServerRow(name, config, "project", state);
197
+ }
198
+ html += `</div>`;
199
+ }
200
+ else if (state.projectFile !== undefined) {
201
+ html += `<p class="muted"><code>${escapeHtml(state.projectFile)}</code> exists but defines no servers.</p>`;
202
+ }
203
+ else {
204
+ html += `
205
+ <div class="no-file-box">
206
+ <p class="muted">No project MCP configuration found (.mcp.json).</p>
207
+ <button data-create-project-file class="secondary">+ Create .mcp.json</button>
208
+ </div>
209
+ `;
210
+ }
211
+ html += `</section>`;
212
+ // Global Section
213
+ const hasGlobalServers = Object.keys(state.globalServers).length > 0;
214
+ html += `
215
+ <section class="server-group">
216
+ <div class="group-header">
217
+ <span class="group-title">Global Servers</span>
218
+ ${state.globalPath !== undefined ? `<span class="group-path">${escapeHtml(state.globalPath)}</span>` : ""}
219
+ </div>
220
+ `;
221
+ if (hasGlobalServers) {
222
+ html += `<div class="servers-list">`;
223
+ for (const [name, config] of Object.entries(state.globalServers)) {
224
+ html += this.renderServerRow(name, config, "global", state);
225
+ }
226
+ html += `</div>`;
227
+ }
228
+ else {
229
+ html += `<p class="muted">No global MCP servers configured.</p>`;
230
+ }
231
+ html += `</section>`;
232
+ // Quick Command Shortcuts
233
+ html += `
234
+ <section class="shortcuts-group">
235
+ <span class="group-title">Oh My Pi Commands</span>
236
+ <div class="shortcut-buttons">
237
+ <button data-prompt="/mcp list" class="secondary">/mcp list</button>
238
+ <button data-prompt="/mcp reload" class="secondary">/mcp reload</button>
239
+ <button data-prompt="/mcp add " class="secondary">+ /mcp add</button>
240
+ </div>
241
+ </section>
242
+ `;
243
+ return html;
244
+ }
245
+ renderServerRow(name, config, scope, state) {
246
+ const enabled = isServerEnabled(config);
247
+ const transport = formatTransport(config);
248
+ const target = formatCommandOrUrl(config);
249
+ const check = state.checkResults?.[name];
250
+ const isChecking = state.isChecking === true && enabled && check === undefined;
251
+ let statusDotClass = enabled ? "active" : "disabled";
252
+ let statusText = enabled ? "Active" : "Disabled";
253
+ let statusTextClass = enabled ? "" : "disabled";
254
+ if (!enabled) {
255
+ statusDotClass = "disabled";
256
+ statusText = "Disabled";
257
+ statusTextClass = "disabled";
258
+ }
259
+ else if (isChecking) {
260
+ statusDotClass = "checking";
261
+ statusText = "Connecting…";
262
+ statusTextClass = "checking";
263
+ }
264
+ else if (check?.status === "connected") {
265
+ statusDotClass = "connected";
266
+ statusText = check.serverInfo?.version ? `Connected (v${check.serverInfo.version})` : "Connected";
267
+ statusTextClass = "connected";
268
+ }
269
+ else if (check?.status === "failed") {
270
+ statusDotClass = "failed";
271
+ statusText = "Connection failed";
272
+ statusTextClass = "failed";
273
+ }
274
+ return `
275
+ <article class="server-row ${enabled ? "enabled" : "disabled"} ${check?.status === "failed" ? "server-failed" : ""}">
276
+ <div class="server-main">
277
+ <div class="server-header">
278
+ <span class="status-dot ${statusDotClass}"></span>
279
+ <strong class="server-name">${escapeHtml(name)}</strong>
280
+ <span class="tag transport">${escapeHtml(transport)}</span>
281
+ <span class="status-text ${statusTextClass}">${escapeHtml(statusText)}</span>
282
+ </div>
283
+ ${target !== "" ? `<div class="server-target"><code>${escapeHtml(target)}</code></div>` : ""}
284
+ ${config.description !== undefined ? `<div class="server-desc">${escapeHtml(config.description)}</div>` : ""}
285
+ ${check?.status === "failed" && check.error ? `
286
+ <div class="server-error-banner">
287
+ <span class="error-icon">⚠️</span>
288
+ <span class="error-msg">${escapeHtml(check.error)}</span>
289
+ </div>
290
+ ` : ""}
291
+ </div>
292
+ <div class="server-actions">
293
+ ${scope === "project" ? `
294
+ <button
295
+ data-toggle-server="${escapeAttr(name)}"
296
+ class="${enabled ? "secondary" : "accent"}"
297
+ >
298
+ ${enabled ? "Disable" : "Enable"}
299
+ </button>
300
+ ` : `
301
+ <span class="badge-readonly">Global</span>
302
+ `}
303
+ </div>
304
+ </article>
305
+ `;
306
+ }
307
+ attachEventListeners(context, state) {
308
+ this.root.querySelector("button[data-refresh]")?.addEventListener("click", () => {
309
+ void this.loadServers(context, true);
310
+ });
311
+ this.root.querySelector("button[data-create-project-file]")?.addEventListener("click", async () => {
312
+ const initial = {
313
+ $schema: "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
314
+ mcpServers: {},
315
+ };
316
+ await context.files.writeFile(".mcp.json", JSON.stringify(initial, null, 2) + "\n");
317
+ void this.loadServers(context, true);
318
+ });
319
+ this.root.querySelectorAll("button[data-toggle-server]").forEach((btn) => {
320
+ btn.addEventListener("click", async () => {
321
+ const serverName = btn.getAttribute("data-toggle-server");
322
+ if (!serverName || "kind" in state || !state.projectFile)
323
+ return;
324
+ try {
325
+ const res = await context.files.readFile(state.projectFile);
326
+ const currentContent = typeof res.content === "string"
327
+ ? res.content
328
+ : new TextDecoder().decode(res.content);
329
+ const updatedContent = toggleServerInConfig(currentContent, serverName);
330
+ await context.files.writeFile(state.projectFile, updatedContent);
331
+ void this.loadServers(context, true);
332
+ }
333
+ catch (err) {
334
+ console.error("Failed to toggle MCP server in configuration:", err);
335
+ }
336
+ });
337
+ });
338
+ this.root.querySelectorAll("button[data-prompt]").forEach((btn) => {
339
+ btn.addEventListener("click", () => {
340
+ const promptText = btn.getAttribute("data-prompt");
341
+ if (promptText) {
342
+ context.prompt.insertText(promptText);
343
+ }
344
+ });
345
+ });
346
+ }
347
+ }
348
+ function cacheKeyForContext(context) {
349
+ return `${context.machine.id}:${context.workspace.projectId}:${context.workspace.id}`;
350
+ }
351
+ function escapeHtml(value) {
352
+ return String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
353
+ }
354
+ function escapeAttr(value) {
355
+ return escapeHtml(value).replaceAll('"', "&quot;");
356
+ }
357
+ function panelStyles() {
358
+ return `
359
+ <style>
360
+ :host { display: contents; }
361
+ .toolbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--pi-border-muted); }
362
+ .toolbar-title { display: flex; align-items: center; gap: 8px; }
363
+ .viewer { box-sizing: border-box; min-height: 0; overflow: auto; padding: 12px; display: flex; flex-direction: column; gap: 16px; }
364
+ .server-group { display: flex; flex-direction: column; gap: 8px; }
365
+ .group-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
366
+ .group-title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--pi-muted); }
367
+ .group-path { font-size: 11px; color: var(--pi-muted); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
368
+ .servers-list { display: grid; gap: 8px; }
369
+ .server-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-surface); padding: 10px 12px; }
370
+ .server-row.disabled { opacity: 0.65; }
371
+ .server-row.server-failed { border-color: rgba(239, 68, 68, 0.4); background: rgba(239, 68, 68, 0.04); }
372
+ .server-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
373
+ .server-header { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
374
+ .server-name { font-size: 13px; color: var(--pi-text); }
375
+ .status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--pi-muted); flex-shrink: 0; }
376
+ .status-dot.active { background: #22c55e; }
377
+ .status-dot.connected { background: #22c55e; }
378
+ .status-dot.checking { background: #eab308; animation: pulse 1.5s infinite; }
379
+ .status-dot.failed { background: #ef4444; }
380
+ .status-dot.disabled { background: var(--pi-muted); opacity: 0.5; }
381
+ .status-text { font-size: 11px; color: var(--pi-muted); }
382
+ .status-text.connected { color: #22c55e; }
383
+ .status-text.checking { color: #eab308; }
384
+ .status-text.failed { color: #ef4444; font-weight: 500; }
385
+ .status-text.disabled { color: var(--pi-muted); opacity: 0.7; }
386
+ .tag { font-size: 10px; padding: 1px 5px; border-radius: 4px; border: 1px solid var(--pi-border-muted); color: var(--pi-muted); font-family: ui-monospace, monospace; }
387
+ .tag.transport { color: var(--pi-accent); border-color: var(--pi-accent-border); }
388
+ .server-target { font-size: 11px; color: var(--pi-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
389
+ .server-target code { border: none; background: transparent; padding: 0; color: inherit; }
390
+ .server-desc { font-size: 11px; color: var(--pi-muted); }
391
+ .server-error-banner { display: flex; align-items: flex-start; gap: 6px; margin-top: 6px; padding: 6px 10px; border-radius: 6px; background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.25); font-size: 11px; color: #f87171; line-height: 1.4; word-break: break-word; }
392
+ .error-icon { flex-shrink: 0; font-size: 12px; line-height: 1; }
393
+ .error-msg { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
394
+ .server-actions { display: flex; align-items: center; gap: 6px; }
395
+ .badge { font-size: 11px; padding: 2px 7px; border-radius: 10px; background: var(--pi-surface); border: 1px solid var(--pi-border); color: var(--pi-muted); }
396
+ .badge.active { border-color: #22c55e44; color: #22c55e; }
397
+ .badge-failed { color: #ef4444; font-weight: 600; }
398
+ .badge-readonly { font-size: 11px; padding: 2px 7px; border-radius: 6px; background: var(--pi-border-muted); color: var(--pi-muted); }
399
+ .no-file-box { display: flex; flex-direction: column; gap: 8px; padding: 12px; border: 1px dashed var(--pi-border); border-radius: 8px; background: var(--pi-surface); align-items: flex-start; }
400
+ .shortcuts-group { display: flex; flex-direction: column; gap: 8px; padding-top: 8px; border-top: 1px solid var(--pi-border-muted); }
401
+ .shortcut-buttons { display: flex; flex-wrap: wrap; gap: 6px; }
402
+ button { border: 1px solid var(--pi-accent-border); border-radius: 6px; background: var(--pi-accent); color: var(--pi-bg); cursor: pointer; padding: 5px 9px; font: inherit; font-size: 12px; }
403
+ button.secondary { border-color: var(--pi-border); background: var(--pi-surface); color: var(--pi-text); }
404
+ button.accent { border-color: var(--pi-accent-border); background: var(--pi-accent); color: var(--pi-bg); }
405
+ button:disabled { cursor: wait; opacity: 0.65; }
406
+ .muted { color: var(--pi-muted); font-size: 12px; margin: 0; }
407
+ .status { border: 1px solid var(--pi-border); border-radius: 8px; padding: 10px; color: var(--pi-muted); font-size: 12px; }
408
+ .status.error { border-color: var(--pi-danger); color: var(--pi-danger); }
409
+ .empty { padding: 16px; color: var(--pi-muted); font-size: 12px; }
410
+ code { border: 1px solid var(--pi-border-muted); border-radius: 4px; background: var(--pi-bg); color: var(--pi-text-secondary); font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; padding: 1px 4px; }
411
+ @keyframes pulse {
412
+ 0%, 100% { opacity: 1; transform: scale(1); }
413
+ 50% { opacity: 0.4; transform: scale(0.85); }
414
+ }
415
+ </style>
416
+ `;
417
+ }
@@ -0,0 +1,45 @@
1
+ // Generated from omp-web-plugins/mcps/omp-web-plugin.ts. Do not edit directly.
2
+ import { defineMcpsPanelElement, mcpsBadge } from "./mcpsPanelElement.js";
3
+ const plugin = {
4
+ apiVersion: 1,
5
+ name: "MCP Servers",
6
+ activate: ({ pluginId, html, svg }) => {
7
+ defineMcpsPanelElement();
8
+ return {
9
+ contributions: {
10
+ actions: [
11
+ {
12
+ id: "workspace.open-mcps",
13
+ title: "Open MCP Servers",
14
+ description: "Open the MCP Servers panel.",
15
+ group: "Workspace",
16
+ enabled: (context) => context.state.selectedWorkspace !== undefined,
17
+ run: (context) => {
18
+ if (context.state.selectedWorkspace === undefined)
19
+ return;
20
+ context.selectWorkspaceTool(`${pluginId}:workspace.mcps`);
21
+ },
22
+ },
23
+ ],
24
+ workspacePanels: [
25
+ {
26
+ id: "workspace.mcps",
27
+ title: "MCPs",
28
+ icon: svg `
29
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
30
+ <rect x="2" y="2" width="20" height="8" rx="2" ry="2"></rect>
31
+ <rect x="2" y="14" width="20" height="8" rx="2" ry="2"></rect>
32
+ <line x1="6" y1="6" x2="6.01" y2="6"></line>
33
+ <line x1="6" y1="18" x2="6.01" y2="18"></line>
34
+ </svg>
35
+ `,
36
+ order: 55,
37
+ badge: (context) => mcpsBadge(context),
38
+ render: (context) => html `<omp-web-mcps-panel .context=${context}></omp-web-mcps-panel>`,
39
+ },
40
+ ],
41
+ },
42
+ };
43
+ },
44
+ };
45
+ export default plugin;
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@omp-web/mcps-plugin",
3
+ "private": true,
4
+ "ompWeb": {
5
+ "plugins": [
6
+ { "id": "mcps", "module": "omp-web-plugin.js" }
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,2 @@
1
+ // Generated from omp-web-plugins/mcps/types.ts. Do not edit directly.
2
+ export {};
@@ -120,6 +120,7 @@ export interface WorkspaceContext {
120
120
  state?: PluginRuntimeState;
121
121
  files: WorkspaceFiles;
122
122
  host: WorkspaceHost;
123
+ apiFetch(path: string, init?: RequestInit): Promise<Response>;
123
124
  }
124
125
  export interface WorkspaceTerminalCommandInput {
125
126
  title: string;
@@ -2,6 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { Hono } from "hono";
5
+ import { createSecurityMiddleware } from "./security.js";
5
6
  import { createBunWebSocket } from "hono/bun";
6
7
  import { serveStatic } from "hono/bun";
7
8
  import { ProjectStore } from "./storage/projectStore.js";
@@ -30,6 +31,7 @@ import { registerMachineProxyRoutes } from "./machines/machineProxyRoutes.js";
30
31
  import { proxyMachinePluginAsset, registerMachinePluginProxyRoutes } from "./machines/machinePluginProxyRoutes.js";
31
32
  import { PushNotificationService } from "./push/PushNotificationService.js";
32
33
  import { registerPushRoutes } from "./push/pushRoutes.js";
34
+ import { registerMcpRoutes } from "./mcpRoutes.js";
33
35
  function registerLocalProjectRoutes(app, projects, workspaces, prefix, options = {}) {
34
36
  app.get(`${prefix}/projects`, async (c) => c.json(await projects.list()));
35
37
  app.post(`${prefix}/projects`, async (c) => {
@@ -105,12 +107,31 @@ function registerLocalFileSuggestionRoutes(app, projects, workspaces, prefix, op
105
107
  }
106
108
  export async function buildApp(deps = {}) {
107
109
  const app = new Hono();
110
+ const configService = deps.config ?? createFileOmpWebConfigService();
111
+ const effectiveConfig = (await configService.read()).effectiveConfig;
112
+ let cachedAllowedHosts = effectiveConfig.allowedHosts;
113
+ let lastCheck = 0;
114
+ const getAllowedHosts = async () => {
115
+ const now = Date.now();
116
+ if (now - lastCheck < 1000)
117
+ return cachedAllowedHosts;
118
+ lastCheck = now;
119
+ try {
120
+ cachedAllowedHosts = (await configService.read()).effectiveConfig.allowedHosts;
121
+ }
122
+ catch {
123
+ // keep cached
124
+ }
125
+ return cachedAllowedHosts;
126
+ };
127
+ app.use("*", createSecurityMiddleware({
128
+ allowedHosts: getAllowedHosts,
129
+ }));
108
130
  const { upgradeWebSocket, websocket } = createBunWebSocket();
109
131
  const projects = deps.projects ?? new ProjectService(new ProjectStore());
110
132
  const workspaces = deps.workspaces ?? new WorkspaceService();
111
133
  const ompWebPlugins = deps.ompWebPlugins ?? new OmpWebPluginService();
112
134
  const piPackages = deps.piPackages ?? createDefaultPiPackageService();
113
- const configService = deps.config ?? createFileOmpWebConfigService();
114
135
  const sessionDaemon = deps.sessionDaemon ?? new SessionDaemonClient();
115
136
  const ompWebStatusCache = createOmpWebStatusCache(() => getOmpWebStatus(sessionDaemon), {
116
137
  onError: (error) => { console.warn("failed to refresh PI WEB status cache", error); },
@@ -157,6 +178,8 @@ export async function buildApp(deps = {}) {
157
178
  registerWorkspaceDeletionRoutes(app, projects, workspaces, sessionDaemon, "/api/machines/local");
158
179
  registerLocalFileSuggestionRoutes(app, projects, workspaces, "/api", { config: configService });
159
180
  registerLocalFileSuggestionRoutes(app, projects, workspaces, "/api/machines/local", { config: configService });
181
+ registerMcpRoutes(app, "/api");
182
+ registerMcpRoutes(app, "/api/machines/local");
160
183
  registerMachineProxyRoutes(app, machines, upgradeWebSocket);
161
184
  const pushService = new PushNotificationService((msg) => { console.warn("[push]", msg); });
162
185
  registerPushRoutes(app, pushService);