@superblocksteam/gateway 2.0.177-next.0 → 2.0.178-next.0

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 (221) hide show
  1. package/README.md +52 -19
  2. package/dist/agents/hosted-boundary.d.ts +36 -0
  3. package/dist/agents/hosted-boundary.d.ts.map +1 -0
  4. package/dist/agents/hosted-boundary.js +66 -0
  5. package/dist/agents/hosted-boundary.js.map +1 -0
  6. package/dist/agents/resolve-orchestrator-url.d.ts +8 -0
  7. package/dist/agents/resolve-orchestrator-url.d.ts.map +1 -1
  8. package/dist/agents/resolve-orchestrator-url.js +5 -0
  9. package/dist/agents/resolve-orchestrator-url.js.map +1 -1
  10. package/dist/capabilities/integration-metadata.d.ts.map +1 -1
  11. package/dist/capabilities/integration-metadata.js +12 -1
  12. package/dist/capabilities/integration-metadata.js.map +1 -1
  13. package/dist/capabilities/lifecycle.d.ts +1 -6
  14. package/dist/capabilities/lifecycle.d.ts.map +1 -1
  15. package/dist/capabilities/lifecycle.js +339 -21
  16. package/dist/capabilities/lifecycle.js.map +1 -1
  17. package/dist/capabilities/live-state-attaches.d.ts +28 -0
  18. package/dist/capabilities/live-state-attaches.d.ts.map +1 -1
  19. package/dist/capabilities/live-state-attaches.js +33 -0
  20. package/dist/capabilities/live-state-attaches.js.map +1 -1
  21. package/dist/capabilities/query-integration.d.ts.map +1 -1
  22. package/dist/capabilities/query-integration.js +12 -1
  23. package/dist/capabilities/query-integration.js.map +1 -1
  24. package/dist/capabilities/tool-permission-reply.d.ts.map +1 -1
  25. package/dist/capabilities/tool-permission-reply.js.map +1 -1
  26. package/dist/capabilities/types.d.ts +3 -4
  27. package/dist/capabilities/types.d.ts.map +1 -1
  28. package/dist/capabilities/types.js.map +1 -1
  29. package/dist/capture/browser-contract.d.ts +1 -18
  30. package/dist/capture/browser-contract.d.ts.map +1 -1
  31. package/dist/capture/browser-contract.js +1 -13
  32. package/dist/capture/browser-contract.js.map +1 -1
  33. package/dist/capture/browser-instructions.d.ts +3 -3
  34. package/dist/capture/browser-instructions.d.ts.map +1 -1
  35. package/dist/capture/browser-instructions.js +4 -5
  36. package/dist/capture/browser-instructions.js.map +1 -1
  37. package/dist/capture/capture-library-probe.d.ts +0 -1
  38. package/dist/capture/capture-library-probe.d.ts.map +1 -1
  39. package/dist/capture/capture-library-probe.js +1 -1
  40. package/dist/capture/capture-library-probe.js.map +1 -1
  41. package/dist/capture/capture-library.d.ts +2 -1
  42. package/dist/capture/capture-library.d.ts.map +1 -1
  43. package/dist/capture/classify-library-paint.d.ts +2 -1
  44. package/dist/capture/classify-library-paint.d.ts.map +1 -1
  45. package/dist/capture/library-parent-script.d.ts +3 -2
  46. package/dist/capture/library-parent-script.d.ts.map +1 -1
  47. package/dist/capture/mode.d.ts +2 -3
  48. package/dist/capture/mode.d.ts.map +1 -1
  49. package/dist/capture/mode.js +2 -3
  50. package/dist/capture/mode.js.map +1 -1
  51. package/dist/config.d.ts +21 -1
  52. package/dist/config.d.ts.map +1 -1
  53. package/dist/config.js +3 -0
  54. package/dist/config.js.map +1 -1
  55. package/dist/debug.js.map +1 -1
  56. package/dist/deps.d.ts +3 -3
  57. package/dist/health/readiness.d.ts +2 -1
  58. package/dist/health/readiness.d.ts.map +1 -1
  59. package/dist/http/main.d.ts +3 -0
  60. package/dist/http/main.d.ts.map +1 -0
  61. package/dist/http/main.js +11 -0
  62. package/dist/http/main.js.map +1 -0
  63. package/dist/http/serve.d.ts +26 -0
  64. package/dist/http/serve.d.ts.map +1 -0
  65. package/dist/http/serve.js +298 -0
  66. package/dist/http/serve.js.map +1 -0
  67. package/dist/integrations/filters.d.ts.map +1 -1
  68. package/dist/integrations/filters.js +1 -16
  69. package/dist/integrations/filters.js.map +1 -1
  70. package/dist/integrations/map.d.ts +0 -5
  71. package/dist/integrations/map.d.ts.map +1 -1
  72. package/dist/integrations/map.js +1 -1
  73. package/dist/integrations/map.js.map +1 -1
  74. package/dist/internal-ingress.d.ts +47 -0
  75. package/dist/internal-ingress.d.ts.map +1 -0
  76. package/dist/internal-ingress.js +90 -0
  77. package/dist/internal-ingress.js.map +1 -0
  78. package/dist/orchestrator/client.d.ts +11 -5
  79. package/dist/orchestrator/client.d.ts.map +1 -1
  80. package/dist/orchestrator/client.js +14 -3
  81. package/dist/orchestrator/client.js.map +1 -1
  82. package/dist/playwright/ensure-chromium.js.map +1 -1
  83. package/dist/preview/capture-screenshot.d.ts +2 -2
  84. package/dist/preview/capture-screenshot.d.ts.map +1 -1
  85. package/dist/preview/capture-screenshot.js +2 -2
  86. package/dist/preview/capture-screenshot.js.map +1 -1
  87. package/dist/process/fault-barrier.d.ts +4 -3
  88. package/dist/process/fault-barrier.d.ts.map +1 -1
  89. package/dist/sabs/app-state.d.ts +2 -13
  90. package/dist/sabs/app-state.d.ts.map +1 -1
  91. package/dist/sabs/app-state.js +7 -2
  92. package/dist/sabs/app-state.js.map +1 -1
  93. package/dist/sabs/awaited-decision.d.ts.map +1 -1
  94. package/dist/sabs/browser-context.d.ts +4 -1
  95. package/dist/sabs/browser-context.d.ts.map +1 -1
  96. package/dist/sabs/browser-context.js +1 -0
  97. package/dist/sabs/browser-context.js.map +1 -1
  98. package/dist/sabs/editor-client-methods.d.ts.map +1 -1
  99. package/dist/sabs/editor-client-methods.js +1 -1
  100. package/dist/sabs/editor-client-methods.js.map +1 -1
  101. package/dist/sabs/editor-socket.d.ts.map +1 -1
  102. package/dist/sabs/editor-socket.js.map +1 -1
  103. package/dist/sabs/session-peer.d.ts +13 -1
  104. package/dist/sabs/session-peer.d.ts.map +1 -1
  105. package/dist/sabs/session-peer.js.map +1 -1
  106. package/dist/sabs/streamed-reply.d.ts.map +1 -1
  107. package/dist/sabs/streamed-reply.js.map +1 -1
  108. package/dist/sabs/turn-collector.d.ts.map +1 -1
  109. package/dist/sabs/turn-collector.js.map +1 -1
  110. package/dist/sabs/websocket-session-peer.d.ts +17 -1
  111. package/dist/sabs/websocket-session-peer.d.ts.map +1 -1
  112. package/dist/sabs/websocket-session-peer.js +89 -15
  113. package/dist/sabs/websocket-session-peer.js.map +1 -1
  114. package/dist/server/client.d.ts +48 -0
  115. package/dist/server/client.d.ts.map +1 -1
  116. package/dist/server/client.js +91 -173
  117. package/dist/server/client.js.map +1 -1
  118. package/dist/server/identity-reads.d.ts +5 -3
  119. package/dist/server/identity-reads.d.ts.map +1 -1
  120. package/dist/server/identity-reads.js +31 -4
  121. package/dist/server/identity-reads.js.map +1 -1
  122. package/dist/start.d.ts +5 -1
  123. package/dist/start.d.ts.map +1 -1
  124. package/dist/start.js +23 -9
  125. package/dist/start.js.map +1 -1
  126. package/dist/stores/memory.d.ts.map +1 -1
  127. package/dist/stores/types.d.ts +2 -1
  128. package/dist/stores/types.d.ts.map +1 -1
  129. package/dist/telemetry/mcp-client.d.ts +2 -1
  130. package/dist/telemetry/mcp-client.d.ts.map +1 -1
  131. package/dist/telemetry/mcp-client.js.map +1 -1
  132. package/dist/telemetry/mcp-usage.d.ts +22 -0
  133. package/dist/telemetry/mcp-usage.d.ts.map +1 -0
  134. package/dist/telemetry/mcp-usage.js +102 -0
  135. package/dist/telemetry/mcp-usage.js.map +1 -0
  136. package/dist/telemetry/metrics.d.ts +9 -0
  137. package/dist/telemetry/metrics.d.ts.map +1 -1
  138. package/dist/telemetry/metrics.js +21 -0
  139. package/dist/telemetry/metrics.js.map +1 -1
  140. package/dist/telemetry/runtime.d.ts +2 -1
  141. package/dist/telemetry/runtime.d.ts.map +1 -1
  142. package/dist/telemetry/runtime.js +18 -5
  143. package/dist/telemetry/runtime.js.map +1 -1
  144. package/dist/transports/mcp/admin-tools.d.ts +3 -7
  145. package/dist/transports/mcp/admin-tools.d.ts.map +1 -1
  146. package/dist/transports/mcp/admin-tools.js +9 -82
  147. package/dist/transports/mcp/admin-tools.js.map +1 -1
  148. package/dist/transports/mcp/app-status-html.d.ts +2 -2
  149. package/dist/transports/mcp/app-status-html.d.ts.map +1 -1
  150. package/dist/transports/mcp/app-status-html.js +1 -27
  151. package/dist/transports/mcp/app-status-html.js.map +1 -1
  152. package/dist/transports/mcp/client-presentation.d.ts +10 -13
  153. package/dist/transports/mcp/client-presentation.d.ts.map +1 -1
  154. package/dist/transports/mcp/client-presentation.js +2 -14
  155. package/dist/transports/mcp/client-presentation.js.map +1 -1
  156. package/dist/transports/mcp/decision-elicitation.js +23 -1
  157. package/dist/transports/mcp/decision-elicitation.js.map +1 -1
  158. package/dist/transports/mcp/dispatch-probe.js.map +1 -1
  159. package/dist/transports/mcp/editor-document-probe.js.map +1 -1
  160. package/dist/transports/mcp/editor-integration-setup-url.d.ts +0 -1
  161. package/dist/transports/mcp/editor-integration-setup-url.d.ts.map +1 -1
  162. package/dist/transports/mcp/editor-integration-setup-url.js +0 -3
  163. package/dist/transports/mcp/editor-integration-setup-url.js.map +1 -1
  164. package/dist/transports/mcp/format-tool-content.js +1 -1
  165. package/dist/transports/mcp/format-tool-content.js.map +1 -1
  166. package/dist/transports/mcp/instructions/tools/build-app.d.ts +2 -3
  167. package/dist/transports/mcp/instructions/tools/build-app.d.ts.map +1 -1
  168. package/dist/transports/mcp/instructions/tools/build-app.js +5 -11
  169. package/dist/transports/mcp/instructions/tools/build-app.js.map +1 -1
  170. package/dist/transports/mcp/instructions/tools/check-app-progress.d.ts.map +1 -1
  171. package/dist/transports/mcp/instructions/tools/check-app-progress.js +1 -25
  172. package/dist/transports/mcp/instructions/tools/check-app-progress.js.map +1 -1
  173. package/dist/transports/mcp/instructions/tools/check-publish-progress.d.ts +1 -2
  174. package/dist/transports/mcp/instructions/tools/check-publish-progress.d.ts.map +1 -1
  175. package/dist/transports/mcp/instructions/tools/check-publish-progress.js +2 -11
  176. package/dist/transports/mcp/instructions/tools/check-publish-progress.js.map +1 -1
  177. package/dist/transports/mcp/instructions/tools/copy.d.ts +1 -8
  178. package/dist/transports/mcp/instructions/tools/copy.d.ts.map +1 -1
  179. package/dist/transports/mcp/instructions/tools/copy.js +4 -28
  180. package/dist/transports/mcp/instructions/tools/copy.js.map +1 -1
  181. package/dist/transports/mcp/instructions/tools/create-integration.d.ts +1 -2
  182. package/dist/transports/mcp/instructions/tools/create-integration.d.ts.map +1 -1
  183. package/dist/transports/mcp/instructions/tools/create-integration.js +4 -8
  184. package/dist/transports/mcp/instructions/tools/create-integration.js.map +1 -1
  185. package/dist/transports/mcp/instructions/tools/edit-app.js +2 -2
  186. package/dist/transports/mcp/instructions/tools/edit-app.js.map +1 -1
  187. package/dist/transports/mcp/instructions/tools/get-app.d.ts.map +1 -1
  188. package/dist/transports/mcp/instructions/tools/get-app.js +1 -23
  189. package/dist/transports/mcp/instructions/tools/get-app.js.map +1 -1
  190. package/dist/transports/mcp/instructions/tools/index.d.ts.map +1 -1
  191. package/dist/transports/mcp/instructions/tools/index.js +0 -4
  192. package/dist/transports/mcp/instructions/tools/index.js.map +1 -1
  193. package/dist/transports/mcp/instructions/tools/publish-app.d.ts +1 -2
  194. package/dist/transports/mcp/instructions/tools/publish-app.d.ts.map +1 -1
  195. package/dist/transports/mcp/instructions/tools/publish-app.js +1 -10
  196. package/dist/transports/mcp/instructions/tools/publish-app.js.map +1 -1
  197. package/dist/transports/mcp/instructions/tools/start-app.d.ts +1 -2
  198. package/dist/transports/mcp/instructions/tools/start-app.d.ts.map +1 -1
  199. package/dist/transports/mcp/instructions/tools/start-app.js +3 -3
  200. package/dist/transports/mcp/instructions/tools/start-app.js.map +1 -1
  201. package/dist/transports/mcp/mount.d.ts +8 -15
  202. package/dist/transports/mcp/mount.d.ts.map +1 -1
  203. package/dist/transports/mcp/mount.js +233 -206
  204. package/dist/transports/mcp/mount.js.map +1 -1
  205. package/dist/transports/mcp/plan-approval.d.ts.map +1 -1
  206. package/dist/transports/mcp/plan-approval.js.map +1 -1
  207. package/dist/transports/mcp/tool-permissions.d.ts +30 -0
  208. package/dist/transports/mcp/tool-permissions.d.ts.map +1 -0
  209. package/dist/transports/mcp/tool-permissions.js +39 -0
  210. package/dist/transports/mcp/tool-permissions.js.map +1 -0
  211. package/package.json +28 -22
  212. package/skills/superblocks-import/SKILL.md +5 -3
  213. package/dist/capture/cowork-editor-url.d.ts +0 -7
  214. package/dist/capture/cowork-editor-url.d.ts.map +0 -1
  215. package/dist/capture/cowork-editor-url.js +0 -12
  216. package/dist/capture/cowork-editor-url.js.map +0 -1
  217. package/dist/transports/mcp/native-browser-presence.d.ts +0 -46
  218. package/dist/transports/mcp/native-browser-presence.d.ts.map +0 -1
  219. package/dist/transports/mcp/native-browser-presence.js +0 -158
  220. package/dist/transports/mcp/native-browser-presence.js.map +0 -1
  221. package/skills/superblocks-build/SKILL.md +0 -59
