@superblocksteam/gateway 2.0.161 → 2.0.162-next.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 (235) hide show
  1. package/README.md +135 -39
  2. package/dist/agents/resolve-orchestrator-url.d.ts +18 -0
  3. package/dist/agents/resolve-orchestrator-url.js +64 -1
  4. package/dist/agents/resolve-orchestrator-url.js.map +1 -1
  5. package/dist/capabilities/lifecycle.d.ts +46 -29
  6. package/dist/capabilities/lifecycle.js +1731 -843
  7. package/dist/capabilities/lifecycle.js.map +1 -1
  8. package/dist/capabilities/query-integration.d.ts +14 -0
  9. package/dist/capabilities/query-integration.js +172 -0
  10. package/dist/capabilities/query-integration.js.map +1 -0
  11. package/dist/capabilities/source-files-archive.d.ts +11 -0
  12. package/dist/capabilities/source-files-archive.js +54 -0
  13. package/dist/capabilities/source-files-archive.js.map +1 -0
  14. package/dist/capabilities/types.d.ts +197 -119
  15. package/dist/capabilities/types.js +45 -6
  16. package/dist/capabilities/types.js.map +1 -1
  17. package/dist/capture/browser-contract.d.ts +15 -5
  18. package/dist/capture/browser-contract.js +13 -3
  19. package/dist/capture/browser-contract.js.map +1 -1
  20. package/dist/capture/browser-instructions.d.ts +3 -2
  21. package/dist/capture/browser-instructions.js +3 -2
  22. package/dist/capture/browser-instructions.js.map +1 -1
  23. package/dist/capture/mode.d.ts +8 -1
  24. package/dist/capture/mode.js +22 -2
  25. package/dist/capture/mode.js.map +1 -1
  26. package/dist/config.d.ts +22 -23
  27. package/dist/config.js +13 -12
  28. package/dist/config.js.map +1 -1
  29. package/dist/deps.d.ts +7 -7
  30. package/dist/index.d.ts +2 -4
  31. package/dist/index.js +2 -4
  32. package/dist/index.js.map +1 -1
  33. package/dist/integrations/read-only-postgres-query.d.ts +2 -0
  34. package/dist/integrations/read-only-postgres-query.js +164 -0
  35. package/dist/integrations/read-only-postgres-query.js.map +1 -0
  36. package/dist/main.js +1 -1
  37. package/dist/main.js.map +1 -1
  38. package/dist/playwright/ensure-chromium.d.ts +5 -7
  39. package/dist/playwright/ensure-chromium.js +13 -13
  40. package/dist/playwright/ensure-chromium.js.map +1 -1
  41. package/dist/preview/capture-screenshot.d.ts +30 -4
  42. package/dist/preview/capture-screenshot.js +67 -17
  43. package/dist/preview/capture-screenshot.js.map +1 -1
  44. package/dist/preview/viewer-url.js +3 -1
  45. package/dist/preview/viewer-url.js.map +1 -1
  46. package/dist/process/fault-barrier.js +1 -1
  47. package/dist/process/fault-barrier.js.map +1 -1
  48. package/dist/sabs/agent-facing-text.d.ts +2 -0
  49. package/dist/sabs/agent-facing-text.js +56 -2
  50. package/dist/sabs/agent-facing-text.js.map +1 -1
  51. package/dist/sabs/app-state.d.ts +126 -0
  52. package/dist/sabs/app-state.js +332 -0
  53. package/dist/sabs/app-state.js.map +1 -0
  54. package/dist/sabs/awaited-decision.d.ts +49 -0
  55. package/dist/sabs/awaited-decision.js +129 -0
  56. package/dist/sabs/awaited-decision.js.map +1 -0
  57. package/dist/sabs/editor-client-methods.d.ts +23 -5
  58. package/dist/sabs/editor-client-methods.js +45 -4
  59. package/dist/sabs/editor-client-methods.js.map +1 -1
  60. package/dist/sabs/editor-socket.d.ts +85 -0
  61. package/dist/sabs/editor-socket.js +67 -0
  62. package/dist/sabs/editor-socket.js.map +1 -0
  63. package/dist/sabs/session-peer.d.ts +119 -82
  64. package/dist/sabs/session-peer.js +10 -1
  65. package/dist/sabs/session-peer.js.map +1 -1
  66. package/dist/sabs/streamed-reply.d.ts +45 -0
  67. package/dist/sabs/streamed-reply.js +125 -0
  68. package/dist/sabs/streamed-reply.js.map +1 -0
  69. package/dist/sabs/turn-collector.d.ts +50 -35
  70. package/dist/sabs/turn-collector.js +110 -176
  71. package/dist/sabs/turn-collector.js.map +1 -1
  72. package/dist/sabs/websocket-session-peer.d.ts +104 -69
  73. package/dist/sabs/websocket-session-peer.js +1333 -477
  74. package/dist/sabs/websocket-session-peer.js.map +1 -1
  75. package/dist/server/client.d.ts +41 -9
  76. package/dist/server/client.js +126 -21
  77. package/dist/server/client.js.map +1 -1
  78. package/dist/start.d.ts +1 -1
  79. package/dist/start.js +30 -8
  80. package/dist/start.js.map +1 -1
  81. package/dist/stores/memory.d.ts +46 -0
  82. package/dist/stores/memory.js +163 -0
  83. package/dist/stores/memory.js.map +1 -0
  84. package/dist/stores/types.d.ts +92 -0
  85. package/dist/stores/types.js +11 -0
  86. package/dist/stores/types.js.map +1 -0
  87. package/dist/telemetry/mcp-client.d.ts +6 -1
  88. package/dist/telemetry/mcp-client.js +100 -4
  89. package/dist/telemetry/mcp-client.js.map +1 -1
  90. package/dist/telemetry/metrics.d.ts +12 -3
  91. package/dist/telemetry/metrics.js +60 -14
  92. package/dist/telemetry/metrics.js.map +1 -1
  93. package/dist/telemetry/runtime.js +4 -6
  94. package/dist/telemetry/runtime.js.map +1 -1
  95. package/dist/transports/mcp/admin-tools.d.ts +8 -0
  96. package/dist/transports/mcp/admin-tools.js +115 -4
  97. package/dist/transports/mcp/admin-tools.js.map +1 -1
  98. package/dist/transports/mcp/app-status-html.d.ts +4 -8
  99. package/dist/transports/mcp/app-status-html.js +336 -128
  100. package/dist/transports/mcp/app-status-html.js.map +1 -1
  101. package/dist/transports/mcp/client-presentation.d.ts +16 -0
  102. package/dist/transports/mcp/client-presentation.js +13 -0
  103. package/dist/transports/mcp/client-presentation.js.map +1 -0
  104. package/dist/transports/mcp/cowork-editor-url.d.ts +6 -0
  105. package/dist/transports/mcp/cowork-editor-url.js +10 -0
  106. package/dist/transports/mcp/cowork-editor-url.js.map +1 -0
  107. package/dist/transports/mcp/decision-card-html.d.ts +26 -0
  108. package/dist/transports/mcp/decision-card-html.js +876 -0
  109. package/dist/transports/mcp/decision-card-html.js.map +1 -0
  110. package/dist/transports/mcp/decision-elicitation.d.ts +42 -0
  111. package/dist/transports/mcp/decision-elicitation.js +59 -0
  112. package/dist/transports/mcp/decision-elicitation.js.map +1 -1
  113. package/dist/transports/mcp/editor-document-probe.d.ts +4 -0
  114. package/dist/transports/mcp/editor-document-probe.js +35 -0
  115. package/dist/transports/mcp/editor-document-probe.js.map +1 -0
  116. package/dist/transports/mcp/editor-integration-setup-url.d.ts +14 -0
  117. package/dist/transports/mcp/editor-integration-setup-url.js +29 -0
  118. package/dist/transports/mcp/editor-integration-setup-url.js.map +1 -0
  119. package/dist/transports/mcp/format-tool-content.d.ts +13 -18
  120. package/dist/transports/mcp/format-tool-content.js +172 -13
  121. package/dist/transports/mcp/format-tool-content.js.map +1 -1
  122. package/dist/transports/mcp/instructions/index.d.ts +23 -0
  123. package/dist/transports/mcp/instructions/index.js +81 -0
  124. package/dist/transports/mcp/instructions/index.js.map +1 -0
  125. package/dist/transports/mcp/instructions/result.d.ts +18 -0
  126. package/dist/transports/mcp/instructions/result.js +58 -0
  127. package/dist/transports/mcp/instructions/result.js.map +1 -0
  128. package/dist/transports/mcp/instructions/tools/ask-user.d.ts +2 -0
  129. package/dist/transports/mcp/instructions/tools/ask-user.js +20 -0
  130. package/dist/transports/mcp/instructions/tools/ask-user.js.map +1 -0
  131. package/dist/transports/mcp/instructions/tools/build-app.d.ts +4 -0
  132. package/dist/transports/mcp/instructions/tools/build-app.js +54 -0
  133. package/dist/transports/mcp/instructions/tools/build-app.js.map +1 -0
  134. package/dist/transports/mcp/instructions/tools/check-app-progress.d.ts +3 -0
  135. package/dist/transports/mcp/instructions/tools/check-app-progress.js +79 -0
  136. package/dist/transports/mcp/instructions/tools/check-app-progress.js.map +1 -0
  137. package/dist/transports/mcp/instructions/tools/check-publish-progress.d.ts +3 -0
  138. package/dist/transports/mcp/instructions/tools/check-publish-progress.js +23 -0
  139. package/dist/transports/mcp/instructions/tools/check-publish-progress.js.map +1 -0
  140. package/dist/transports/mcp/instructions/tools/copy.d.ts +18 -0
  141. package/dist/transports/mcp/instructions/tools/copy.js +49 -0
  142. package/dist/transports/mcp/instructions/tools/copy.js.map +1 -0
  143. package/dist/transports/mcp/instructions/tools/create-integration.d.ts +4 -0
  144. package/dist/transports/mcp/instructions/tools/create-integration.js +41 -0
  145. package/dist/transports/mcp/instructions/tools/create-integration.js.map +1 -0
  146. package/dist/transports/mcp/instructions/tools/edit-app.d.ts +3 -0
  147. package/dist/transports/mcp/instructions/tools/edit-app.js +18 -0
  148. package/dist/transports/mcp/instructions/tools/edit-app.js.map +1 -0
  149. package/dist/transports/mcp/instructions/tools/get-app.d.ts +3 -0
  150. package/dist/transports/mcp/instructions/tools/get-app.js +49 -0
  151. package/dist/transports/mcp/instructions/tools/get-app.js.map +1 -0
  152. package/dist/transports/mcp/instructions/tools/get-integration-metadata.d.ts +2 -0
  153. package/dist/transports/mcp/instructions/tools/get-integration-metadata.js +13 -0
  154. package/dist/transports/mcp/instructions/tools/get-integration-metadata.js.map +1 -0
  155. package/dist/transports/mcp/instructions/tools/index.d.ts +7 -0
  156. package/dist/transports/mcp/instructions/tools/index.js +31 -0
  157. package/dist/transports/mcp/instructions/tools/index.js.map +1 -0
  158. package/dist/transports/mcp/instructions/tools/publish-app.d.ts +3 -0
  159. package/dist/transports/mcp/instructions/tools/publish-app.js +23 -0
  160. package/dist/transports/mcp/instructions/tools/publish-app.js.map +1 -0
  161. package/dist/transports/mcp/instructions/tools/start-app.d.ts +3 -0
  162. package/dist/transports/mcp/instructions/tools/start-app.js +22 -0
  163. package/dist/transports/mcp/instructions/tools/start-app.js.map +1 -0
  164. package/dist/transports/mcp/instructions/tools/upload-artifact.d.ts +2 -0
  165. package/dist/transports/mcp/instructions/tools/upload-artifact.js +13 -0
  166. package/dist/transports/mcp/instructions/tools/upload-artifact.js.map +1 -0
  167. package/dist/transports/mcp/mcp-app-brand-css.d.ts +1 -0
  168. package/dist/transports/mcp/mcp-app-brand-css.js +180 -0
  169. package/dist/transports/mcp/mcp-app-brand-css.js.map +1 -0
  170. package/dist/transports/mcp/mount.d.ts +14 -1
  171. package/dist/transports/mcp/mount.js +729 -172
  172. package/dist/transports/mcp/mount.js.map +1 -1
  173. package/dist/transports/mcp/native-browser-presence.d.ts +45 -0
  174. package/dist/transports/mcp/native-browser-presence.js +158 -0
  175. package/dist/transports/mcp/native-browser-presence.js.map +1 -0
  176. package/dist/transports/mcp/plan-approval.d.ts +44 -0
  177. package/dist/transports/mcp/plan-approval.js +179 -0
  178. package/dist/transports/mcp/plan-approval.js.map +1 -0
  179. package/dist/transports/mcp/session-directory.d.ts +7 -0
  180. package/dist/transports/mcp/session-directory.js +18 -0
  181. package/dist/transports/mcp/session-directory.js.map +1 -0
  182. package/dist/transports/mcp/tool-names.d.ts +2 -0
  183. package/dist/transports/mcp/tool-names.js +2 -0
  184. package/dist/transports/mcp/tool-names.js.map +1 -0
  185. package/package.json +16 -7
  186. package/skills/superblocks-build/SKILL.md +59 -0
  187. package/skills/superblocks-import/SKILL.md +125 -0
  188. package/dist/capabilities/import-prompt.d.ts +0 -11
  189. package/dist/capabilities/import-prompt.js +0 -96
  190. package/dist/capabilities/import-prompt.js.map +0 -1
  191. package/dist/capabilities/persisted-progress.d.ts +0 -46
  192. package/dist/capabilities/persisted-progress.js +0 -246
  193. package/dist/capabilities/persisted-progress.js.map +0 -1
  194. package/dist/events/cursor.d.ts +0 -43
  195. package/dist/events/cursor.js +0 -78
  196. package/dist/events/cursor.js.map +0 -1
  197. package/dist/events/memory-event-store.d.ts +0 -34
  198. package/dist/events/memory-event-store.js +0 -110
  199. package/dist/events/memory-event-store.js.map +0 -1
  200. package/dist/events/merge.d.ts +0 -23
  201. package/dist/events/merge.js +0 -97
  202. package/dist/events/merge.js.map +0 -1
  203. package/dist/events/normalized-collector.d.ts +0 -62
  204. package/dist/events/normalized-collector.js +0 -156
  205. package/dist/events/normalized-collector.js.map +0 -1
  206. package/dist/events/schema.d.ts +0 -9
  207. package/dist/events/schema.js +0 -93
  208. package/dist/events/schema.js.map +0 -1
  209. package/dist/events/snapshot.d.ts +0 -32
  210. package/dist/events/snapshot.js +0 -57
  211. package/dist/events/snapshot.js.map +0 -1
  212. package/dist/events/stream-key.d.ts +0 -2
  213. package/dist/events/stream-key.js +0 -31
  214. package/dist/events/stream-key.js.map +0 -1
  215. package/dist/events/types.d.ts +0 -179
  216. package/dist/events/types.js +0 -66
  217. package/dist/events/types.js.map +0 -1
  218. package/dist/resume/memory-progress-store.d.ts +0 -39
  219. package/dist/resume/memory-progress-store.js +0 -82
  220. package/dist/resume/memory-progress-store.js.map +0 -1
  221. package/dist/resume/memory-recent-app-store.d.ts +0 -14
  222. package/dist/resume/memory-recent-app-store.js +0 -27
  223. package/dist/resume/memory-recent-app-store.js.map +0 -1
  224. package/dist/resume/memory-turn-store.d.ts +0 -18
  225. package/dist/resume/memory-turn-store.js +0 -73
  226. package/dist/resume/memory-turn-store.js.map +0 -1
  227. package/dist/resume/progress-key.d.ts +0 -21
  228. package/dist/resume/progress-key.js +0 -58
  229. package/dist/resume/progress-key.js.map +0 -1
  230. package/dist/resume/stores.d.ts +0 -14
  231. package/dist/resume/stores.js +0 -18
  232. package/dist/resume/stores.js.map +0 -1
  233. package/dist/resume/types.d.ts +0 -124
  234. package/dist/resume/types.js +0 -13
  235. package/dist/resume/types.js.map +0 -1
