@forklaunch/core 3.2.1 → 3.4.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 (59) hide show
  1. package/bin/gateway-mock/routes/payments-context.mjs +260 -0
  2. package/bin/gateway-mock/routes/payments.mjs +94 -1
  3. package/lib/agent/agent.d.ts +26 -0
  4. package/lib/agent/agent.d.ts.map +1 -0
  5. package/lib/agent/continuation.d.ts +29 -0
  6. package/lib/agent/continuation.d.ts.map +1 -0
  7. package/lib/agent/index.d.ts +8 -0
  8. package/lib/agent/index.d.ts.map +1 -0
  9. package/lib/agent/index.js +1049 -0
  10. package/lib/agent/index.js.map +1 -0
  11. package/lib/agent/index.mjs +1005 -0
  12. package/lib/agent/index.mjs.map +1 -0
  13. package/lib/agent/sources.d.ts +27 -0
  14. package/lib/agent/sources.d.ts.map +1 -0
  15. package/lib/agent/tools.d.ts +39 -0
  16. package/lib/agent/tools.d.ts.map +1 -0
  17. package/lib/agent/types.d.ts +130 -0
  18. package/lib/agent/types.d.ts.map +1 -0
  19. package/lib/http/bindRuntimeAuth.d.ts +53 -0
  20. package/lib/http/bindRuntimeAuth.d.ts.map +1 -0
  21. package/lib/http/bindRuntimeAuth.test.d.ts +2 -0
  22. package/lib/http/bindRuntimeAuth.test.d.ts.map +1 -0
  23. package/lib/http/index.d.ts +3 -0
  24. package/lib/http/index.d.ts.map +1 -1
  25. package/lib/http/index.js +487 -114
  26. package/lib/http/index.js.map +1 -1
  27. package/lib/http/index.mjs +484 -114
  28. package/lib/http/index.mjs.map +1 -1
  29. package/lib/http/localModelGatewayClient.d.ts +27 -0
  30. package/lib/http/localModelGatewayClient.d.ts.map +1 -0
  31. package/lib/http/mcpGenerator/mcpGenerator.d.ts.map +1 -1
  32. package/lib/http/middleware/internal/validationDiagnostic.d.ts +3 -0
  33. package/lib/http/middleware/internal/validationDiagnostic.d.ts.map +1 -0
  34. package/lib/http/middleware/request/auth.middleware.d.ts +0 -11
  35. package/lib/http/middleware/request/auth.middleware.d.ts.map +1 -1
  36. package/lib/http/middleware/request/parse.middleware.d.ts.map +1 -1
  37. package/lib/http/middleware/response/enrichExpressLikeSend.middleware.d.ts.map +1 -1
  38. package/lib/http/middleware/response/parse.middleware.d.ts.map +1 -1
  39. package/lib/http/modelGatewayClient.d.ts.map +1 -1
  40. package/lib/http/modelStream.d.ts +4 -0
  41. package/lib/http/modelStream.d.ts.map +1 -0
  42. package/lib/http/openApiV3Generator/openApiV3Generator.d.ts.map +1 -1
  43. package/lib/http/paymentsClient.d.ts +55 -1
  44. package/lib/http/paymentsClient.d.ts.map +1 -1
  45. package/lib/http/previewModelGatewayClient.d.ts +5 -1
  46. package/lib/http/previewModelGatewayClient.d.ts.map +1 -1
  47. package/lib/http/registerDelegatedSurface.d.ts +28 -0
  48. package/lib/http/registerDelegatedSurface.d.ts.map +1 -0
  49. package/lib/http/registerDelegatedSurface.test.d.ts +2 -0
  50. package/lib/http/registerDelegatedSurface.test.d.ts.map +1 -0
  51. package/lib/http/router/discriminateBody.d.ts +2 -1
  52. package/lib/http/router/discriminateBody.d.ts.map +1 -1
  53. package/lib/http/router/expressLikeRouter.d.ts.map +1 -1
  54. package/lib/ws/asyncApiV3Generator/asyncApiV3Generator.d.ts.map +1 -1
  55. package/lib/ws/index.js +94 -8
  56. package/lib/ws/index.js.map +1 -1
  57. package/lib/ws/index.mjs +94 -8
  58. package/lib/ws/index.mjs.map +1 -1
  59. package/package.json +12 -5