@@ -1,5 +1,7 @@
1
1
  import { llmConfigFromFlags, } from "@superblocksteam/library-shared/types";
2
- import { uploadAttachmentToServerApi } from "@superblocksteam/shared";
2
+ import { errorCode, isLostConnection, isRequestNotSent, isTransientHttpStatus, MIN_RETRY_ATTEMPT_MS, ReadRetrySchedule, } from "@superblocksteam/mcp-server/read-retry";
3
+ import { PlatformMcpServerRequest, uploadAttachmentToServerApi, } from "@superblocksteam/shared";
4
+ import { internalIngress } from "../internal-ingress.js";
3
5
  import { recordFactsFlagBootstrapOutcome, recordIdentityReadOutcome, recordServerReadRetryOutcome, } from "../telemetry/metrics.js";
4
6
  import { withGuardedIdentityReads, } from "./identity-reads.js";
5
7
  const MAX_DETAIL_LENGTH = 300;
@@ -54,51 +56,6 @@ export class ServerTimeoutError extends Error {
54
56
  this.name = "ServerTimeoutError";
55
57
  }
56
58
  }
57
- const RETRYABLE_TRANSPORT_CODES = new Set([
58
- "EAI_AGAIN",
59
- "ECONNABORTED",
60
- "ECONNREFUSED",
61
- "ECONNRESET",
62
- "EHOSTUNREACH",
63
- "ENETDOWN",
64
- "ENETUNREACH",
65
- "EPIPE",
66
- "ETIMEDOUT",
67
- "UND_ERR_BODY_TIMEOUT",
68
- "UND_ERR_CONNECT_TIMEOUT",
69
- "UND_ERR_HEADERS_TIMEOUT",
70
- "UND_ERR_SOCKET",
71
- ]);
72
- const UNSENT_TRANSPORT_CODES = new Set([
73
- "EAI_AGAIN",
74
- "ECONNREFUSED",
75
- "EHOSTUNREACH",
76
- "ENETDOWN",
77
- "ENETUNREACH",
78
- "UND_ERR_CONNECT_TIMEOUT",
79
- ]);
80
- function errorCode(error, depth = 0) {
81
- if (depth >= 4 || !error || typeof error !== "object") {
82
- return undefined;
83
- }
84
- const { cause, code } = error;
85
- if (typeof code === "string") {
86
- return code;
87
- }
88
- const causeCode = errorCode(cause, depth + 1);
89
- if (causeCode) {
90
- return causeCode;
91
- }
92
- if (error instanceof AggregateError) {
93
- for (const nested of error.errors) {
94
- const nestedCode = errorCode(nested, depth + 1);
95
- if (nestedCode) {
96
- return nestedCode;
97
- }
98
- }
99
- }
100
- return undefined;
101
- }
102
59
  /**
103
60
  * The connection failed: refused, reset, or dropped before the answer was
104
61
  * complete. Named so a retry can cover it without also covering a response
@@ -118,10 +75,8 @@ export class ServerTransportError extends Error {
118
75
  this.name = "ServerTransportError";
119
76
  this.code = code;
120
77
  this.requestNotSent =
121
- options.requestNotSent === true &&
122
- code !== undefined &&
123
- UNSENT_TRANSPORT_CODES.has(code);
124
- this.retryable = code !== undefined && RETRYABLE_TRANSPORT_CODES.has(code);
78
+ options.requestNotSent === true && isRequestNotSent(options.cause);
79
+ this.retryable = isLostConnection(options.cause);
125
80
  }
126
81
  }
127
82
  /**
@@ -144,6 +99,10 @@ const DEFAULT_TIMEOUT_MS = 120_000;
144
99
  * rather than never can still finish and fill the cache.
145
100
  */