@@ -1,17 +1,14 @@
1
+ import { MCP_APP_BRAND_CSS } from "./mcp-app-brand-css.js";
1
2
  /**
2
- * Self-contained MCP App HTML for Gateway status / open-link buttons.
3
+ * Self-contained MCP App HTML for Gateway status and open-link buttons.
3
4
  *
4
- * Nested iframes of Superblocks (preview / editor / live) were removed: Claude's
5
- * widget origin cannot hold a Superblocks Auth0 session, and Private Network
6
- * Access blocks localhost from a public parent. Display-mode controls (pip /
7
- * fullscreen / inline) were also removed — without an iframe they only resized
8
- * the status card. openLink buttons keep URLs in JS memory only (not printed).
5
+ * openLink buttons keep URLs in JS memory only (not printed).
9
6
  *
10
7
  * Speaks the MCP Apps postMessage dialect without bundling ext-apps into the
11
8
  * card (hosts cache UI resources; keep this file small and inline). Bump the
12
9
  * resource URI when the HTML changes so Claude Desktop refreshes its cache.
13
10
  */
14
- export const APP_STATUS_RESOURCE_URI = "ui://gateway/app-status-v8.html";
11
+ export const APP_STATUS_RESOURCE_URI = "ui://gateway/app-status-v37.html";
15
12
  export const APP_STATUS_HTML = `<!DOCTYPE html>
16
13
  <html lang="en">
17
14
  <head>
@@ -19,29 +16,9 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
19
16
  <meta name="viewport" content="width=device-width, initial-scale=1" />
20
17
  <title>Superblocks app</title>
21
18
  <style>
22
- :root {
23
- color-scheme: light dark;
24
- }
25
- body {
26
- margin: 0;
27
- font-family: var(--font-sans, system-ui, -apple-system, sans-serif);
28
- font-size: var(--font-text-sm-size, 13px);
29
- line-height: var(--font-text-sm-line-height, 1.45);
30
- color: var(--color-text-primary, #0f172a);
31
- background: transparent;
32
- }
33
- .card {
34
- display: flex;
35
- flex-direction: column;
36
- gap: 10px;
37
- padding: 14px;
38
- border: var(--border-width-regular, 1px) solid
39
- var(--color-border-primary, #e2e8f0);
40
- border-radius: var(--border-radius-lg, 12px);
41
- background: var(--color-background-primary, #ffffff);
42
- }
43
19
  .head {
44
20
  display: flex;
21
+ min-width: 0;
45
22
  align-items: center;
46
23
  gap: 8px;
47
24
  }
@@ -53,29 +30,46 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
53
30
  }
54
31
  .status {
55
32
  padding: 2px 8px;
33
+ border: var(--border-width-regular, 1px) solid var(--sb-app-border);
56
34
  border-radius: var(--border-radius-full, 999px);
57
- background: var(--color-background-secondary, #f1f5f9);
58
- color: var(--color-text-secondary, #64748b);
35
+ background: var(--sb-app-background-subtle);
36
+ color: var(--sb-app-text-secondary);
59
37
  font-size: var(--font-text-xs-size, 12px);
60
38
  text-transform: lowercase;
61
39
  }
40
+ .status[data-tone="info"] {
41
+ border-color: var(--sb-app-info-border);
42
+ background: var(--sb-app-info-background);
43
+ color: var(--sb-app-info-text);
44
+ }
45
+ .status[data-tone="success"] {
46
+ border-color: var(--sb-app-success-border);
47
+ background: var(--sb-app-success-background);
48
+ color: var(--sb-app-success-text);
49
+ }
50
+ .status[data-tone="warning"] {
51
+ border-color: var(--sb-app-warning-border);
52
+ background: var(--sb-app-warning-background);
53
+ color: var(--sb-app-warning-text);
54
+ }
55
+ .status[data-tone="danger"] {
56
+ border-color: var(--sb-app-danger-border);
57
+ background: var(--sb-app-danger-background);
58
+ color: var(--sb-app-danger-text);
59
+ }
62
60
  .meta {
63
- color: var(--color-text-tertiary, #94a3b8);
61
+ color: var(--sb-app-text-tertiary);
64
62
  font-size: var(--font-text-xs-size, 12px);
65
63
  }
66
64
  .note {
67
- display: none;
68
- color: var(--color-text-warning, #b45309);
65
+ color: var(--sb-app-warning-text);
69
66
  font-size: var(--font-text-xs-size, 12px);
70
67
  }
71
- .note.shown {
72
- display: block;
73
- }
74
68
  .shot {
75
69
  display: none;
70
+ max-width: 100%;
76
71
  width: 100%;
77
- border: var(--border-width-regular, 1px) solid
78
- var(--color-border-primary, #e2e8f0);
72
+ border: var(--border-width-regular, 1px) solid var(--sb-app-border);
79
73
  border-radius: var(--border-radius-md, 8px);
80
74
  }
81
75
  .shot.shown {
@@ -86,51 +80,40 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
86
80
  flex-wrap: wrap;
87
81
  gap: 8px;
88
82
  }
89
- button {
90
- appearance: none;
91
- border: var(--border-width-regular, 1px) solid
92
- var(--color-border-primary, #e2e8f0);
93
- border-radius: var(--border-radius-md, 8px);
94
- padding: 7px 12px;
95
- background: var(--color-background-secondary, #f8fafc);
96
- color: var(--color-text-primary, #0f172a);
97
- font: inherit;
98
- font-weight: var(--font-weight-medium, 500);
99
- cursor: pointer;
100
- }
101
- button:hover:not(:disabled) {
102
- background: var(--color-background-tertiary, #f1f5f9);
103
- }
104
- button:disabled {
105
- opacity: 0.45;
106
- cursor: not-allowed;
83
+ @media (max-width: 360px) {
84
+ .head {
85
+ align-items: flex-start;
86
+ flex-direction: column;
87
+ }
107
88
  }
89
+ ${MCP_APP_BRAND_CSS}
108
90
  </style>
109
91
  </head>
110
92
  <body>
111
93
  <div class="card">
112
94
  <div class="head">
113
95
  <h1>Superblocks app</h1>
114
- <span id="status" class="status">loading</span>
96
+ <span id="status" class="status" data-tone="info" aria-live="polite">loading</span>
115
97
  </div>
116
98
  <div id="appId" class="meta"></div>
117
99
  <img id="shot" class="shot" alt="Preview of the running app" />
118
- <div id="note" class="note"></div>
100
+ <div id="note" class="note" aria-live="polite"></div>
119
101
  <div class="actions">
120
- <button id="openEditor" type="button" disabled>Open editor</button>
121
- <button id="openPreview" type="button" disabled>Open preview</button>
122
- <button id="publishApp" type="button" disabled>Publish app</button>
102
+ <button id="openEditor" class="button-secondary" type="button" disabled>Open editor</button>
103
+ <button id="openPreview" class="button-secondary" type="button" disabled>Open preview</button>
104
+ <button id="publishApp" class="button-primary" type="button" disabled>Publish app</button>
123
105
  </div>
124
106
  </div>
125
107
  <script>
126
108
  (() => {
127
109
  const PROTOCOL_VERSION = "2026-01-26";
128
- // Statuses get_app reports when the app is still the empty starter
129
- // template. "building" is deliberately absent: the card only hears a
130
- // build finished if a new result arrives, so a stale badge would strand
131
- // the button.
110
+ // Statuses with no usable build to publish. "building" is deliberately
111
+ // absent because a stale badge should not strand the button.
132
112
  const UNBUILT_STATUSES = [
133
113
  "cancelled",
114
+ "checkpoint_failed",
115
+ "detached",
116
+ "failed",
134
117
  "live_edit_terminated",
135
118
  "needs_decision",
136
119
  "no_changes",
@@ -139,14 +122,33 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
139
122
  const BLOCKED_ACTIONS = [
140
123
  "answer_question",
141
124
  "approve_plan",
125
+ "open_editor",
142
126
  "retry_build",
143
- "retry_import",
144
127
  ];
128
+ const STATUS_TONES = {
129
+ building: "info",
130
+ cancelled: "warning",
131
+ checkpoint_failed: "danger",
132
+ deployed: "success",
133
+ detached: "warning",
134
+ failed: "danger",
135
+ live_edit_terminated: "danger",
136
+ loading: "info",
137
+ needs_decision: "warning",
138
+ no_changes: "warning",
139
+ publish_failed: "danger",
140
+ publishing: "info",
141
+ ready: "success",
142
+ timeout: "danger",
143
+ waiting: "info",
144
+ };
145
145
  let nextId = 1;
146
146
  const pending = new Map();
147
147
  let applicationId = "";
148
148
  let appName = "";
149
+ let branch = "";
149
150
  let commitId = "";
151
+ let directoryHash = "";
150
152
  let pendingAction = "";
151
153
  let editUrl = "";
152
154
  let previewUrl = "";
@@ -154,14 +156,14 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
154
156
  let canOpenLinks = false;
155
157
  let canCallTools = false;
156
158
  let publishing = false;
159
+ let teardownRequested = false;
157
160
  let lastHeight = 0;
158
- let refreshInFlight = false;
161
+ let buildWatchStartedAtMs;
162
+ let refreshInFlightGeneration = -1;
159
163
  let refreshTimer = 0;
160
164
  let refreshGeneration = 0;
161
- let refreshAttempts = 0;
162
- let pollAfterMs = 2000;
163
- const MIN_POLL_MS = 2000;
164
- const MAX_REFRESH_ATTEMPTS = 40;
165
+ const BUILD_WATCH_MS = 20 * 60_000;
166
+ const POLL_INTERVAL_MS = 2000;
165
167
 
166
168
  function post(message) {
167
169
  window.parent.postMessage(message, "*");
@@ -190,9 +192,21 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
190
192
  }
191
193
  }
192
194
 
195
+ function renderStatus(status) {
196
+ const badge = element("status");
197
+ badge.textContent = status;
198
+ badge.setAttribute(
199
+ "data-tone",
200
+ Object.hasOwn(STATUS_TONES, status) ? STATUS_TONES[status] : "neutral",
201
+ );
202
+ }
203
+
193
204
  // Hosts size the card from this notification alone; without it the card
194
205
  // is laid out at zero height and never appears.
195
206
  function reportSize() {
207
+ if (teardownRequested) {
208
+ return;
209
+ }
196
210
  const height = Math.ceil(document.documentElement.scrollHeight || 0);
197
211
  if (height <= 0 || height === lastHeight) {
198
212
  return;
@@ -260,6 +274,10 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
260
274
  * returns.
261
275
  */
262
276
  function nothingToPublish() {
277
+ const badge = element("status").textContent;
278
+ if (UNBUILT_STATUSES.indexOf(badge) !== -1) {
279
+ return true;
280
+ }
263
281
  if (previewUrl || publishUrl) {
264
282
  return false;
265
283
  }
@@ -269,11 +287,10 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
269
287
  if (BLOCKED_ACTIONS.indexOf(pendingAction) !== -1) {
270
288
  return true;
271
289
  }
272
- const badge = element("status").textContent;
273
290
  if (!badge || badge === "loading" || badge === "waiting") {
274
291
  return true;
275
292
  }
276
- return UNBUILT_STATUSES.indexOf(badge) !== -1;
293
+ return false;
277
294
  }
278
295
 
279
296
  /**
@@ -281,19 +298,28 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
281
298
  * huge and ugly in the card).
282
299
  */
283
300
  function renderActions() {
301
+ const status = element("status").textContent;
302
+ const previewReady = status === "ready";
303
+ const rollingOut = status === "publishing";
304
+ const published = Boolean(publishUrl) && !rollingOut;
284
305
  element("openEditor").disabled = !editUrl || !canOpenLinks;
285
- element("openPreview").disabled = !previewUrl || !canOpenLinks;
306
+ element("openPreview").disabled =
307
+ !previewReady || !previewUrl || !canOpenLinks;
286
308
  // Once the app is live, publishing again is not what anyone wants from
287
309
  // this button — going to the live app is.
288
- element("publishApp").textContent = publishUrl
289
- ? "Open published app"
290
- : "Publish app";
291
- element("publishApp").disabled = publishUrl
292
- ? !canOpenLinks
293
- : !applicationId ||
294
- !canCallTools ||
295
- publishing ||
296
- nothingToPublish();
310
+ element("publishApp").textContent = rollingOut
311
+ ? "Publishing..."
312
+ : published
313
+ ? "Open published app"
314
+ : "Publish app";
315
+ element("publishApp").disabled = rollingOut
316
+ ? true
317
+ : published
318
+ ? !canOpenLinks
319
+ : !applicationId ||
320
+ !canCallTools ||
321
+ publishing ||
322
+ nothingToPublish();
297
323
  // Only warn about what is actually broken. Hosts that do open links
298
324
  // handle local http fine; if one refuses, refused() says so then.
299
325
  if (!canOpenLinks && (editUrl || previewUrl) && !publishUrl) {
@@ -351,8 +377,29 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
351
377
  if (!value || typeof value !== "object") {
352
378
  return;
353
379
  }
380
+ if (
381
+ value.browserNavigation &&
382
+ typeof value.browserNavigation === "object" &&
383
+ value.browserNavigation.action === "open_editor"
384
+ ) {
385
+ if (!teardownRequested) {
386
+ teardownRequested = true;
387
+ refreshGeneration += 1;
388
+ clearRefreshTimer();
389
+ document.body.hidden = true;
390
+ notify("ui/notifications/size-changed", { height: 0, width: 0 });
391
+ notify("ui/notifications/request-teardown", {});
392
+ }
393
+ return;
394
+ }
395
+ if (teardownRequested) {
396
+ return;
397
+ }
354
398
  if (typeof value.status === "string") {
355
- element("status").textContent = value.status;
399
+ if (value.status !== "building") {
400
+ buildWatchStartedAtMs = undefined;
401
+ }
402
+ renderStatus(value.status);
356
403
  }
357
404
  if (typeof value.applicationId === "string") {
358
405
  applicationId = value.applicationId;
@@ -362,8 +409,20 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
362
409
  appName = value.name;
363
410
  renderIdentity();
364
411
  }
365
- if (typeof value.commitId === "string") {
366
- commitId = value.commitId;
412
+ if (typeof value.branch === "string") {
413
+ branch = value.branch;
414
+ }
415
+ if (typeof value.directoryHash === "string") {
416
+ directoryHash = value.directoryHash;
417
+ }
418
+ if (
419
+ typeof value.commitId === "string" ||
420
+ typeof value.previewUrl === "string" ||
421
+ (typeof value.applicationId === "string" &&
422
+ typeof value.status === "string")
423
+ ) {
424
+ commitId =
425
+ typeof value.commitId === "string" ? value.commitId : "";
367
426
  }
368
427
  if (typeof value.pendingAction === "string") {
369
428
  pendingAction = value.pendingAction;
@@ -378,13 +437,12 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
378
437
  }
379
438
  if (typeof value.previewUrl === "string") {
380
439
  previewUrl = webUrl(value.previewUrl);
440
+ } else if (typeof value.status === "string" && !commitId) {
441
+ previewUrl = "";
381
442
  }
382
443
  if (typeof value.publishUrl === "string") {
383
444
  publishUrl = webUrl(value.publishUrl);
384
445
  }
385
- if (typeof value.nextPollAfterMs === "number") {
386
- pollAfterMs = value.nextPollAfterMs;
387
- }
388
446
  // Hosts that deliver URLs without a status left the badge on the HTML
389
447
  // default ("loading") even after the app was built.
390
448
  const badge = element("status").textContent;
@@ -393,15 +451,21 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
393
451
  editUrl &&
394
452
  (badge === "" || badge === "loading" || badge === "waiting")
395
453
  ) {
396
- element("status").textContent = previewUrl ? "ready" : "building";
454
+ renderStatus(previewUrl ? "ready" : "building");
397
455
  }
398
456
  renderActions();
457
+ if (
458
+ canCallTools &&
459
+ commitId &&
460
+ element("status").textContent === "publishing"
461
+ ) {
462
+ scheduleRefresh(retryDelay());
463
+ }
399
464
  }
400
465
 
401
466
  /**
402
- * Re-ask get_app when the host gave us no result, or while status is
403
- * still building. wait:false and ensurePreview:false so polls only
404
- * ask for status. Honor nextPollAfterMs; 0 means stop.
467
+ * Ask the app-only status tool when the host gave us no result, or while
468
+ * status is still building.
405
469
  */
406
470
  function clearRefreshTimer() {
407
471
  if (refreshTimer) {
@@ -411,12 +475,16 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
411
475
  }
412
476
 
413
477
  function retryDelay() {
414
- return Math.max(MIN_POLL_MS, pollAfterMs || MIN_POLL_MS);
478
+ return POLL_INTERVAL_MS;
415
479
  }
416
480
 
417
481
  function scheduleRefresh(delayMs) {
418
482
  clearRefreshTimer();
419
- if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) {
483
+ if (
484
+ element("status").textContent !== "publishing" &&
485
+ buildWatchStartedAtMs !== undefined &&
486
+ Date.now() - buildWatchStartedAtMs >= BUILD_WATCH_MS
487
+ ) {
420
488
  note("Stopped checking whether the app is still building.");
421
489
  return;
422
490
  }
@@ -428,7 +496,12 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
428
496
 
429
497
  function refreshFromServer() {
430
498
  clearRefreshTimer();
431
- if (!canCallTools || !applicationId || refreshInFlight) {
499
+ if (
500
+ teardownRequested ||
501
+ !canCallTools ||
502
+ refreshInFlightGeneration === refreshGeneration ||
503
+ publishing
504
+ ) {
432
505
  return;
433
506
  }
434
507
  if (typeof document.hidden === "boolean" && document.hidden) {
@@ -436,32 +509,69 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
436
509
  return;
437
510
  }
438
511
  const badge = element("status").textContent;
439
- if (editUrl && badge !== "building") {
512
+ const publishStatus = badge === "publishing";
513
+ if (editUrl && badge !== "building" && !publishStatus) {
514
+ buildWatchStartedAtMs = undefined;
440
515
  return;
441
516
  }
442
- if (refreshAttempts >= MAX_REFRESH_ATTEMPTS) {
443
- note("Stopped checking whether the app is still building.");
444
- return;
517
+ if (!publishStatus) {
518
+ if (badge === "building") {
519
+ if (buildWatchStartedAtMs === undefined) {
520
+ buildWatchStartedAtMs = Date.now();
521
+ }
522
+ } else {
523
+ buildWatchStartedAtMs = undefined;
524
+ }
525
+ if (
526
+ buildWatchStartedAtMs !== undefined &&
527
+ Date.now() - buildWatchStartedAtMs >= BUILD_WATCH_MS
528
+ ) {
529
+ note("Stopped checking whether the app is still building.");
530
+ return;
531
+ }
445
532
  }
446
- refreshAttempts += 1;
447
533
  const generation = refreshGeneration;
448
- refreshInFlight = true;
534
+ refreshInFlightGeneration = generation;
535
+ const previewStatus = !publishStatus && Boolean(commitId && directoryHash);
449
536
  request("tools/call", {
450
- name: "get_app",
537
+ name: publishStatus
538
+ ? "check_publish_progress"
539
+ : previewStatus
540
+ ? "get_preview_status"
541
+ : "get_app_status",
451
542
  arguments: {
452
- applicationId,
453
- ensurePreview: false,
454
- wait: false,
543
+ ...(applicationId ? { applicationId } : {}),
544
+ ...(!publishStatus && branch ? { branch } : {}),
545
+ ...(publishStatus && commitId ? { commitId } : {}),
546
+ ...(previewStatus ? { commitId, directoryHash, wait: false } : {}),
455
547
  },
456
548
  })
457
549
  .then((result) => {
458
- refreshInFlight = false;
550
+ if (refreshInFlightGeneration === generation) {
551
+ refreshInFlightGeneration = -1;
552
+ }
459
553
  if (generation !== refreshGeneration) {
460
- if (element("status").textContent === "building") {
554
+ if (
555
+ !publishing &&
556
+ element("status").textContent === "building" &&
557
+ refreshInFlightGeneration !== refreshGeneration
558
+ ) {
461
559
  scheduleRefresh(retryDelay());
462
560
  }
463
561
  return;
464
562
  }
563
+ const publishError = publishStatus
564
+ ? terminalPublishErrorOf(result)
565
+ : "";
566
+ if (publishError) {
567
+ refreshGeneration += 1;
568
+ clearRefreshTimer();
569
+ publishUrl = "";
570
+ renderStatus("publish_failed");
571
+ note(publishError);
572
+ renderActions();
573
+ return;
574
+ }
465
575
  if (!result || result.isError) {
466
576
  scheduleRefresh(retryDelay());
467
577
  return;
@@ -470,16 +580,28 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
470
580
  if (result.structuredContent) {
471
581
  applyPayload(result.structuredContent);
472
582
  }
473
- if (element("status").textContent !== "building") {
583
+ const status = element("status").textContent;
584
+ if (status === "deployed") {
585
+ note(
586
+ publishUrl
587
+ ? "Published - open the live app with Open published app."
588
+ : "Published - the app is live.",
589
+ );
474
590
  return;
475
591
  }
476
- if (pollAfterMs === 0) {
592
+ if (status === "publishing") {
593
+ scheduleRefresh(retryDelay());
594
+ return;
595
+ }
596
+ if (status !== "building") {
477
597
  return;
478
598
  }
479
599
  scheduleRefresh(retryDelay());
480
600
  })
481
601
  .catch(() => {
482
- refreshInFlight = false;
602
+ if (refreshInFlightGeneration === generation) {
603
+ refreshInFlightGeneration = -1;
604
+ }
483
605
  if (generation !== refreshGeneration) {
484
606
  return;
485
607
  }
@@ -491,8 +613,13 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
491
613
  if (!context || typeof context !== "object") {
492
614
  return;
493
615
  }
616
+ const root = document.documentElement;
617
+ if (context.theme === "light" || context.theme === "dark") {
618
+ root.setAttribute("data-theme", context.theme);
619
+ root.style.colorScheme = context.theme;
620
+ }
494
621
  const variables = context.styles && context.styles.variables;
495
- const style = document.documentElement.style;
622
+ const style = root.style;
496
623
  if (variables && style && typeof style.setProperty === "function") {
497
624
  for (const name of Object.keys(variables)) {
498
625
  const value = variables[name];
@@ -501,9 +628,72 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
501
628
  }
502
629
  }
503
630
  }
631
+ const fonts = context.styles && context.styles.css?.fonts;
632
+ if (typeof fonts === "string") {
633
+ let fontStyle = document.getElementById("__mcp-host-fonts");
634
+ if (!fontStyle) {
635
+ fontStyle = document.createElement("style");
636
+ fontStyle.id = "__mcp-host-fonts";
637
+ document.head.appendChild(fontStyle);
638
+ }
639
+ fontStyle.textContent = fonts;
640
+ }
504
641
  reportSize();
505
642
  }
506
643
 
644
+ function capabilityFailureOf(result) {
645
+ if (!result || !Array.isArray(result.content)) {
646
+ return null;
647
+ }
648
+ for (const block of result.content) {
649
+ if (!block || block.type !== "text" || !block.text) {
650
+ continue;
651
+ }
652
+ try {
653
+ const parsed = JSON.parse(block.text);
654
+ if (parsed && parsed.kind === "error") {
655
+ return {
656
+ code: parsed.code || "",
657
+ message: parsed.message || parsed.code || "Tool call failed.",
658
+ };
659
+ }
660
+ } catch {
661
+ continue;
662
+ }
663
+ }
664
+ return null;
665
+ }
666
+
667
+ function capabilityErrorOf(result) {
668
+ return capabilityFailureOf(result)?.message || "";
669
+ }
670
+
671
+ function terminalPublishErrorOf(result) {
672
+ const failure = capabilityFailureOf(result);
673
+ if (
674
+ !failure ||
675
+ ![
676
+ "publish_failed",
677
+ "publish_needs_acknowledgement",
678
+ "publish_timeout",
679
+ ].includes(failure.code)
680
+ ) {
681
+ return "";
682
+ }
683
+ return failure.message;
684
+ }
685
+
686
+ /** First text block of a failed tool result, for the note line. */
687
+ function errorTextOf(result, fallback) {
688
+ const capabilityError = capabilityErrorOf(result);
689
+ if (capabilityError) {
690
+ return capabilityError;
691
+ }
692
+ const first =
693
+ result && Array.isArray(result.content) ? result.content[0] : null;
694
+ return first && first.text ? String(first.text).slice(0, 280) : fallback;
695
+ }
696
+
507
697
  function refused(url) {
508
698
  note(
509
699
  isPlainHttp(url)
@@ -541,6 +731,8 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
541
731
  return;
542
732
  }
543
733
  publishing = true;
734
+ refreshGeneration += 1;
735
+ clearRefreshTimer();
544
736
  renderActions();
545
737
  note("Publishing…");
546
738
  const args = {
@@ -552,29 +744,30 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
552
744
  if (commitId) {
553
745
  args.commitId = commitId;
554
746
  }
747
+ if (branch) {
748
+ args.branch = branch;
749
+ }
555
750
  request("tools/call", { name: "publish_app", arguments: args })
556
751
  .then((result) => {
557
752
  publishing = false;
558
- if (!result || result.isError) {
559
- const text =
560
- result &&
561
- Array.isArray(result.content) &&
562
- result.content[0] &&
563
- result.content[0].text
564
- ? String(result.content[0].text)
565
- : "Publish failed.";
566
- note(text.slice(0, 280));
753
+ const publishError = capabilityErrorOf(result);
754
+ if (!result || result.isError || publishError) {
755
+ note(errorTextOf(result, "Publish failed."));
567
756
  renderActions();
757
+ refreshFromServer();
568
758
  return;
569
759
  }
570
760
  applyContent(result.content || []);
571
761
  if (result.structuredContent) {
572
762
  applyPayload(result.structuredContent);
573
763
  }
764
+ const status = element("status").textContent;
574
765
  note(
575
- publishUrl
576
- ? "Published — open the live app with Open published app."
577
- : "Published — the app is live.",
766
+ status === "publishing"
767
+ ? "Publish accepted; the deploy is still rolling out."
768
+ : publishUrl
769
+ ? "Published - open the live app with Open published app."
770
+ : "Published - the app is live.",
578
771
  );
579
772
  renderActions();
580
773
  })
@@ -585,6 +778,7 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
585
778
  "The host could not call publish_app.",
586
779
  );
587
780
  renderActions();
781
+ refreshFromServer();
588
782
  });
589
783
  }
590
784
 
@@ -644,6 +838,9 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
644
838
  openLink(previewUrl),
645
839
  );
646
840
  element("publishApp").addEventListener("click", () => {
841
+ if (element("status").textContent === "publishing") {
842
+ return;
843
+ }
647
844
  if (publishUrl) {
648
845
  openLink(publishUrl);
649
846
  return;
@@ -659,7 +856,7 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
659
856
  appCapabilities: {
660
857
  availableDisplayModes: ["inline"],
661
858
  },
662
- appInfo: { name: "gateway-app-status", version: "0.0.6" },
859
+ appInfo: { name: "gateway-app-status", version: "0.0.8" },
663
860
  protocolVersion: PROTOCOL_VERSION,
664
861
  })
665
862
  .then((result) => {
@@ -675,12 +872,23 @@ export const APP_STATUS_HTML = `<!DOCTYPE html>
675
872
  result.hostCapabilities &&
676
873
  result.hostCapabilities.serverTools,
677
874
  );
678
- applyHostContext(result && result.hostContext);
679
- renderActions();
680
875
  if (result && result.toolResult) {
681
876
  applyPayload(result.toolResult);
682
877
  }
683
- refreshFromServer();
878
+ if (teardownRequested) {
879
+ return;
880
+ }
881
+ applyHostContext(result && result.hostContext);
882
+ renderActions();
883
+ const badge = element("status").textContent;
884
+ if (badge === "publishing") {
885
+ return;
886
+ }
887
+ if (!applicationId && (!badge || badge === "loading")) {
888
+ scheduleRefresh(500);
889
+ } else {
890
+ refreshFromServer();
891
+ }
684
892
  })
685
893
  .catch(() => {
686
894
  // Hosts that push tool results via notification still work.