@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
@@ -1,3 +1,65 @@
1
+ // src/http/bindRuntimeAuth.ts
2
+ function bindRuntimeAuth(details, binding) {
3
+ const policy = { ...details.auth };
4
+ const keys = Object.keys(binding);
5
+ const credentials = { ...binding };
6
+ const invalid = () => {
7
+ throw new Error("Invalid runtime authentication binding");
8
+ };
9
+ const allowedPolicy = policy.scheme === "jwt" ? [
10
+ "scheme",
11
+ "headerName",
12
+ "tokenPrefix",
13
+ "sessionSchema",
14
+ "allowedRoles",
15
+ "allowedPermissions"
16
+ ] : ["scheme", "headerName", "tokenPrefix"];
17
+ if (Object.keys(policy).some((key) => !allowedPolicy.includes(key)))
18
+ invalid();
19
+ for (const key of ["headerName", "tokenPrefix"]) {
20
+ if (policy[key] !== void 0 && (typeof policy[key] !== "string" || !policy[key]))
21
+ invalid();
22
+ }
23
+ if (policy.scheme === "jwt" && policy.allowedRoles === void 0 && policy.allowedPermissions === void 0)
24
+ invalid();
25
+ let authentication;
26
+ if (policy.scheme === "jwt") {
27
+ if (keys.some(
28
+ (key) => !["jwksPublicKeyUrl", "signatureKey", "decodeResource"].includes(key)
29
+ ))
30
+ invalid();
31
+ const keyFields = keys.filter((key) => key !== "decodeResource");
32
+ if (keyFields.length !== 1 || typeof credentials[keyFields[0]] !== "string" || !credentials[keyFields[0]])
33
+ invalid();
34
+ const decodeResource = credentials.decodeResource;
35
+ if (decodeResource !== void 0 && typeof decodeResource !== "function")
36
+ invalid();
37
+ delete credentials.decodeResource;
38
+ authentication = {
39
+ jwt: credentials,
40
+ ...decodeResource === void 0 ? {} : { decodeResource }
41
+ };
42
+ } else if (policy.scheme === "hmac") {
43
+ 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(
44
+ (key) => typeof key !== "string" || !key
45
+ ))
46
+ invalid();
47
+ authentication = { hmac: credentials };
48
+ } else {
49
+ return invalid();
50
+ }
51
+ delete policy.scheme;
52
+ for (const key of ["allowedRoles", "allowedPermissions"]) {
53
+ if (policy[key] !== void 0) {
54
+ const values = policy[key];
55
+ if (!Array.isArray(values) || values.some((value) => typeof value !== "string"))
56
+ invalid();
57
+ policy[key] = new Set(values);
58
+ }
59
+ }
60
+ return { ...details, auth: { ...policy, ...authentication } };
61
+ }
62
+
1
63
  // src/http/middleware/request/cors.middleware.ts
2
64
  import corsMiddleware from "cors";