146
101
  const DEFAULT_IDENTITY_TIMEOUT_MS = 30_000;
102
+ /** The live-edit connect route, on the branch when there is one. */
103
+ const liveEditConnectRequest = (applicationId, branch) => branch
104
+ ? PlatformMcpServerRequest.to("GET /api/v3/applications/*/branches/*/connect", applicationId, branch)
105
+ : PlatformMcpServerRequest.to("GET /api/v3/applications/*/connect", applicationId);
147
106
  /**
148
107
  * Whether a failed identity read means the endpoint is unhealthy rather than
149
108
  * that this one credential is bad.
@@ -162,72 +121,15 @@ const DEFAULT_IDENTITY_TIMEOUT_MS = 30_000;
162
121
  function identityEndpointUnhealthy(error) {
163
122
  return !(error instanceof ServerHttpError && !isTransientHttpStatus(error.status));
164
123
  }
165
- function isTransientHttpStatus(status) {
166
- return status >= 500 || status === 429;
167
- }
168
124
  /**
169
- * How long a read waits before each re-send, by what failed it.
170
- *
171
- * Only reads are retried, and only because the call sites that leave `retry`
172
- * on are pure lookups: a repeat of one changes nothing. A failed POST may well
173
- * have taken effect, and re-sending one can deploy or commit twice; a read
174
- * that provisions, or that already sits in its own poll loop, opts out.
175
- *
176
- * A transient status is a fast answer from a route that is up, so a short
177
- * pause is enough. 429 counts as transient alongside 5xx: it is the route
178
- * reporting its own capacity, and backing off is the whole point of it.
179
- * Retry-After replaces that pause when it is at most five seconds; a longer
180
- * server window is surfaced immediately rather than parking a tool call.
181
- *
182
- * A lost connection is usually a deployment rolling through the API, which
183
- * refuses and resets connections for a few seconds. This schedule waits that
184
- * out, and still finishes well inside the ten seconds a tool call waits on
185
- * identity. A timeout is never retried: that attempt already spent its whole
186
- * bound, and sending it again spends another.
187
- *
188
- * Each delay is one retry: transient statuses get two, lost connections get
189
- * four.
125
+ * What failed one attempt, in the terms {@link ReadRetrySchedule} judges.
126
+ * Anything else, such as an answer that did not parse, is never retried.
190
127
  */