package/lib/http/index.js CHANGED
@@ -66,10 +66,12 @@ __export(http_exports, {
66
66
  auditComplianceAccess: () => auditComplianceAccess,
67
67
  auditEnabled: () => auditEnabled,
68
68
  auditEventType: () => auditEventType,
69
+ bindRuntimeAuth: () => bindRuntimeAuth,
69
70
  createContext: () => createContext,
70
71
  createEmailClient: () => createEmailClient,
71
72
  createHmacToken: () => createHmacToken,
72
73
  createInstanceGatewayTransport: () => createInstanceGatewayTransport,
74
+ createLocalModelGatewayClient: () => createLocalModelGatewayClient,
73
75
  createModelGatewayClient: () => createModelGatewayClient,
74
76
  createPaymentsClient: () => createPaymentsClient,
75
77
  createPreviewModelGatewayClient: () => createPreviewModelGatewayClient,
@@ -119,6 +121,7 @@ __export(http_exports, {
119
121
  put: () => put,
120
122
  recordMetric: () => recordMetric,
121
123
  recordRequestAudit: () => recordRequestAudit,
124
+ registerDelegatedSurface: () => registerDelegatedSurface,
122
125
  resetProcessHandlersForTesting: () => resetProcessHandlersForTesting,
123
126
  signPlatformEvent: () => signPlatformEvent,
124
127
  smsSegments: () => smsSegments,
@@ -134,6 +137,68 @@ __export(http_exports, {
134
137
  });
135
138
  module.exports = __toCommonJS(http_exports);
136
139
 
140
+ // src/http/bindRuntimeAuth.ts
141
+ function bindRuntimeAuth(details, binding) {
142
+ const policy = { ...details.auth };
143
+ const keys = Object.keys(binding);
144
+ const credentials = { ...binding };
145
+ const invalid = () => {
146
+ throw new Error("Invalid runtime authentication binding");
147
+ };
148
+ const allowedPolicy = policy.scheme === "jwt" ? [
149
+ "scheme",
150
+ "headerName",
151
+ "tokenPrefix",
152
+ "sessionSchema",
153
+ "allowedRoles",
154
+ "allowedPermissions"
155
+ ] : ["scheme", "headerName", "tokenPrefix"];
156
+ if (Object.keys(policy).some((key) => !allowedPolicy.includes(key)))
157
+ invalid();
158
+ for (const key of ["headerName", "tokenPrefix"]) {
159
+ if (policy[key] !== void 0 && (typeof policy[key] !== "string" || !policy[key]))
160
+ invalid();
161
+ }
162
+ if (policy.scheme === "jwt" && policy.allowedRoles === void 0 && policy.allowedPermissions === void 0)
163
+ invalid();
164
+ let authentication;
165
+ if (policy.scheme === "jwt") {
166
+ if (keys.some(
167
+ (key) => !["jwksPublicKeyUrl", "signatureKey", "decodeResource"].includes(key)
168
+ ))
169
+ invalid();
170
+ const keyFields = keys.filter((key) => key !== "decodeResource");
171
+ if (keyFields.length !== 1 || typeof credentials[keyFields[0]] !== "string" || !credentials[keyFields[0]])
172
+ invalid();
173
+ const decodeResource = credentials.decodeResource;
174
+ if (decodeResource !== void 0 && typeof decodeResource !== "function")
175
+ invalid();
176
+ delete credentials.decodeResource;
177
+ authentication = {
178
+ jwt: credentials,
179
+ ...decodeResource === void 0 ? {} : { decodeResource }
180
+ };
181
+ } else if (policy.scheme === "hmac") {
182
+ if (keys.length !== 1 || keys[0] !== "secretKeys" || !credentials.secretKeys || typeof credentials.secretKeys !== "object" || Array.isArray(credentials.secretKeys) || Object.keys(credentials.secretKeys).length === 0 || Object.values(credentials.secretKeys).some(
183
+ (key) => typeof key !== "string" || !key
184
+ ))
185
+ invalid();
186
+ authentication = { hmac: credentials };
187
+ } else {
188
+ return invalid();
189
+ }
190
+ delete policy.scheme;
191
+ for (const key of ["allowedRoles", "allowedPermissions"]) {
192
+ if (policy[key] !== void 0) {
193
+ const values = policy[key];
194
+ if (!Array.isArray(values) || values.some((value) => typeof value !== "string"))
195
+ invalid();
196
+ policy[key] = new Set(values);
197
+ }
198
+ }
199
+ return { ...details, auth: { ...policy, ...authentication } };
200
+ }
201
+
137
202
  // src/http/middleware/request/cors.middleware.ts
138
203
  var import_cors = __toESM(require("cors"));
139
204
  function cors(corsOptions) {
@@ -594,7 +659,7 @@ function createContext(schemaValidator) {
594
659
  }
595
660
 
596
661
  // src/http/router/discriminateBody.ts
597
- function discriminateBody(schemaValidator, body) {
662
+ function discriminateBody(schemaValidator, body, inputProjection) {
598
663
  if (body == null) {
599
664
  return void 0;
600
665
  }
@@ -644,7 +709,7 @@ function discriminateBody(schemaValidator, body) {
644
709
  parserType: "text",
645
710
  schema: maybeTypedBody
646
711
  };
647
- } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
712
+ } else if ((inputProjection ?? schemaValidator.openapi(maybeTypedBody)).format === "binary") {
648
713
  return {
649
714
  contentType: "application/octet-stream",
650
715
  parserType: "file",
@@ -1074,15 +1139,23 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
1074
1139
  if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
1075
1140
  return invalidAuthorizationTokenFormat;
1076
1141
  }
1077
- const verificationResult = await auth.verificationFunction({
1078
- method: req?.method ?? "",
1079
- path: req?.path ?? "",
1080
- body: req?._rawBody ?? req?.body,
1081
- timestamp: new Date(parsedTimestamp),
1082
- nonce: parsedNonce,
1083
- signature: parsedSignature,
1084
- secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
1085
- });
1142
+ let verificationResult = false;
1143
+ const signedPaths = hmacSignedPaths(req);
1144
+ for (const path of signedPaths) {
1145
+ verificationResult = Boolean(await auth.verificationFunction({
1146
+ method: req?.method ?? "",
1147
+ path,
1148
+ body: req?._rawBody ?? req?.body,
1149
+ timestamp: new Date(parsedTimestamp),
1150
+ nonce: parsedNonce,
1151
+ signature: parsedSignature,
1152
+ secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
1153
+ }));
1154
+ if (verificationResult) {
1155
+ if (path !== signedPaths[0]) warnMountRelativeHmacPath();
1156
+ break;
1157
+ }
1158
+ }
1086
1159
  if (!verificationResult) {
1087
1160
  return invalidAuthorizationSignature;
1088
1161
  }
@@ -1249,6 +1322,25 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
1249
1322
  }
1250
1323
  }
1251
1324
  }
1325
+ function acceptMountRelativeHmacPaths() {
1326
+ const value = typeof process === "undefined" ? void 0 : process.env?.HMAC_ACCEPT_MOUNT_RELATIVE_PATHS;
1327
+ return value == null || !["false", "0"].includes(value.trim().toLowerCase());
1328
+ }
1329
+ var warnedMountRelativeHmacPath = false;
1330
+ function warnMountRelativeHmacPath() {
1331
+ if (warnedMountRelativeHmacPath) return;
1332
+ warnedMountRelativeHmacPath = true;
1333
+ console.warn(
1334
+ "Accepted an HMAC signature over the router-relative path. Sign the full request path instead; set HMAC_ACCEPT_MOUNT_RELATIVE_PATHS=false to reject the deprecated form."
1335
+ );
1336
+ }
1337
+ function hmacSignedPaths(req) {
1338
+ const relative = req?.path ?? "";
1339
+ const wire = (req?.originalUrl ?? req?.url ?? relative).split("?")[0] || relative;
1340
+ const paths = [wire];
1341
+ if (relative !== wire && acceptMountRelativeHmacPaths()) paths.push(relative);
1342
+ return paths;
1343
+ }
1252
1344
  async function parseRequestAuth(req, res, next) {
1253
1345
  const access = req.contractDetails.access;
1254
1346
  if (access === "public") {
@@ -1482,6 +1574,21 @@ function isRequestShape(maybeResponseShape) {
1482
1574
  return maybeResponseShape != null && ("body" in maybeResponseShape || "query" in maybeResponseShape || "params" in maybeResponseShape || "headers" in maybeResponseShape);
1483
1575
  }
1484
1576
 
1577
+ // src/http/middleware/internal/validationDiagnostic.ts
1578
+ var sendingDiagnostics = /* @__PURE__ */ new WeakSet();
1579
+ function isValidationDiagnostic(response) {
1580
+ return sendingDiagnostics.has(response);
1581
+ }
1582
+ function sendValidationDiagnostic(response, send) {
1583
+ const alreadySending = sendingDiagnostics.has(response);
1584
+ sendingDiagnostics.add(response);
1585
+ try {
1586
+ return send();
1587
+ } finally {
1588
+ if (!alreadySending) sendingDiagnostics.delete(response);
1589
+ }
1590
+ }
1591
+
1485
1592
  // src/http/middleware/request/parse.middleware.ts
1486
1593
  function parse(req, res, next) {
1487
1594
  const globalOptions = req._globalOptions?.();
@@ -1563,14 +1670,13 @@ function parse(req, res, next) {
1563
1670
  default:
1564
1671
  case void 0:
1565
1672
  case "error":
1566
- res.type("application/json");
1673
+ res.type("text/plain");
1567
1674
  res.status(400);
1568
1675
  if (hasSend(res)) {
1569
- res.send(
1570
- `${collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
1676
+ const diagnostic = `${collectedParseErrors ?? (0, import_validator2.prettyPrintParseErrors)(parsedRequest.errors, "Request")}
1571
1677
 
1572
- Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
1573
- );
1678
+ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`;
1679
+ sendValidationDiagnostic(res, () => res.send(diagnostic));
1574
1680
  } else {
1575
1681
  next?.(new Error("Request is not sendable."));
1576
1682
  }
@@ -1979,13 +2085,25 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1979
2085
  span: void 0
1980
2086
  },
1981
2087
  _rawBody: void 0,
1982
- _parsedVersions: void 0
2088
+ _parsedVersions: void 0,
2089
+ // hyper-express's content parser reads the body through its Request's
2090
+ // body readers; serve the body the call was given.
2091
+ buffer: async () => Buffer.from(
2092
+ typeof req.body === "string" ? req.body : JSON.stringify(req.body)
2093
+ ),
2094
+ json: async () => req.body,
2095
+ text: async () => typeof req.body === "string" ? req.body : JSON.stringify(req.body)
1983
2096
  };
1984
2097
  const res = {
1985
2098
  statusCode: 200,
2099
+ // hyper-express's response enrichment reads the status and headers
2100
+ // from these fields, which its own Response keeps.
2101
+ _status_code: 200,
2102
+ _headers: responseHeaders,
1986
2103
  status: (code) => {
1987
2104
  statusCode = code;
1988
2105
  res.statusCode = code;
2106
+ res._status_code = code;
1989
2107
  return res;
1990
2108
  },
1991
2109
  send: (message) => {
@@ -2005,6 +2123,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
2005
2123
  return res;
2006
2124
  },
2007
2125
  getHeader: (key) => responseHeaders[key],
2126
+ getHeaders: () => ({ ...responseHeaders }),
2127
+ locals: {},
2008
2128
  type: (contentType) => {
2009
2129
  responseHeaders["content-type"] = contentType;
2010
2130
  return res;
@@ -2023,31 +2143,59 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
2023
2143
  version
2024
2144
  };
2025
2145
  const executeMiddlewares = request?.executeMiddlewares ?? false;
2146
+ const cHandler = controllerHandler;
2026
2147
  if (executeMiddlewares && middlewares.length > 0) {
2027
- const allHandlers = [...middlewares];
2028
- let cursor = allHandlers.shift();
2029
- if (cursor) {
2030
- for (const fn of allHandlers) {
2031
- await cursor(req, res, (err) => {
2032
- if (err) {
2033
- throw err;
2148
+ const chain = middlewares;
2149
+ await new Promise((resolve, reject) => {
2150
+ let settled = false;
2151
+ let responded = false;
2152
+ let controllerStarted = false;
2153
+ const finish = (error) => {
2154
+ if (settled) return;
2155
+ settled = true;
2156
+ if (error) reject(error);
2157
+ else resolve();
2158
+ };
2159
+ res.on("finish", () => {
2160
+ responded = true;
2161
+ if (!controllerStarted) finish();
2162
+ });
2163
+ const run = async (index) => {
2164
+ try {
2165
+ if (index === chain.length) {
2166
+ controllerStarted = true;
2167
+ await cHandler(req, res, (err) => {
2168
+ if (err) {
2169
+ throw err;
2170
+ }
2171
+ });
2172
+ finish();
2173
+ return;
2034
2174
  }
2035
- cursor = fn;
2036
- });
2037
- }
2038
- await cursor(req, res, async (err) => {
2039
- if (err) {
2040
- throw err;
2175
+ let calledNext = false;
2176
+ await chain[index](req, res, (err) => {
2177
+ calledNext = true;
2178
+ if (err) {
2179
+ throw err;
2180
+ }
2181
+ return run(index + 1);
2182
+ });
2183
+ if (!calledNext && chain[index].length < 3 && !responded) {
2184
+ await run(index + 1);
2185
+ }
2186
+ } catch (error) {
2187
+ finish(error);
2041
2188
  }
2042
- });
2043
- }
2189
+ };
2190
+ void run(0);
2191
+ });
2192
+ } else {
2193
+ await cHandler(req, res, (err) => {
2194
+ if (err) {
2195
+ throw err;
2196
+ }
2197
+ });
2044
2198
  }
2045
- const cHandler = controllerHandler;
2046
- await cHandler(req, res, (err) => {
2047
- if (err) {
2048
- throw err;
2049
- }
2050
- });
2051
2199
  return {
2052
2200
  code: Number(statusCode),
2053
2201
  response: responseMessage,
@@ -2831,12 +2979,55 @@ function signPlatformEvent(params) {
2831
2979
  });
2832
2980
  }
2833
2981
 
2982
+ // src/http/modelStream.ts
2983
+ async function* readModelStream(response) {
2984
+ const reader = response.body?.getReader();
2985
+ if (!reader)
2986
+ throw new ModelGatewayRequestError(502, "The model stream is empty.");
2987
+ const decoder = new TextDecoder();
2988
+ let buffer = "", bytes = 0;
2989
+ try {
2990
+ for (; ; ) {
2991
+ const part = await reader.read();
2992
+ if (part.done) break;
2993
+ bytes += part.value.byteLength;
2994
+ if (bytes > 512e3) throw new Error("oversized");
2995
+ buffer += decoder.decode(part.value, { stream: true });
2996
+ let match;
2997
+ while (match = buffer.match(/\r?\n\r?\n/)) {
2998
+ const event = buffer.slice(0, match.index);
2999
+ buffer = buffer.slice(match.index + match[0].length);
3000
+ const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
3001
+ if (!data) continue;
3002
+ if (data === "[DONE]") return;
3003
+ const value = JSON.parse(data);
3004
+ if (value.error || !Array.isArray(value.choices))
3005
+ throw new Error("invalid");
3006
+ yield value;
3007
+ }
3008
+ }
3009
+ throw new Error("interrupted");
3010
+ } catch {
3011
+ throw new ModelGatewayRequestError(
3012
+ 502,
3013
+ "The model stream was interrupted. No automatic retry was sent."
3014
+ );
3015
+ } finally {
3016
+ await reader.cancel().catch(() => {
3017
+ });
3018
+ reader.releaseLock();
3019
+ }
3020
+ }
3021
+
2834
3022
  // src/http/modelGatewayClient.ts
2835
3023
  var ModelGatewayRequestError = class extends InstanceGatewayRequestError {
2836
3024
  name = "ModelGatewayRequestError";
2837
3025
  };
2838
3026
  function createModelGatewayClient(options2 = {}) {
2839
- const transport = createInstanceGatewayTransport(options2, "The model gateway");
3027
+ const transport = createInstanceGatewayTransport(
3028
+ options2,
3029
+ "The model gateway"
3030
+ );
2840
3031
  async function send(method, route, body, signal) {
2841
3032
  try {
2842
3033
  return await transport.request(method, route, body, { signal });
@@ -2872,42 +3063,12 @@ function createModelGatewayClient(options2 = {}) {
2872
3063
  request,
2873
3064
  streamOptions.signal
2874
3065
  );
2875
- if (!response.body) return;
2876
- for await (const data of sseData(response.body)) {
2877
- if (data === "[DONE]") return;
2878
- try {
2879
- yield JSON.parse(data);
2880
- } catch {
2881
- }
2882
- }
3066
+ yield* readModelStream(response);
2883
3067
  }
2884
3068
  }
2885
3069
  }
2886
3070
  };
2887
3071
  }
2888
- async function* sseData(stream) {
2889
- const decoder = new TextDecoder();
2890
- let buffer = "";
2891
- const reader = stream.getReader();
2892
- try {
2893
- for (; ; ) {
2894
- const { value, done } = await reader.read();
2895
- if (done) break;
2896
- buffer += decoder.decode(value, { stream: true });
2897
- let boundary;
2898
- while ((boundary = buffer.search(/\r?\n\r?\n/)) !== -1) {
2899
- const event = buffer.slice(0, boundary);
2900
- buffer = buffer.slice(boundary).replace(/^\r?\n\r?\n/, "");
2901
- const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
2902
- if (data) yield data;
2903
- }
2904
- }
2905
- const tail = buffer.trim();
2906
- if (tail.startsWith("data:")) yield tail.slice(5).trimStart();
2907
- } finally {
2908
- reader.releaseLock();
2909
- }
2910
- }
2911
3072
 
2912
3073
  // src/http/previewModelGatewayClient.ts
2913
3074
  function record(value) {
@@ -2963,9 +3124,9 @@ function createPreviewModelGatewayClient(options2 = {}) {
2963
3124
  options2,
2964
3125
  "The local preview model gateway"
2965
3126
  );
2966
- async function send(method, route, body) {
3127
+ async function send(method, route, body, signal) {
2967
3128
  try {
2968
- return await transport.request(method, route, body);
3129
+ return await transport.request(method, route, body, { signal });
2969
3130
  } catch (error) {
2970
3131
  if (error instanceof InstanceGatewayRequestError)
2971
3132
  throw new ModelGatewayRequestError(
@@ -2983,6 +3144,15 @@ function createPreviewModelGatewayClient(options2 = {}) {
2983
3144
  },
2984
3145
  chat: {
2985
3146
  completions: {
3147
+ async *stream(request, options3 = {}) {
3148
+ const response = await send(
3149
+ "POST",
3150
+ "/models/chat/completions",
3151
+ { ...request, stream: true },
3152
+ options3.signal
3153
+ );
3154
+ yield* readModelStream(response);
3155
+ },
2986
3156
  async create(request) {
2987
3157
  return await (await send(
2988
3158
  "POST",
@@ -3517,6 +3687,19 @@ function createPaymentsClient(options2 = {}) {
3517
3687
  },
3518
3688
  status() {
3519
3689
  return call("GET", "/payments/status");
3690
+ },
3691
+ payouts(params = {}) {
3692
+ const { limit } = params;
3693
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 50)) {
3694
+ throw new TypeError("limit must be a whole number from 1 to 50");
3695
+ }
3696
+ return call(
3697
+ "GET",
3698
+ limit === void 0 ? "/payments/payouts" : `/payments/payouts?limit=${limit}`
3699
+ );
3700
+ },
3701
+ dashboardLink() {
3702
+ return call("POST", "/payments/dashboard-link", {});
3520
3703
  }
3521
3704
  };
3522
3705
  }
@@ -3536,7 +3719,7 @@ function loadStripe() {
3536
3719
  function stripeGatewayFetch(settings, fetchImpl = fetch) {
3537
3720
  const base = new URL(settings.gatewayUrl);
3538
3721
  const basePath = base.pathname.replace(/\/+$/, "");
3539
- return async (input, init = {}) => {
3722
+ const signedFetch = async (input, init = {}) => {
3540
3723
  const original = new URL(
3541
3724
  typeof input === "string" || input instanceof URL ? input : input.url
3542
3725
  );
@@ -3576,6 +3759,13 @@ function stripeGatewayFetch(settings, fetchImpl = fetch) {
3576
3759
  }
3577
3760
  );
3578
3761
  };
3762
+ const preconnect = Reflect.get(fetchImpl, "preconnect");
3763
+ if (typeof preconnect === "function") {
3764
+ Object.defineProperty(signedFetch, "preconnect", {
3765
+ value: (_input, options2) => Reflect.apply(preconnect, fetchImpl, [base.origin, options2])
3766
+ });
3767
+ }
3768
+ return signedFetch;
3579
3769
  }
3580
3770
  function createStripeClient(options2 = {}) {
3581
3771
  const StripeClass = options2.Stripe ?? loadStripe();
@@ -3594,8 +3784,8 @@ function createStripeClient(options2 = {}) {
3594
3784
  return new StripeClass(options2.apiKey, options2.config);
3595
3785
  }
3596
3786
  const settings = instanceGatewaySettings(options2, "Payments");
3597
- const gateway = new URL(settings.gatewayUrl);
3598
- const protocol = gateway.protocol === "http:" ? "http" : "https";
3787
+ const gateway2 = new URL(settings.gatewayUrl);
3788
+ const protocol = gateway2.protocol === "http:" ? "http" : "https";
3599
3789
  return new StripeClass(
3600
3790
  // No key: the authenticator below replaces it, and the gateway holds the
3601
3791
  // real one. (stripe-node accepts an authenticator in place of a key.)
@@ -3603,8 +3793,8 @@ function createStripeClient(options2 = {}) {
3603
3793
  {
3604
3794
  maxNetworkRetries: 2,
3605
3795
  ...options2.config,
3606
- host: gateway.hostname,
3607
- port: gateway.port || (protocol === "http" ? 80 : 443),
3796
+ host: gateway2.hostname,
3797
+ port: gateway2.port || (protocol === "http" ? 80 : 443),
3608
3798
  protocol,
3609
3799
  telemetry: false,
3610
3800
  // The signed fetch sets `authorization`; nothing else may.
@@ -4808,10 +4998,10 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4808
4998
  let bodySchema;
4809
4999
  let responsesSchemas;
4810
5000
  if (route.contractDetails.versions) {
4811
- const version2 = route.contractDetails.versions[index];
5001
+ const version2 = Object.values(route.contractDetails.versions)[index];
5002
+ responsesSchemas = version2.responses;
4812
5003
  if (version2.body && schemaValidator.parse(inputSchema, args).ok) {
4813
5004
  bodySchema = version2.body;
4814
- responsesSchemas = version2.responses;
4815
5005
  }
4816
5006
  } else {
4817
5007
  bodySchema = route.contractDetails.body;
@@ -4822,7 +5012,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4822
5012
  if (discriminatedBody) {
4823
5013
  switch (discriminatedBody.parserType) {
4824
5014
  case "json": {
4825
- parsedBody = (0, import_common13.safeStringify)(body);
5015
+ parsedBody = body === void 0 ? void 0 : JSON.stringify(body);
4826
5016
  break;
4827
5017
  }
4828
5018
  case "text": {
@@ -4888,11 +5078,11 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4888
5078
  ).toString();
4889
5079
  url += queryString ? `?${queryString}` : "";
4890
5080
  }
4891
- const response = await fetch(encodeURI(url), {
5081
+ const response = await fetch(url, {
4892
5082
  method: route.method.toUpperCase(),
4893
5083
  headers: {
4894
5084
  ...headers,
4895
- ...discriminatedBody?.contentType != "multipart/form-data" ? {
5085
+ ...discriminatedBody?.contentType != "multipart/form-data" && (contentType ?? discriminatedBody?.contentType) !== void 0 ? {
4896
5086
  "Content-Type": contentType ?? discriminatedBody?.contentType
4897
5087
  } : {}
4898
5088
  },
@@ -4970,7 +5160,12 @@ function isResponseCompiledSchema(schema) {
4970
5160
  }
4971
5161
 
4972
5162
  // src/http/middleware/response/parse.middleware.ts
5163
+ var sendingValidationError = /* @__PURE__ */ new WeakSet();
4973
5164
  function parse2(req, res, next) {
5165
+ if (sendingValidationError.has(res)) {
5166
+ next?.();
5167
+ return;
5168
+ }
4974
5169
  let headers;
4975
5170
  let responses;
4976
5171
  const globalOptions = req._globalOptions?.();
@@ -5013,15 +5208,35 @@ function parse2(req, res, next) {
5013
5208
  responses = responseSchemas.responses;
5014
5209
  }
5015
5210
  const statusCode = Number(res.statusCode);
5016
- const parsedResponse = schemaValidator.parse(
5017
- [400, 401, 404, 403, 500].includes(statusCode) ? schemaValidator.union([schemaValidator.string, responses?.[statusCode]]) : responses?.[statusCode],
5018
- res.bodyData
5019
- );
5211
+ const defaultErrorStatus = [400, 401, 404, 403, 500].includes(statusCode);
5212
+ const missingResponseSchema = !defaultErrorStatus && !Object.prototype.hasOwnProperty.call(responses, statusCode);
5213
+ if (missingResponseSchema && collapsedOptions !== "warning") {
5214
+ throw new Error(`Undocumented response status ${statusCode}`);
5215
+ }
5216
+ const declaredResponse = responses?.[statusCode];
5217
+ const defaultErrorResponse = () => {
5218
+ try {
5219
+ return schemaValidator.parse(
5220
+ schemaValidator.union([schemaValidator.string, declaredResponse]),
5221
+ res.bodyData
5222
+ );
5223
+ } catch (error) {
5224
+ if (declaredResponse === void 0) throw error;
5225
+ const declared = schemaValidator.parse(declaredResponse, res.bodyData);
5226
+ if (declared.ok) return declared;
5227
+ const message = schemaValidator.parse(
5228
+ schemaValidator.string,
5229
+ res.bodyData
5230
+ );
5231
+ return message.ok ? message : declared;
5232
+ }
5233
+ };
5234
+ const parsedResponse = missingResponseSchema ? { ok: true } : defaultErrorStatus ? defaultErrorResponse() : schemaValidator.parse(declaredResponse, res.bodyData);
5020
5235
  const parsedHeaders = schemaValidator.parse(
5021
5236
  headers ?? schemaValidator.unknown,
5022
5237
  res.getHeaders()
5023
5238
  );
5024
- const parseErrors = [];
5239
+ const parseErrors = missingResponseSchema ? [`Response: Undocumented response status ${statusCode}`] : [];
5025
5240
  if (!parsedHeaders.ok) {
5026
5241
  const headerErrors = (0, import_validator3.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
5027
5242
  if (headerErrors) {
@@ -5038,21 +5253,26 @@ function parse2(req, res, next) {
5038
5253
  }
5039
5254
  }
5040
5255
  if (parseErrors.length > 0) {
5041
- switch (req.contractDetails.options?.responseValidation) {
5256
+ switch (collapsedOptions) {
5042
5257
  default:
5043
5258
  case void 0:
5044
5259
  case "error":
5045
5260
  res.type("text/plain");
5046
5261
  res.status(500);
5047
5262
  if (hasSend(res)) {
5048
- res.send(
5049
- `Invalid response:
5263
+ sendingValidationError.add(res);
5264
+ try {
5265
+ res.send(
5266
+ `Invalid response:
5050
5267
  ${parseErrors.join(
5051
- "\n\n"
5052
- )}
5268
+ "\n\n"
5269
+ )}
5053
5270
 
5054
5271
  Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
5055
- );
5272
+ );
5273
+ } finally {
5274
+ sendingValidationError.delete(res);
5275
+ }
5056
5276
  } else {
5057
5277
  next?.(new Error("Response is not sendable."));
5058
5278
  }
@@ -5063,8 +5283,6 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
5063
5283
  ${parseErrors.join("\n\n")}`
5064
5284
  );
5065
5285
  break;
5066
- case "none":
5067
- break;
5068
5286
  }
5069
5287
  }
5070
5288
  next?.();
@@ -5106,6 +5324,10 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5106
5324
  originalSend.call(instance, data);
5107
5325
  return;
5108
5326
  }
5327
+ if (typeof data === "string" && isValidationDiagnostic(res)) {
5328
+ originalSend.call(instance, data);
5329
+ return;
5330
+ }
5109
5331
  let responses;
5110
5332
  if (req.contractDetails.responses == null && (req.contractDetails.versions == null || Object.values(req.contractDetails.versions).some(
5111
5333
  (version) => version?.responses == null
@@ -5115,7 +5337,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5115
5337
  if (req.contractDetails.responses != null) {
5116
5338
  responses = req.contractDetails.responses;
5117
5339
  } else {
5118
- responses = req.contractDetails.versions[req.version].responses;
5340
+ responses = req.contractDetails.versions[req.version]?.responses ?? {};
5119
5341
  }
5120
5342
  }
5121
5343
  const responseBodies = discriminateResponseBodies(
@@ -5337,7 +5559,16 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5337
5559
  for (const key in discriminatedResponseBodiesResult) {
5338
5560
  coercedResponses[key] = {
5339
5561
  description: httpStatusCodes_default[key],
5340
- content: contentResolver(
5562
+ // Undefined/void represent no response body, not a JSON null payload.
5563
+ // Check the schema kind rather than its OpenAPI projection: null and
5564
+ // nullish also project to null but still admit an actual JSON body.
5565
+ content: schemaValidator.undefined !== void 0 && typedSchemaValidator.isInstanceOf(
5566
+ discriminatedResponseBodiesResult[key].schema,
5567
+ schemaValidator.undefined
5568
+ ) || schemaValidator.void !== void 0 && typedSchemaValidator.isInstanceOf(
5569
+ discriminatedResponseBodiesResult[key].schema,
5570
+ schemaValidator.void
5571
+ ) ? void 0 : contentResolver(
5341
5572
  schemaValidator,
5342
5573
  discriminatedResponseBodiesResult[key].schema,
5343
5574
  discriminatedResponseBodiesResult[key].contentType
@@ -5373,15 +5604,21 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5373
5604
  operationObject.parameters?.push({
5374
5605
  name: key,
5375
5606
  in: "path",
5607
+ required: true,
5376
5608
  schema: typedSchemaValidator.openapi(params[key])
5377
5609
  });
5378
5610
  }
5379
5611
  }
5380
- const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body) : null;
5612
+ const adapter = schemaValidator;
5613
+ const bareProjection = body && adapter.isSchema(body) ? adapter.openapiRequestBody?.(body) : void 0;
5614
+ const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body, bareProjection?.schema) : null;
5381
5615
  if (discriminatedBodyResult) {
5616
+ const projection = bareProjection ?? adapter.openapiRequestBody?.(discriminatedBodyResult.schema);
5382
5617
  operationObject.requestBody = {
5383
- required: true,
5384
- content: contentResolver(
5618
+ required: projection?.required ?? true,
5619
+ content: projection ? {
5620
+ [discriminatedBodyResult.contentType]: { schema: projection.schema }
5621
+ } : contentResolver(
5385
5622
  schemaValidator,
5386
5623
  discriminatedBodyResult.schema,
5387
5624
  discriminatedBodyResult.contentType
@@ -5416,25 +5653,14 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5416
5653
  content: contentResolver(schemaValidator, schemaValidator.string)
5417
5654
  };
5418
5655
  if ("basic" in auth) {
5419
- operationObject.security = [
5420
- {
5421
- basic: Array.from(
5422
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5423
- )
5424
- }
5425
- ];
5656
+ operationObject.security = [{ basic: [] }];
5426
5657
  securitySchemes["basic"] = {
5427
5658
  type: "http",
5428
5659
  scheme: "basic"
5429
5660
  };
5430
5661
  } else if (auth) {
5431
- operationObject.security = [
5432
- {
5433
- [auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
5434
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5435
- )
5436
- }
5437
- ];
5662
+ const schemeName = auth.headerName || "Authorization";
5663
+ operationObject.security = [{ [schemeName]: [] }];
5438
5664
  if (auth.headerName && auth.headerName !== "Authorization") {
5439
5665
  securitySchemes[auth.headerName] = {
5440
5666
  type: "apiKey",
@@ -5665,6 +5891,150 @@ var RateLimiter = class {
5665
5891
  return READ_METHODS.has(method.toUpperCase()) ? "read" : "write";
5666
5892
  }
5667
5893
  };
5894
+
5895
+ // src/http/registerDelegatedSurface.ts
5896
+ function registerDelegatedSurface(surface, mount, register) {
5897
+ if (!surface.id || !surface.provider || surface.coverage !== "provider-owned" || !surface.client.package || !surface.client.export || surface.runtime.basePath !== "runtime-resolved" || typeof surface.runtime.openApi !== "boolean" || !Array.isArray(surface.callbacks) || surface.callbacks.some(
5898
+ (callback) => typeof callback !== "string" || !callback.startsWith("/")
5899
+ )) {
5900
+ throw new Error("Invalid delegated HTTP surface descriptor");
5901
+ }
5902
+ const paths = Array.isArray(mount.paths) ? mount.paths : [mount.paths];
5903
+ if (mount.basePath !== "" && !mount.basePath.startsWith("/") || paths.length === 0 || paths.some(
5904
+ (path) => !path.startsWith("/") || mount.basePath !== "" && !path.startsWith(mount.basePath + "/")
5905
+ )) {
5906
+ throw new Error("Invalid delegated HTTP surface mount");
5907
+ }
5908
+ return register(mount);
5909
+ }
5910
+
5911
+ // src/http/localModelGatewayClient.ts
5912
+ var import_node_child_process = require("child_process");
5913
+ var import_node_util = require("util");
5914
+ var execute = (0, import_node_util.promisify)(import_node_child_process.execFile);
5915
+ var gateway = "https://studio-orchestrator.forklaunch.com/studio-orchestrator/desktop-runtime-model-gateway";
5916
+ function validate(value) {
5917
+ if (!value || !/^[a-f0-9]{64}$/.test(value.grantId) || !/^[a-f0-9]{64}$/.test(value.capability) || value.gatewayBaseUrl !== `${gateway}/${value.grantId}/v1` || value.scope !== "local-runtime" || !Number.isSafeInteger(value.expiresAt) || value.expiresAt <= Date.now() || value.expiresAt > Date.now() + 9e5 || !/^[A-Za-z0-9._-]{1,128}$/.test(value.model) || !Number.isInteger(value.maxCalls) || value.maxCalls < 1 || value.maxCalls > 20 || !Number.isInteger(value.maxUnits) || value.maxUnits < 1 || value.maxUnits > 1e5) {
5918
+ throw new Error(
5919
+ "Invalid local model connection. No model request was sent."
5920
+ );
5921
+ }
5922
+ return value;
5923
+ }
5924
+ function createLocalModelGatewayClient(options2 = {}) {
5925
+ if (process.env.FORKLAUNCH_API_KEY === void 0 && !options2.credentialProvider && process.env.FORKLAUNCH_LOCAL_MODEL_RELAY === "1") {
5926
+ return createPreviewModelGatewayClient({ fetch: options2.fetch });
5927
+ }
5928
+ const applicationId = options2.applicationId ?? process.env.FORKLAUNCH_APPLICATION_ID;
5929
+ const model = options2.model ?? process.env.AGENT_MODEL;
5930
+ if (applicationId && !/^[a-f0-9-]{36}$/i.test(applicationId)) {
5931
+ throw new Error(
5932
+ "FORKLAUNCH_APPLICATION_ID must be a saved application UUID."
5933
+ );
5934
+ }
5935
+ const provider = options2.credentialProvider ?? (async () => {
5936
+ const args = ["model", "credential", "--json"];
5937
+ if (applicationId) args.push("--application-id", applicationId);
5938
+ if (model) args.push("--model", model);
5939
+ try {
5940
+ const { stdout } = await execute("forklaunch", args, {
5941
+ timeout: 35e3,
5942
+ maxBuffer: 65536,
5943
+ windowsHide: true
5944
+ });
5945
+ return JSON.parse(stdout);
5946
+ } catch {
5947
+ throw new Error(
5948
+ "Could not connect the local agent. Run forklaunch login, check app access, and use a CLI that supports model credential."
5949
+ );
5950
+ }
5951
+ });
5952
+ const optionsFetch = options2.fetch ?? fetch;
5953
+ let pending;
5954
+ let credential;
5955
+ async function connection() {
5956
+ if (credential && credential.expiresAt > Date.now()) return credential;
5957
+ pending ??= provider().then(validate).then((value) => credential = value).finally(() => {
5958
+ pending = void 0;
5959
+ });
5960
+ return pending;
5961
+ }
5962
+ return {
5963
+ async models() {
5964
+ const c = await connection();
5965
+ return {
5966
+ models: [{ id: c.model }],
5967
+ preview: {
5968
+ expiresAt: c.expiresAt,
5969
+ maxCalls: c.maxCalls,
5970
+ maxUnits: c.maxUnits
5971
+ }
5972
+ };
5973
+ },
5974
+ chat: {
5975
+ completions: {
5976
+ async *stream(request, options3 = {}) {
5977
+ const c = await connection();
5978
+ if (request.model !== c.model)
5979
+ throw new ModelGatewayRequestError(
5980
+ 400,
5981
+ "Choose the model authorized for this local session."
5982
+ );
5983
+ const response = await optionsFetch(
5984
+ `${c.gatewayBaseUrl}/chat/completions`,
5985
+ {
5986
+ method: "POST",
5987
+ redirect: "error",
5988
+ signal: options3.signal ? AbortSignal.any([options3.signal, AbortSignal.timeout(6e4)]) : AbortSignal.timeout(6e4),
5989
+ headers: {
5990
+ authorization: `Bearer ${c.capability}`,
5991
+ "content-type": "application/json"
5992
+ },
5993
+ body: JSON.stringify({ ...request, stream: true })
5994
+ }
5995
+ );
5996
+ if (!response.ok) {
5997
+ await response.body?.cancel();
5998
+ throw new ModelGatewayRequestError(
5999
+ response.status,
6000
+ "The local model stream was refused. No automatic retry was sent."
6001
+ );
6002
+ }
6003
+ yield* readModelStream(response);
6004
+ },
6005
+ async create(request) {
6006
+ const c = await connection();
6007
+ if (request.model !== c.model)
6008
+ throw new ModelGatewayRequestError(
6009
+ 400,
6010
+ "Choose the model authorized for this local session."
6011
+ );
6012
+ const response = await (options2.fetch ?? fetch)(
6013
+ `${c.gatewayBaseUrl}/chat/completions`,
6014
+ {
6015
+ method: "POST",
6016
+ redirect: "error",
6017
+ signal: AbortSignal.timeout(6e4),
6018
+ headers: {
6019
+ authorization: `Bearer ${c.capability}`,
6020
+ "content-type": "application/json"
6021
+ },
6022
+ body: JSON.stringify({ ...request, stream: false })
6023
+ }
6024
+ );
6025
+ if (!response.ok) {
6026
+ await response.body?.cancel();
6027
+ throw new ModelGatewayRequestError(
6028
+ response.status,
6029
+ "The local model request was refused or failed. No automatic retry was sent."
6030
+ );
6031
+ }
6032
+ return response.json();
6033
+ }
6034
+ }
6035
+ }
6036
+ };
6037
+ }
5668
6038
  // Annotate the CommonJS export names for ESM import in node:
5669
6039
  0 && (module.exports = {
5670
6040
  ATTR_API_NAME,
@@ -5703,10 +6073,12 @@ var RateLimiter = class {
5703
6073
  auditComplianceAccess,
5704
6074
  auditEnabled,
5705
6075
  auditEventType,
6076
+ bindRuntimeAuth,
5706
6077
  createContext,
5707
6078
  createEmailClient,
5708
6079
  createHmacToken,
5709
6080
  createInstanceGatewayTransport,
6081
+ createLocalModelGatewayClient,
5710
6082
  createModelGatewayClient,
5711
6083
  createPaymentsClient,
5712
6084
  createPreviewModelGatewayClient,
@@ -5756,6 +6128,7 @@ var RateLimiter = class {
5756
6128
  put,
5757
6129
  recordMetric,
5758
6130
  recordRequestAudit,
6131
+ registerDelegatedSurface,
5759
6132
  resetProcessHandlersForTesting,
5760
6133
  signPlatformEvent,
5761
6134
  smsSegments,