3
65
  function cors(corsOptions) {
@@ -473,7 +535,7 @@ function createContext(schemaValidator) {
473
535
  }
474
536
 
475
537
  // src/http/router/discriminateBody.ts
476
- function discriminateBody(schemaValidator, body) {
538
+ function discriminateBody(schemaValidator, body, inputProjection) {
477
539
  if (body == null) {
478
540
  return void 0;
479
541
  }
@@ -523,7 +585,7 @@ function discriminateBody(schemaValidator, body) {
523
585
  parserType: "text",
524
586
  schema: maybeTypedBody
525
587
  };
526
- } else if (schemaValidator.openapi(maybeTypedBody).format === "binary") {
588
+ } else if ((inputProjection ?? schemaValidator.openapi(maybeTypedBody)).format === "binary") {
527
589
  return {
528
590
  contentType: "application/octet-stream",
529
591
  parserType: "file",
@@ -955,15 +1017,23 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
955
1017
  if (!parsedKeyId || !parsedTimestamp || !parsedNonce || !parsedSignature) {
956
1018
  return invalidAuthorizationTokenFormat;
957
1019
  }
958
- const verificationResult = await auth.verificationFunction({
959
- method: req?.method ?? "",
960
- path: req?.path ?? "",
961
- body: req?._rawBody ?? req?.body,
962
- timestamp: new Date(parsedTimestamp),
963
- nonce: parsedNonce,
964
- signature: parsedSignature,
965
- secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
966
- });
1020
+ let verificationResult = false;
1021
+ const signedPaths = hmacSignedPaths(req);
1022
+ for (const path of signedPaths) {
1023
+ verificationResult = Boolean(await auth.verificationFunction({
1024
+ method: req?.method ?? "",
1025
+ path,
1026
+ body: req?._rawBody ?? req?.body,
1027
+ timestamp: new Date(parsedTimestamp),
1028
+ nonce: parsedNonce,
1029
+ signature: parsedSignature,
1030
+ secretKey: collapsedAuthorizationMethod.hmac.secretKeys[parsedKeyId]
1031
+ }));
1032
+ if (verificationResult) {
1033
+ if (path !== signedPaths[0]) warnMountRelativeHmacPath();
1034
+ break;
1035
+ }
1036
+ }
967
1037
  if (!verificationResult) {
968
1038
  return invalidAuthorizationSignature;
969
1039
  }
@@ -1130,6 +1200,25 @@ async function checkAuthorizationToken(req, authorizationMethod, authorizationTo
1130
1200
  }
1131
1201
  }
1132
1202
  }
1203
+ function acceptMountRelativeHmacPaths() {
1204
+ const value = typeof process === "undefined" ? void 0 : process.env?.HMAC_ACCEPT_MOUNT_RELATIVE_PATHS;
1205
+ return value == null || !["false", "0"].includes(value.trim().toLowerCase());
1206
+ }
1207
+ var warnedMountRelativeHmacPath = false;
1208
+ function warnMountRelativeHmacPath() {
1209
+ if (warnedMountRelativeHmacPath) return;
1210
+ warnedMountRelativeHmacPath = true;
1211
+ console.warn(
1212
+ "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."
1213
+ );
1214
+ }
1215
+ function hmacSignedPaths(req) {
1216
+ const relative = req?.path ?? "";
1217
+ const wire = (req?.originalUrl ?? req?.url ?? relative).split("?")[0] || relative;
1218
+ const paths = [wire];
1219
+ if (relative !== wire && acceptMountRelativeHmacPaths()) paths.push(relative);
1220
+ return paths;
1221
+ }
1133
1222
  async function parseRequestAuth(req, res, next) {
1134
1223
  const access = req.contractDetails.access;
1135
1224
  if (access === "public") {
@@ -1367,6 +1456,21 @@ function isRequestShape(maybeResponseShape) {
1367
1456
  return maybeResponseShape != null && ("body" in maybeResponseShape || "query" in maybeResponseShape || "params" in maybeResponseShape || "headers" in maybeResponseShape);
1368
1457
  }
1369
1458
 
1459
+ // src/http/middleware/internal/validationDiagnostic.ts
1460
+ var sendingDiagnostics = /* @__PURE__ */ new WeakSet();
1461
+ function isValidationDiagnostic(response) {
1462
+ return sendingDiagnostics.has(response);
1463
+ }
1464
+ function sendValidationDiagnostic(response, send) {
1465
+ const alreadySending = sendingDiagnostics.has(response);
1466
+ sendingDiagnostics.add(response);
1467
+ try {
1468
+ return send();
1469
+ } finally {
1470
+ if (!alreadySending) sendingDiagnostics.delete(response);
1471
+ }
1472
+ }
1473
+
1370
1474
  // src/http/middleware/request/parse.middleware.ts
1371
1475
  function parse(req, res, next) {
1372
1476
  const globalOptions = req._globalOptions?.();
@@ -1448,14 +1552,13 @@ function parse(req, res, next) {
1448
1552
  default:
1449
1553
  case void 0:
1450
1554
  case "error":
1451
- res.type("application/json");
1555
+ res.type("text/plain");
1452
1556
  res.status(400);
1453
1557
  if (hasSend(res)) {
1454
- res.send(
1455
- `${collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")}
1558
+ const diagnostic = `${collectedParseErrors ?? prettyPrintParseErrors2(parsedRequest.errors, "Request")}
1456
1559
 
1457
- Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
1458
- );
1560
+ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`;
1561
+ sendValidationDiagnostic(res, () => res.send(diagnostic));
1459
1562
  } else {
1460
1563
  next?.(new Error("Request is not sendable."));
1461
1564
  }
@@ -1864,13 +1967,25 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1864
1967
  span: void 0
1865
1968
  },
1866
1969
  _rawBody: void 0,
1867
- _parsedVersions: void 0
1970
+ _parsedVersions: void 0,
1971
+ // hyper-express's content parser reads the body through its Request's
1972
+ // body readers; serve the body the call was given.
1973
+ buffer: async () => Buffer.from(
1974
+ typeof req.body === "string" ? req.body : JSON.stringify(req.body)
1975
+ ),
1976
+ json: async () => req.body,
1977
+ text: async () => typeof req.body === "string" ? req.body : JSON.stringify(req.body)
1868
1978
  };
1869
1979
  const res = {
1870
1980
  statusCode: 200,
1981
+ // hyper-express's response enrichment reads the status and headers
1982
+ // from these fields, which its own Response keeps.
1983
+ _status_code: 200,
1984
+ _headers: responseHeaders,
1871
1985
  status: (code) => {
1872
1986
  statusCode = code;
1873
1987
  res.statusCode = code;
1988
+ res._status_code = code;
1874
1989
  return res;
1875
1990
  },
1876
1991
  send: (message) => {
@@ -1890,6 +2005,8 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1890
2005
  return res;
1891
2006
  },
1892
2007
  getHeader: (key) => responseHeaders[key],
2008
+ getHeaders: () => ({ ...responseHeaders }),
2009
+ locals: {},
1893
2010
  type: (contentType) => {
1894
2011
  responseHeaders["content-type"] = contentType;
1895
2012
  return res;
@@ -1908,31 +2025,59 @@ var ForklaunchExpressLikeRouter = class _ForklaunchExpressLikeRouter {
1908
2025
  version
1909
2026
  };
1910
2027
  const executeMiddlewares = request?.executeMiddlewares ?? false;
2028
+ const cHandler = controllerHandler;
1911
2029
  if (executeMiddlewares && middlewares.length > 0) {
1912
- const allHandlers = [...middlewares];
1913
- let cursor = allHandlers.shift();
1914
- if (cursor) {
1915
- for (const fn of allHandlers) {
1916
- await cursor(req, res, (err) => {
1917
- if (err) {
1918
- throw err;
2030
+ const chain = middlewares;
2031
+ await new Promise((resolve, reject) => {
2032
+ let settled = false;
2033
+ let responded = false;
2034
+ let controllerStarted = false;
2035
+ const finish = (error) => {
2036
+ if (settled) return;
2037
+ settled = true;
2038
+ if (error) reject(error);
2039
+ else resolve();
2040
+ };
2041
+ res.on("finish", () => {
2042
+ responded = true;
2043
+ if (!controllerStarted) finish();
2044
+ });
2045
+ const run = async (index) => {
2046
+ try {
2047
+ if (index === chain.length) {
2048
+ controllerStarted = true;
2049
+ await cHandler(req, res, (err) => {
2050
+ if (err) {
2051
+ throw err;
2052
+ }
2053
+ });
2054
+ finish();
2055
+ return;
1919
2056
  }
1920
- cursor = fn;
1921
- });
1922
- }
1923
- await cursor(req, res, async (err) => {
1924
- if (err) {
1925
- throw err;
2057
+ let calledNext = false;
2058
+ await chain[index](req, res, (err) => {
2059
+ calledNext = true;
2060
+ if (err) {
2061
+ throw err;
2062
+ }
2063
+ return run(index + 1);
2064
+ });
2065
+ if (!calledNext && chain[index].length < 3 && !responded) {
2066
+ await run(index + 1);
2067
+ }
2068
+ } catch (error) {
2069
+ finish(error);
1926
2070
  }
1927
- });
1928
- }
2071
+ };
2072
+ void run(0);
2073
+ });
2074
+ } else {
2075
+ await cHandler(req, res, (err) => {
2076
+ if (err) {
2077
+ throw err;
2078
+ }
2079
+ });
1929
2080
  }
1930
- const cHandler = controllerHandler;
1931
- await cHandler(req, res, (err) => {
1932
- if (err) {
1933
- throw err;
1934
- }
1935
- });
1936
2081
  return {
1937
2082
  code: Number(statusCode),
1938
2083
  response: responseMessage,
@@ -2716,12 +2861,55 @@ function signPlatformEvent(params) {
2716
2861
  });
2717
2862
  }
2718
2863
 
2864
+ // src/http/modelStream.ts
2865
+ async function* readModelStream(response) {
2866
+ const reader = response.body?.getReader();
2867
+ if (!reader)
2868
+ throw new ModelGatewayRequestError(502, "The model stream is empty.");
2869
+ const decoder = new TextDecoder();
2870
+ let buffer = "", bytes = 0;
2871
+ try {
2872
+ for (; ; ) {
2873
+ const part = await reader.read();
2874
+ if (part.done) break;
2875
+ bytes += part.value.byteLength;
2876
+ if (bytes > 512e3) throw new Error("oversized");
2877
+ buffer += decoder.decode(part.value, { stream: true });
2878
+ let match;
2879
+ while (match = buffer.match(/\r?\n\r?\n/)) {
2880
+ const event = buffer.slice(0, match.index);
2881
+ buffer = buffer.slice(match.index + match[0].length);
2882
+ const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
2883
+ if (!data) continue;
2884
+ if (data === "[DONE]") return;
2885
+ const value = JSON.parse(data);
2886
+ if (value.error || !Array.isArray(value.choices))
2887
+ throw new Error("invalid");
2888
+ yield value;
2889
+ }
2890
+ }
2891
+ throw new Error("interrupted");
2892
+ } catch {
2893
+ throw new ModelGatewayRequestError(
2894
+ 502,
2895
+ "The model stream was interrupted. No automatic retry was sent."
2896
+ );
2897
+ } finally {
2898
+ await reader.cancel().catch(() => {
2899
+ });
2900
+ reader.releaseLock();
2901
+ }
2902
+ }
2903
+
2719
2904
  // src/http/modelGatewayClient.ts
2720
2905
  var ModelGatewayRequestError = class extends InstanceGatewayRequestError {
2721
2906
  name = "ModelGatewayRequestError";
2722
2907
  };
2723
2908
  function createModelGatewayClient(options2 = {}) {
2724
- const transport = createInstanceGatewayTransport(options2, "The model gateway");
2909
+ const transport = createInstanceGatewayTransport(
2910
+ options2,
2911
+ "The model gateway"
2912
+ );
2725
2913
  async function send(method, route, body, signal) {
2726
2914
  try {
2727
2915
  return await transport.request(method, route, body, { signal });
@@ -2757,42 +2945,12 @@ function createModelGatewayClient(options2 = {}) {
2757
2945
  request,
2758
2946
  streamOptions.signal
2759
2947
  );
2760
- if (!response.body) return;
2761
- for await (const data of sseData(response.body)) {
2762
- if (data === "[DONE]") return;
2763
- try {
2764
- yield JSON.parse(data);
2765
- } catch {
2766
- }
2767
- }
2948
+ yield* readModelStream(response);
2768
2949
  }
2769
2950
  }
2770
2951
  }
2771
2952
  };
2772
2953
  }
2773
- async function* sseData(stream) {
2774
- const decoder = new TextDecoder();
2775
- let buffer = "";
2776
- const reader = stream.getReader();
2777
- try {
2778
- for (; ; ) {
2779
- const { value, done } = await reader.read();
2780
- if (done) break;
2781
- buffer += decoder.decode(value, { stream: true });
2782
- let boundary;
2783
- while ((boundary = buffer.search(/\r?\n\r?\n/)) !== -1) {
2784
- const event = buffer.slice(0, boundary);
2785
- buffer = buffer.slice(boundary).replace(/^\r?\n\r?\n/, "");
2786
- const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
2787
- if (data) yield data;
2788
- }
2789
- }
2790
- const tail = buffer.trim();
2791
- if (tail.startsWith("data:")) yield tail.slice(5).trimStart();
2792
- } finally {
2793
- reader.releaseLock();
2794
- }
2795
- }
2796
2954
 
2797
2955
  // src/http/previewModelGatewayClient.ts
2798
2956
  function record(value) {
@@ -2848,9 +3006,9 @@ function createPreviewModelGatewayClient(options2 = {}) {
2848
3006
  options2,
2849
3007
  "The local preview model gateway"
2850
3008
  );
2851
- async function send(method, route, body) {
3009
+ async function send(method, route, body, signal) {
2852
3010
  try {
2853
- return await transport.request(method, route, body);
3011
+ return await transport.request(method, route, body, { signal });
2854
3012
  } catch (error) {
2855
3013
  if (error instanceof InstanceGatewayRequestError)
2856
3014
  throw new ModelGatewayRequestError(
@@ -2868,6 +3026,15 @@ function createPreviewModelGatewayClient(options2 = {}) {
2868
3026
  },
2869
3027
  chat: {
2870
3028
  completions: {
3029
+ async *stream(request, options3 = {}) {
3030
+ const response = await send(
3031
+ "POST",
3032
+ "/models/chat/completions",
3033
+ { ...request, stream: true },
3034
+ options3.signal
3035
+ );
3036
+ yield* readModelStream(response);
3037
+ },
2871
3038
  async create(request) {
2872
3039
  return await (await send(
2873
3040
  "POST",
@@ -3401,6 +3568,19 @@ function createPaymentsClient(options2 = {}) {
3401
3568
  },
3402
3569
  status() {
3403
3570
  return call("GET", "/payments/status");
3571
+ },
3572
+ payouts(params = {}) {
3573
+ const { limit } = params;
3574
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 50)) {
3575
+ throw new TypeError("limit must be a whole number from 1 to 50");
3576
+ }
3577
+ return call(
3578
+ "GET",
3579
+ limit === void 0 ? "/payments/payouts" : `/payments/payouts?limit=${limit}`
3580
+ );
3581
+ },
3582
+ dashboardLink() {
3583
+ return call("POST", "/payments/dashboard-link", {});
3404
3584
  }
3405
3585
  };
3406
3586
  }
@@ -3420,7 +3600,7 @@ function loadStripe() {
3420
3600
  function stripeGatewayFetch(settings, fetchImpl = fetch) {
3421
3601
  const base = new URL(settings.gatewayUrl);
3422
3602
  const basePath = base.pathname.replace(/\/+$/, "");
3423
- return async (input, init = {}) => {
3603
+ const signedFetch = async (input, init = {}) => {
3424
3604
  const original = new URL(
3425
3605
  typeof input === "string" || input instanceof URL ? input : input.url
3426
3606
  );
@@ -3460,6 +3640,13 @@ function stripeGatewayFetch(settings, fetchImpl = fetch) {
3460
3640
  }
3461
3641
  );
3462
3642
  };
3643
+ const preconnect = Reflect.get(fetchImpl, "preconnect");
3644
+ if (typeof preconnect === "function") {
3645
+ Object.defineProperty(signedFetch, "preconnect", {
3646
+ value: (_input, options2) => Reflect.apply(preconnect, fetchImpl, [base.origin, options2])
3647
+ });
3648
+ }
3649
+ return signedFetch;
3463
3650
  }
3464
3651
  function createStripeClient(options2 = {}) {
3465
3652
  const StripeClass = options2.Stripe ?? loadStripe();
@@ -3478,8 +3665,8 @@ function createStripeClient(options2 = {}) {
3478
3665
  return new StripeClass(options2.apiKey, options2.config);
3479
3666
  }
3480
3667
  const settings = instanceGatewaySettings(options2, "Payments");
3481
- const gateway = new URL(settings.gatewayUrl);
3482
- const protocol = gateway.protocol === "http:" ? "http" : "https";
3668
+ const gateway2 = new URL(settings.gatewayUrl);
3669
+ const protocol = gateway2.protocol === "http:" ? "http" : "https";
3483
3670
  return new StripeClass(
3484
3671
  // No key: the authenticator below replaces it, and the gateway holds the
3485
3672
  // real one. (stripe-node accepts an authenticator in place of a key.)
@@ -3487,8 +3674,8 @@ function createStripeClient(options2 = {}) {
3487
3674
  {
3488
3675
  maxNetworkRetries: 2,
3489
3676
  ...options2.config,
3490
- host: gateway.hostname,
3491
- port: gateway.port || (protocol === "http" ? 80 : 443),
3677
+ host: gateway2.hostname,
3678
+ port: gateway2.port || (protocol === "http" ? 80 : 443),
3492
3679
  protocol,
3493
3680
  telemetry: false,
3494
3681
  // The signed fetch sets `authorization`; nothing else may.
@@ -4692,10 +4879,10 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4692
4879
  let bodySchema;
4693
4880
  let responsesSchemas;
4694
4881
  if (route.contractDetails.versions) {
4695
- const version2 = route.contractDetails.versions[index];
4882
+ const version2 = Object.values(route.contractDetails.versions)[index];
4883
+ responsesSchemas = version2.responses;
4696
4884
  if (version2.body && schemaValidator.parse(inputSchema, args).ok) {
4697
4885
  bodySchema = version2.body;
4698
- responsesSchemas = version2.responses;
4699
4886
  }
4700
4887
  } else {
4701
4888
  bodySchema = route.contractDetails.body;
@@ -4706,7 +4893,7 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4706
4893
  if (discriminatedBody) {
4707
4894
  switch (discriminatedBody.parserType) {
4708
4895
  case "json": {
4709
- parsedBody = safeStringify4(body);
4896
+ parsedBody = body === void 0 ? void 0 : JSON.stringify(body);
4710
4897
  break;
4711
4898
  }
4712
4899
  case "text": {
@@ -4772,11 +4959,11 @@ function generateMcpServer(schemaValidator, protocol, host, port, version, appli
4772
4959
  ).toString();
4773
4960
  url += queryString ? `?${queryString}` : "";
4774
4961
  }
4775
- const response = await fetch(encodeURI(url), {
4962
+ const response = await fetch(url, {
4776
4963
  method: route.method.toUpperCase(),
4777
4964
  headers: {
4778
4965
  ...headers,
4779
- ...discriminatedBody?.contentType != "multipart/form-data" ? {
4966
+ ...discriminatedBody?.contentType != "multipart/form-data" && (contentType ?? discriminatedBody?.contentType) !== void 0 ? {
4780
4967
  "Content-Type": contentType ?? discriminatedBody?.contentType
4781
4968
  } : {}
4782
4969
  },
@@ -4856,7 +5043,12 @@ function isResponseCompiledSchema(schema) {
4856
5043
  }
4857
5044
 
4858
5045
  // src/http/middleware/response/parse.middleware.ts
5046
+ var sendingValidationError = /* @__PURE__ */ new WeakSet();
4859
5047
  function parse2(req, res, next) {
5048
+ if (sendingValidationError.has(res)) {
5049
+ next?.();
5050
+ return;
5051
+ }
4860
5052
  let headers;
4861
5053
  let responses;
4862
5054
  const globalOptions = req._globalOptions?.();
@@ -4899,15 +5091,35 @@ function parse2(req, res, next) {
4899
5091
  responses = responseSchemas.responses;
4900
5092
  }
4901
5093
  const statusCode = Number(res.statusCode);
4902
- const parsedResponse = schemaValidator.parse(
4903
- [400, 401, 404, 403, 500].includes(statusCode) ? schemaValidator.union([schemaValidator.string, responses?.[statusCode]]) : responses?.[statusCode],
4904
- res.bodyData
4905
- );
5094
+ const defaultErrorStatus = [400, 401, 404, 403, 500].includes(statusCode);
5095
+ const missingResponseSchema = !defaultErrorStatus && !Object.prototype.hasOwnProperty.call(responses, statusCode);
5096
+ if (missingResponseSchema && collapsedOptions !== "warning") {
5097
+ throw new Error(`Undocumented response status ${statusCode}`);
5098
+ }
5099
+ const declaredResponse = responses?.[statusCode];
5100
+ const defaultErrorResponse = () => {
5101
+ try {
5102
+ return schemaValidator.parse(
5103
+ schemaValidator.union([schemaValidator.string, declaredResponse]),
5104
+ res.bodyData
5105
+ );
5106
+ } catch (error) {
5107
+ if (declaredResponse === void 0) throw error;
5108
+ const declared = schemaValidator.parse(declaredResponse, res.bodyData);
5109
+ if (declared.ok) return declared;
5110
+ const message = schemaValidator.parse(
5111
+ schemaValidator.string,
5112
+ res.bodyData
5113
+ );
5114
+ return message.ok ? message : declared;
5115
+ }
5116
+ };
5117
+ const parsedResponse = missingResponseSchema ? { ok: true } : defaultErrorStatus ? defaultErrorResponse() : schemaValidator.parse(declaredResponse, res.bodyData);
4906
5118
  const parsedHeaders = schemaValidator.parse(
4907
5119
  headers ?? schemaValidator.unknown,
4908
5120
  res.getHeaders()
4909
5121
  );
4910
- const parseErrors = [];
5122
+ const parseErrors = missingResponseSchema ? [`Response: Undocumented response status ${statusCode}`] : [];
4911
5123
  if (!parsedHeaders.ok) {
4912
5124
  const headerErrors = prettyPrintParseErrors3(parsedHeaders.errors, "Header");
4913
5125
  if (headerErrors) {
@@ -4924,21 +5136,26 @@ function parse2(req, res, next) {
4924
5136
  }
4925
5137
  }
4926
5138
  if (parseErrors.length > 0) {
4927
- switch (req.contractDetails.options?.responseValidation) {
5139
+ switch (collapsedOptions) {
4928
5140
  default:
4929
5141
  case void 0:
4930
5142
  case "error":
4931
5143
  res.type("text/plain");
4932
5144
  res.status(500);
4933
5145
  if (hasSend(res)) {
4934
- res.send(
4935
- `Invalid response:
5146
+ sendingValidationError.add(res);
5147
+ try {
5148
+ res.send(
5149
+ `Invalid response:
4936
5150
  ${parseErrors.join(
4937
- "\n\n"
4938
- )}
5151
+ "\n\n"
5152
+ )}
4939
5153
 
4940
5154
  Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
4941
- );
5155
+ );
5156
+ } finally {
5157
+ sendingValidationError.delete(res);
5158
+ }
4942
5159
  } else {
4943
5160
  next?.(new Error("Response is not sendable."));
4944
5161
  }
@@ -4949,8 +5166,6 @@ Correlation id: ${req.context.correlationId ?? "No correlation ID"}`
4949
5166
  ${parseErrors.join("\n\n")}`
4950
5167
  );
4951
5168
  break;
4952
- case "none":
4953
- break;
4954
5169
  }
4955
5170
  }
4956
5171
  next?.();
@@ -5004,6 +5219,10 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5004
5219
  originalSend.call(instance, data);
5005
5220
  return;
5006
5221
  }
5222
+ if (typeof data === "string" && isValidationDiagnostic(res)) {
5223
+ originalSend.call(instance, data);
5224
+ return;
5225
+ }
5007
5226
  let responses;
5008
5227
  if (req.contractDetails.responses == null && (req.contractDetails.versions == null || Object.values(req.contractDetails.versions).some(
5009
5228
  (version) => version?.responses == null
@@ -5013,7 +5232,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
5013
5232
  if (req.contractDetails.responses != null) {
5014
5233
  responses = req.contractDetails.responses;
5015
5234
  } else {
5016
- responses = req.contractDetails.versions[req.version].responses;
5235
+ responses = req.contractDetails.versions[req.version]?.responses ?? {};
5017
5236
  }
5018
5237
  }
5019
5238
  const responseBodies = discriminateResponseBodies(
@@ -5235,7 +5454,16 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5235
5454
  for (const key in discriminatedResponseBodiesResult) {
5236
5455
  coercedResponses[key] = {
5237
5456
  description: httpStatusCodes_default[key],
5238
- content: contentResolver(
5457
+ // Undefined/void represent no response body, not a JSON null payload.
5458
+ // Check the schema kind rather than its OpenAPI projection: null and
5459
+ // nullish also project to null but still admit an actual JSON body.
5460
+ content: schemaValidator.undefined !== void 0 && typedSchemaValidator.isInstanceOf(
5461
+ discriminatedResponseBodiesResult[key].schema,
5462
+ schemaValidator.undefined
5463
+ ) || schemaValidator.void !== void 0 && typedSchemaValidator.isInstanceOf(
5464
+ discriminatedResponseBodiesResult[key].schema,
5465
+ schemaValidator.void
5466
+ ) ? void 0 : contentResolver(
5239
5467
  schemaValidator,
5240
5468
  discriminatedResponseBodiesResult[key].schema,
5241
5469
  discriminatedResponseBodiesResult[key].contentType
@@ -5271,15 +5499,21 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5271
5499
  operationObject.parameters?.push({
5272
5500
  name: key,
5273
5501
  in: "path",
5502
+ required: true,
5274
5503
  schema: typedSchemaValidator.openapi(params[key])
5275
5504
  });
5276
5505
  }
5277
5506
  }
5278
- const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body) : null;
5507
+ const adapter = schemaValidator;
5508
+ const bareProjection = body && adapter.isSchema(body) ? adapter.openapiRequestBody?.(body) : void 0;
5509
+ const discriminatedBodyResult = body ? discriminateBody(schemaValidator, body, bareProjection?.schema) : null;
5279
5510
  if (discriminatedBodyResult) {
5511
+ const projection = bareProjection ?? adapter.openapiRequestBody?.(discriminatedBodyResult.schema);
5280
5512
  operationObject.requestBody = {
5281
- required: true,
5282
- content: contentResolver(
5513
+ required: projection?.required ?? true,
5514
+ content: projection ? {
5515
+ [discriminatedBodyResult.contentType]: { schema: projection.schema }
5516
+ } : contentResolver(
5283
5517
  schemaValidator,
5284
5518
  discriminatedBodyResult.schema,
5285
5519
  discriminatedBodyResult.contentType
@@ -5314,25 +5548,14 @@ function generateOperationObject(schemaValidator, path, method, controllerName,
5314
5548
  content: contentResolver(schemaValidator, schemaValidator.string)
5315
5549
  };
5316
5550
  if ("basic" in auth) {
5317
- operationObject.security = [
5318
- {
5319
- basic: Array.from(
5320
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5321
- )
5322
- }
5323
- ];
5551
+ operationObject.security = [{ basic: [] }];
5324
5552
  securitySchemes["basic"] = {
5325
5553
  type: "http",
5326
5554
  scheme: "basic"
5327
5555
  };
5328
5556
  } else if (auth) {
5329
- operationObject.security = [
5330
- {
5331
- [auth.headerName !== "Authorization" ? "bearer" : "apiKey"]: Array.from(
5332
- "allowedPermissions" in auth && auth.allowedPermissions instanceof Set ? auth.allowedPermissions.values() : []
5333
- )
5334
- }
5335
- ];
5557
+ const schemeName = auth.headerName || "Authorization";
5558
+ operationObject.security = [{ [schemeName]: [] }];
5336
5559
  if (auth.headerName && auth.headerName !== "Authorization") {
5337
5560
  securitySchemes[auth.headerName] = {
5338
5561
  type: "apiKey",
@@ -5563,6 +5786,150 @@ var RateLimiter = class {
5563
5786
  return READ_METHODS.has(method.toUpperCase()) ? "read" : "write";
5564
5787
  }
5565
5788
  };
5789
+
5790
+ // src/http/registerDelegatedSurface.ts
5791
+ function registerDelegatedSurface(surface, mount, register) {
5792
+ 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(
5793
+ (callback) => typeof callback !== "string" || !callback.startsWith("/")
5794
+ )) {
5795
+ throw new Error("Invalid delegated HTTP surface descriptor");
5796
+ }
5797
+ const paths = Array.isArray(mount.paths) ? mount.paths : [mount.paths];
5798
+ if (mount.basePath !== "" && !mount.basePath.startsWith("/") || paths.length === 0 || paths.some(
5799
+ (path) => !path.startsWith("/") || mount.basePath !== "" && !path.startsWith(mount.basePath + "/")
5800
+ )) {
5801
+ throw new Error("Invalid delegated HTTP surface mount");
5802
+ }
5803
+ return register(mount);
5804
+ }
5805
+
5806
+ // src/http/localModelGatewayClient.ts
5807
+ import { execFile } from "child_process";
5808
+ import { promisify } from "util";
5809
+ var execute = promisify(execFile);
5810
+ var gateway = "https://studio-orchestrator.forklaunch.com/studio-orchestrator/desktop-runtime-model-gateway";
5811
+ function validate(value) {
5812
+ 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) {
5813
+ throw new Error(
5814
+ "Invalid local model connection. No model request was sent."
5815
+ );
5816
+ }
5817
+ return value;
5818
+ }
5819
+ function createLocalModelGatewayClient(options2 = {}) {
5820
+ if (process.env.FORKLAUNCH_API_KEY === void 0 && !options2.credentialProvider && process.env.FORKLAUNCH_LOCAL_MODEL_RELAY === "1") {
5821
+ return createPreviewModelGatewayClient({ fetch: options2.fetch });
5822
+ }
5823
+ const applicationId = options2.applicationId ?? process.env.FORKLAUNCH_APPLICATION_ID;
5824
+ const model = options2.model ?? process.env.AGENT_MODEL;
5825
+ if (applicationId && !/^[a-f0-9-]{36}$/i.test(applicationId)) {
5826
+ throw new Error(
5827
+ "FORKLAUNCH_APPLICATION_ID must be a saved application UUID."
5828
+ );
5829
+ }
5830
+ const provider = options2.credentialProvider ?? (async () => {
5831
+ const args = ["model", "credential", "--json"];
5832
+ if (applicationId) args.push("--application-id", applicationId);
5833
+ if (model) args.push("--model", model);
5834
+ try {
5835
+ const { stdout } = await execute("forklaunch", args, {
5836
+ timeout: 35e3,
5837
+ maxBuffer: 65536,
5838
+ windowsHide: true
5839
+ });
5840
+ return JSON.parse(stdout);
5841
+ } catch {
5842
+ throw new Error(
5843
+ "Could not connect the local agent. Run forklaunch login, check app access, and use a CLI that supports model credential."
5844
+ );
5845
+ }
5846
+ });
5847
+ const optionsFetch = options2.fetch ?? fetch;
5848
+ let pending;
5849
+ let credential;
5850
+ async function connection() {
5851
+ if (credential && credential.expiresAt > Date.now()) return credential;
5852
+ pending ??= provider().then(validate).then((value) => credential = value).finally(() => {
5853
+ pending = void 0;
5854
+ });
5855
+ return pending;
5856
+ }
5857
+ return {
5858
+ async models() {
5859
+ const c = await connection();
5860
+ return {
5861
+ models: [{ id: c.model }],
5862
+ preview: {
5863
+ expiresAt: c.expiresAt,
5864
+ maxCalls: c.maxCalls,
5865
+ maxUnits: c.maxUnits
5866
+ }
5867
+ };
5868
+ },
5869
+ chat: {
5870
+ completions: {
5871
+ async *stream(request, options3 = {}) {
5872
+ const c = await connection();
5873
+ if (request.model !== c.model)
5874
+ throw new ModelGatewayRequestError(
5875
+ 400,
5876
+ "Choose the model authorized for this local session."
5877
+ );
5878
+ const response = await optionsFetch(
5879
+ `${c.gatewayBaseUrl}/chat/completions`,
5880
+ {
5881
+ method: "POST",
5882
+ redirect: "error",
5883
+ signal: options3.signal ? AbortSignal.any([options3.signal, AbortSignal.timeout(6e4)]) : AbortSignal.timeout(6e4),
5884
+ headers: {
5885
+ authorization: `Bearer ${c.capability}`,
5886
+ "content-type": "application/json"
5887
+ },
5888
+ body: JSON.stringify({ ...request, stream: true })
5889
+ }
5890
+ );
5891
+ if (!response.ok) {
5892
+ await response.body?.cancel();
5893
+ throw new ModelGatewayRequestError(
5894
+ response.status,
5895
+ "The local model stream was refused. No automatic retry was sent."
5896
+ );
5897
+ }
5898
+ yield* readModelStream(response);
5899
+ },
5900
+ async create(request) {
5901
+ const c = await connection();
5902
+ if (request.model !== c.model)
5903
+ throw new ModelGatewayRequestError(
5904
+ 400,
5905
+ "Choose the model authorized for this local session."
5906
+ );
5907
+ const response = await (options2.fetch ?? fetch)(
5908
+ `${c.gatewayBaseUrl}/chat/completions`,
5909
+ {
5910
+ method: "POST",
5911
+ redirect: "error",
5912
+ signal: AbortSignal.timeout(6e4),
5913
+ headers: {
5914
+ authorization: `Bearer ${c.capability}`,
5915
+ "content-type": "application/json"
5916
+ },
5917
+ body: JSON.stringify({ ...request, stream: false })
5918
+ }
5919
+ );
5920
+ if (!response.ok) {
5921
+ await response.body?.cancel();
5922
+ throw new ModelGatewayRequestError(
5923
+ response.status,
5924
+ "The local model request was refused or failed. No automatic retry was sent."
5925
+ );
5926
+ }
5927
+ return response.json();
5928
+ }
5929
+ }
5930
+ }
5931
+ };
5932
+ }
5566
5933
  export {
5567
5934
  ATTR_API_NAME,
5568
5935
  ATTR_APPLICATION_ID,
@@ -5600,10 +5967,12 @@ export {
5600
5967
  auditComplianceAccess,
5601
5968
  auditEnabled,
5602
5969
  auditEventType,
5970
+ bindRuntimeAuth,
5603
5971
  createContext,
5604
5972
  createEmailClient,
5605
5973
  createHmacToken,
5606
5974
  createInstanceGatewayTransport,
5975
+ createLocalModelGatewayClient,
5607
5976
  createModelGatewayClient,
5608
5977
  createPaymentsClient,
5609
5978
  createPreviewModelGatewayClient,
@@ -5653,6 +6022,7 @@ export {
5653
6022
  put,
5654
6023
  recordMetric,
5655
6024
  recordRequestAudit,
6025
+ registerDelegatedSurface,
5656
6026
  resetProcessHandlersForTesting,
5657
6027
  signPlatformEvent,
5658
6028
  smsSegments,