191
- const TRANSIENT_STATUS_RETRY_DELAYS_MS = [100, 200];
192
- const LOST_CONNECTION_RETRY_DELAYS_MS = [250, 500, 1_000, 2_000];
193
- const MAX_RETRY_AFTER_MS = 5_000;
194
- const MIN_RETRY_ATTEMPT_MS = 50;
195
- function jitter(delayMs) {
196
- return Math.ceil(delayMs * (0.75 + Math.random() * 0.25));
197
- }
198
- function jitterAfterMinimum(delayMs) {
199
- return Math.ceil(delayMs * (1 + Math.random() * 0.25));
200
- }
201
- class ReadRetrySchedule {
202
- lostConnections = 0;
203
- transientStatuses = 0;
204
- next(error) {
205
- if (error instanceof ServerHttpError) {
206
- if (!isTransientHttpStatus(error.status)) {
207
- return undefined;
208
- }
209
- const delayMs = TRANSIENT_STATUS_RETRY_DELAYS_MS[this.transientStatuses++];
210
- if (delayMs === undefined) {
211
- return "exhausted";
212
- }
213
- if (error.retryAfterMs === undefined) {
214
- return { cause: "http", waitMs: jitter(delayMs) };
215
- }
216
- return error.retryAfterMs > MAX_RETRY_AFTER_MS
217
- ? undefined
218
- : {
219
- cause: "http",
220
- waitMs: jitterAfterMinimum(Math.max(delayMs, error.retryAfterMs)),
221
- };
222
- }
223
- if (error instanceof ServerTransportError && error.retryable) {
224
- const delayMs = LOST_CONNECTION_RETRY_DELAYS_MS[this.lostConnections++];
225
- return delayMs === undefined
226
- ? "exhausted"
227
- : { cause: "transport", waitMs: jitter(delayMs) };
228
- }
229
- return undefined;
230
- }
128
+ function readFailure(error) {
129
+ return error instanceof ServerHttpError ||
130
+ error instanceof ServerTransportError
131
+ ? error
132
+ : undefined;
231
133
  }
232
134
  function parseRetryAfterMs(value) {
233
135
  const retryAfter = value?.trim();
@@ -379,6 +281,14 @@ export class HttpSuperblocksServerClient {
379
281
  ...options.identityReads,
380
282
  });
381
283
  }
284
+ async reportMcpUsage(input) {
285
+ await this.request(PlatformMcpServerRequest.to("POST /api/v1/builder-usage"), {
286
+ apiKey: input.apiKey,
287
+ body: JSON.stringify(input.event),
288
+ retry: false,
289
+ timeoutMs: 1000,
290
+ });
291
+ }
382
292
  baseUrl() {
383
293
  return this.serverUrl.replace(/\/$/, "");
384
294
  }
@@ -390,10 +300,10 @@ export class HttpSuperblocksServerClient {
390
300
  * every field, which is indistinguishable from a legitimately empty response
391
301
  * — so this is deliberately strict rather than falling back to the raw body.
392
302
  */
393
- async requestData(path, init) {
394
- const body = await this.request(path, init);
303
+ async requestData(target, init) {
304
+ const body = await this.request(target, init);
395
305
  if (body?.data === undefined) {
396
- throw new Error(`Server ${path} returned no data field; expected a { responseMeta, data } envelope`);
306
+ throw new Error(`Server ${target.path} returned no data field; expected a { responseMeta, data } envelope`);
397
307
  }
398
308
  return body.data;
399
309
  }
@@ -408,8 +318,9 @@ export class HttpSuperblocksServerClient {
408
318
  * lost connection used to fail on first sight, which is how a build's poll
409
319
  * loop lost its caller's identity while a deployment rolled through the API.
410
320
  */
411
- async request(path, init) {
412
- const method = (init.method ?? "GET").toUpperCase();
321
+ async request(target, { query, ...init }) {
322
+ const path = query ? `${target.path}?${query}` : target.path;
323
+ const method = target.method;
413
324
  const repeatSafe = init.retry ?? method === "GET";
414
325
  const retries = new ReadRetrySchedule();
415
326
  const timeoutMs = init.timeoutMs ?? this.timeoutMs;
@@ -431,6 +342,7 @@ export class HttpSuperblocksServerClient {
431
342
  try {
432
343
  const result = await this.send(path, {
433
344
  ...init,
345
+ method,
434
346
  timeoutMs: remainingMs,
435
347
  });
436
348
  if (retryCause) {
@@ -455,7 +367,7 @@ export class HttpSuperblocksServerClient {
455
367
  (method === "GET" &&
456
368
  error instanceof ServerTransportError &&
457
369
  error.requestNotSent)
458
- ? retries.next(error)
370
+ ? retries.next(readFailure(error))
459
371
  : undefined;
460
372
  if (retry === undefined || retry === "exhausted") {
461
373
  retryExhausted = retryCause !== undefined && retry === "exhausted";
@@ -558,9 +470,8 @@ export class HttpSuperblocksServerClient {
558
470
  /** The unguarded read behind {@link validateApiKey}. */
559
471
  async readIdentityFromServer(apiKey) {
560
472
  try {
561
- const data = await this.requestData("/api/v1/users/me", {
473
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v1/users/me"), {
562
474
  apiKey,
563
- method: "GET",
564
475
  operation: "identity",
565
476
  timeoutMs: this.identityTimeoutMs,
566
477
  });
@@ -586,9 +497,8 @@ export class HttpSuperblocksServerClient {
586
497
  }
587
498
  }
588
499
  async getAgentInventory(input) {
589
- const data = await this.requestData("/api/v1/users/me", {
500
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v1/users/me"), {
590
501
  apiKey: input.apiKey,
591
- method: "GET",
592
502
  operation: "identity",
593
503
  // Same route, so the same bound. This one runs on the build path rather
594
504
  // than per tool call, and left at the sandbox timeout a wedged
@@ -631,7 +541,7 @@ export class HttpSuperblocksServerClient {
631
541
  };
632
542
  }
633
543
  async createApplication(input) {
634
- return this.requestData("/api/v2/applications/", {
544
+ return this.requestData(PlatformMcpServerRequest.to("POST /api/v2/applications/"), {
635
545
  apiKey: input.apiKey,
636
546
  // Live edit only attaches to code-mode apps; without this the server
637
547
  // builds a legacy DSL app and `connect` rejects it.
@@ -641,7 +551,6 @@ export class HttpSuperblocksServerClient {
641
551
  organizationId: input.organizationId,
642
552
  ...(input.templateName ? { templateName: input.templateName } : {}),
643
553
  }),
644
- method: "POST",
645
554
  ...(input.signal ? { signal: input.signal } : {}),
646
555
  ...(input.timeoutMs === undefined
647
556
  ? {}
@@ -678,10 +587,7 @@ export class HttpSuperblocksServerClient {
678
587
  }
679
588
  async connectLiveEdit(input) {
680
589
  const deadlineAtMs = Date.now() + this.timeoutMs;
681
- const branchSegment = input.branch
682
- ? `/branches/${encodeURIComponent(input.branch)}`
683
- : "";
684
- const path = `/api/v3/applications/${input.applicationId}${branchSegment}/connect`;
590
+ const connect = liveEditConnectRequest(input.applicationId, input.branch);
685
591
  let liveEditId = "session";
686
592
  const deadlineError = () => new Error(`Live edit ${liveEditId} did not become active within ${this.timeoutMs}ms`);
687
593
  const requestBeforeDeadline = async (query) => {
@@ -689,9 +595,9 @@ export class HttpSuperblocksServerClient {
689
595
  throw deadlineError();
690
596
  }
691
597
  try {
692
- return await this.requestData(`${path}${query}`, {
598
+ return await this.requestData(connect, {
693
599
  apiKey: input.apiKey,
694
- method: "GET",
600
+ query,
695
601
  operation: "live_edit",
696
602
  retry: false,
697
603
  timeoutMs: Math.max(1, deadlineAtMs - Date.now()),
@@ -704,7 +610,7 @@ export class HttpSuperblocksServerClient {
704
610
  throw error;
705
611
  }
706
612
  };
707
- let data = await requestBeforeDeadline("?returnBeforeReady=true");
613
+ let data = await requestBeforeDeadline("returnBeforeReady=true");
708
614
  let polled = false;
709
615
  for (;;) {
710
616
  const session = data.sandboxStartData;
@@ -716,11 +622,13 @@ export class HttpSuperblocksServerClient {
716
622
  const status = liveEditStatus(session.status);
717
623
  if (session.pitcherURL &&
718
624
  (session.status === undefined || status === "active")) {
625
+ internalIngress.route(session.pitcherURL);
719
626
  return {
720
627
  liveEditId: session.sandboxId,
721
628
  liveEditUrl: session.pitcherURL,
722
629
  pitcherToken: session.pitcherToken,
723
630
  pitcherURL: session.pitcherURL,
631
+ ...(data.targetBranch ? { targetBranch: data.targetBranch } : {}),
724
632
  };
725
633
  }
726
634
  if (status !== "starting") {
@@ -733,18 +641,14 @@ export class HttpSuperblocksServerClient {
733
641
  throw deadlineError();
734
642
  }
735
643
  polled = true;
736
- data = await requestBeforeDeadline(`?liveEditId=${encodeURIComponent(session.sandboxId)}`);
644
+ data = await requestBeforeDeadline(`liveEditId=${encodeURIComponent(session.sandboxId)}`);
737
645
  }
738
646
  }
739
647
  async getLiveEditStatus(input) {
740
- const branchSegment = input.branch
741
- ? `/branches/${encodeURIComponent(input.branch)}`
742
- : "";
743
- const path = `/api/v3/applications/${input.applicationId}${branchSegment}/connect?liveEditId=${encodeURIComponent(input.liveEditId)}`;
744
648
  try {
745
- const data = await this.requestData(path, {
649
+ const data = await this.requestData(liveEditConnectRequest(input.applicationId, input.branch), {
746
650
  apiKey: input.apiKey,
747
- method: "GET",
651
+ query: `liveEditId=${encodeURIComponent(input.liveEditId)}`,
748
652
  retry: false,
749
653
  signal: input.signal,
750
654
  });
@@ -790,10 +694,9 @@ export class HttpSuperblocksServerClient {
790
694
  }
791
695
  body.commitId = input.commitId;
792
696
  }
793
- const data = await this.request("/api/v1/token/scoped", {
697
+ const data = await this.request(PlatformMcpServerRequest.to("POST /api/v1/token/scoped"), {
794
698
  apiKey: input.apiKey,
795
699
  body: JSON.stringify(body),
796
- method: "POST",
797
700
  });
798
701
  if (!data?.access_token) {
799
702
  throw new Error("Server /api/v1/token/scoped returned no access_token; the live-edit session has nothing to verify");
@@ -811,16 +714,16 @@ export class HttpSuperblocksServerClient {
811
714
  if (input.applicationId) {
812
715
  params.set("applicationId", input.applicationId);
813
716
  }
814
- const data = await this.requestData(`/api/v1/integrations/superset?${params.toString()}`, {
717
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v1/integrations/superset"), {
815
718
  apiKey: input.apiKey,
816
- method: "GET",
719
+ query: params.toString(),
817
720
  operation: "superset_integrations",
818
721
  });
819
722
  return Array.isArray(data) ? data : [];
820
723
  }
821
724
  async getIntegration(input) {
822
725
  try {
823
- return await this.requestData(`/api/v1/integrations/${encodeURIComponent(input.integrationId)}`, { apiKey: input.apiKey, method: "GET", operation: "integration" });
726
+ return await this.requestData(PlatformMcpServerRequest.to("GET /api/v1/integrations/*", input.integrationId), { apiKey: input.apiKey, operation: "integration" });
824
727
  }
825
728
  catch (error) {
826
729
  if (error instanceof ServerHttpError && error.status === 404) {
@@ -831,7 +734,7 @@ export class HttpSuperblocksServerClient {
831
734
  }
832
735
  async getApplicationEnvironment(input) {
833
736
  try {
834
- const data = await this.requestData(`/api/v2/applications/${encodeURIComponent(input.applicationId)}/environments`, { apiKey: input.apiKey, method: "GET" });
737
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v2/applications/*/environments", input.applicationId), { apiKey: input.apiKey });
835
738
  const editor = data?.profiles?.editor;
836
739
  const availableProfileIds = Array.isArray(editor?.availableProfileIds)
837
740
  ? editor.availableProfileIds.filter((id) => typeof id === "string")
@@ -862,9 +765,8 @@ export class HttpSuperblocksServerClient {
862
765
  }
863
766
  }
864
767
  async getOrganizationProfiles(input) {
865
- const data = await this.requestData(`/api/v1/organizations/${encodeURIComponent(input.organizationId)}/profiles`, {
768
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v1/organizations/*/profiles", input.organizationId), {
866
769
  apiKey: input.apiKey,
867
- method: "GET",
868
770
  operation: "organization_profiles",
869
771
  });
870
772
  if (!Array.isArray(data)) {
@@ -884,6 +786,29 @@ export class HttpSuperblocksServerClient {
884
786
  key: row.key,
885
787
  }));
886
788
  }
789
+ async getBranchStatus(input) {
790
+ const data = await this.requestData(PlatformMcpServerRequest.to("GET /api/v2/applications/*/branch/status", input.applicationId), {
791
+ apiKey: input.apiKey,
792
+ ...(input.signal ? { signal: input.signal } : {}),
793
+ ...(input.timeoutMs !== undefined
794
+ ? { timeoutMs: input.timeoutMs }
795
+ : {}),
796
+ });
797
+ return { branch: data.branch, mainHead: data.mainHead, mode: data.mode };
798
+ }
799
+ async landBranch(input) {
800
+ return this.requestData(PlatformMcpServerRequest.to("POST /api/v2/applications/*/branch/land", input.applicationId), {
801
+ apiKey: input.apiKey,
802
+ body: JSON.stringify({
803
+ publish: true,
804
+ retryFailedMigrations: input.retryFailedMigrations,
805
+ }),
806
+ ...(input.signal ? { signal: input.signal } : {}),
807
+ ...(input.timeoutMs !== undefined
808
+ ? { timeoutMs: input.timeoutMs }
809
+ : {}),
810
+ });
811
+ }
887
812
  /**
888
813
  * Snapshots the application's current live-edit state as a commit.
889
814
  *
@@ -892,10 +817,9 @@ export class HttpSuperblocksServerClient {
892
817
  * editor's publish flow does.
893
818
  */
894
819
  async createCommit(input) {
895
- const branchSegment = input.branch
896
- ? `/branches/${encodeURIComponent(input.branch)}`
897
- : "";
898
- const data = await this.requestData(`/api/v2/applications/${input.applicationId}${branchSegment}/commit`, {
820
+ const data = await this.requestData(input.branch
821
+ ? PlatformMcpServerRequest.to("POST /api/v2/applications/*/branches/*/commit", input.applicationId, input.branch)
822
+ : PlatformMcpServerRequest.to("POST /api/v2/applications/*/commit", input.applicationId), {
899
823
  apiKey: input.apiKey,
900
824
  body: JSON.stringify({
901
825
  commitMessage: input.message,
@@ -903,9 +827,10 @@ export class HttpSuperblocksServerClient {
903
827
  // a commit racing an editor that has moved on since.
904
828
  lastSuccessfulWrite: Date.now(),
905
829
  }),
906
- method: "POST",
907
830
  ...(input.signal ? { signal: input.signal } : {}),
908
- ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
831
+ ...(input.timeoutMs !== undefined
832
+ ? { timeoutMs: input.timeoutMs }
833
+ : {}),
909
834
  });
910
835
  return {
911
836
  commitId: data.commitId,
@@ -923,20 +848,18 @@ export class HttpSuperblocksServerClient {
923
848
  * same content end up watching one build instead of racing two.
924
849
  */
925
850
  async createBuild(input) {
926
- await this.request(`/api/v3/applications/${input.applicationId}/${encodeURIComponent(input.directoryHash)}/builds`, {
851
+ await this.request(PlatformMcpServerRequest.to("POST /api/v3/applications/*/*/builds", input.applicationId, input.directoryHash), {
927
852
  apiKey: input.apiKey,
928
853
  body: JSON.stringify({ commitId: input.commitId }),
929
- method: "POST",
930
854
  });
931
855
  }
932
856
  async getBuildProgress(input) {
933
- const path = `/api/v3/applications/${input.applicationId}/build-bulk-status`;
857
+ const target = PlatformMcpServerRequest.to("POST /api/v3/applications/*/build-bulk-status", input.applicationId);
934
858
  // POST because the hashes go in the body, but it is a read: the server
935
859
  // asks SABS for status and changes nothing.
936
- const data = await this.requestData(path, {
860
+ const data = await this.requestData(target, {
937
861
  apiKey: input.apiKey,
938
862
  body: JSON.stringify({ directoryHashes: [input.directoryHash] }),
939
- method: "POST",
940
863
  operation: "build_progress",
941
864
  retry: true,
942
865
  ...(input.signal ? { signal: input.signal } : {}),
@@ -947,14 +870,13 @@ export class HttpSuperblocksServerClient {
947
870
  return buildProgress(data.directoryHashToStatus?.[input.directoryHash]?.status);
948
871
  }
949
872
  async deployApplication(input) {
950
- const path = `/api/v2/applications/${input.applicationId}/deployments`;
951
- const body = await this.request(path, {
873
+ const target = PlatformMcpServerRequest.to("POST /api/v2/applications/*/deployments", input.applicationId);
874
+ const body = await this.request(target, {
952
875
  apiKey: input.apiKey,
953
876
  // PostApplicationDeployRequestBody rejects unknown properties, so this
954
877
  // body carries the commit and nothing else — the branch is already
955
878
  // fixed by the commit being deployed.
956
879
  body: JSON.stringify({ commitId: input.commitId }),
957
- method: "POST",
958
880
  ...(input.signal ? { signal: input.signal } : {}),
959
881
  ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
960
882
  });
@@ -966,16 +888,15 @@ export class HttpSuperblocksServerClient {
966
888
  }
967
889
  // Neither shape. Guessing "it worked" here would report a publish that
968
890
  // never happened as a success.
969
- throw new Error(`Server ${path} returned neither a deployment nor a deploy status`);
891
+ throw new Error(`Server ${target.path} returned neither a deployment nor a deploy status`);
970
892
  }
971
893
  /**
972
894
  * Polls the deploy queue for a commit the UI watches after a queued publish.
973
895
  */
974
896
  async getDeployStatus(input) {
975
- const path = `/api/v2/applications/${input.applicationId}/deploy-status/${encodeURIComponent(input.commitId)}`;
976
- const body = await this.request(path, {
897
+ const target = PlatformMcpServerRequest.to("GET /api/v2/applications/*/deploy-status/*", input.applicationId, input.commitId);
898
+ const body = await this.request(target, {
977
899
  apiKey: input.apiKey,
978
- method: "GET",
979
900
  retry: false,
980
901
  ...(input.signal ? { signal: input.signal } : {}),
981
902
  ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
@@ -993,7 +914,7 @@ export class HttpSuperblocksServerClient {
993
914
  status,
994
915
  };
995
916
  }
996
- throw new Error(`Server ${path} returned an unknown deploy status`);
917
+ throw new Error(`Server ${target.path} returned an unknown deploy status`);
997
918
  }
998
919
  /**
999
920
  * Asks Clark for an app name, the same way the editor header does after a
@@ -1001,13 +922,12 @@ export class HttpSuperblocksServerClient {
1001
922
  */
1002
923
  async generateAppName(input) {
1003
924
  // Another bare body rather than the `{ responseMeta, data }` envelope.
1004
- const data = await this.request("/api/v1/ai/generate-app-name", {
925
+ const data = await this.request(PlatformMcpServerRequest.to("POST /api/v1/ai/generate-app-name"), {
1005
926
  apiKey: input.apiKey,
1006
927
  body: JSON.stringify({
1007
928
  applicationId: input.applicationId,
1008
929
  prompt: input.prompt,
1009
930
  }),
1010
- method: "POST",
1011
931
  });
1012
932
  if (!data?.name?.trim()) {
1013
933
  throw new Error("Server /api/v1/ai/generate-app-name returned no name");
@@ -1023,21 +943,19 @@ export class HttpSuperblocksServerClient {
1023
943
  * why nothing on a hot path should call it.
1024
944
  */
1025
945
  async getApplicationName(input) {
1026
- const body = await this.request("/api/v2/applications", {
946
+ const body = await this.request(PlatformMcpServerRequest.to("GET /api/v2/applications"), {
1027
947
  apiKey: input.apiKey,
1028
- method: "GET",
1029
948
  operation: "applications",
1030
949
  });
1031
950
  const found = body?.data?.applications?.find((application) => application?.id === input.applicationId);
1032
951
  return found?.name?.trim() || undefined;
1033
952
  }
1034
953
  async renameApplication(input) {
1035
- await this.request(`/api/v2/applications/${input.applicationId}/metadata`, {
954
+ await this.request(PlatformMcpServerRequest.to("PUT /api/v2/applications/*/metadata", input.applicationId), {
1036
955
  apiKey: input.apiKey,
1037
956
  // PutApplicationMetadataUpdateRequestBody accepts exactly one of name,
1038
957
  // folderId, or isPublic — sending more than one fails validation.
1039
958
  body: JSON.stringify({ name: input.name }),
1040
- method: "PUT",
1041
959
  });
1042
960
  }
1043
961
  }