@forklaunch/core 3.3.0 → 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 (38) hide show
  1. package/bin/gateway-mock/routes/payments.mjs +47 -0
  2. package/lib/agent/agent.d.ts +26 -0
  3. package/lib/agent/agent.d.ts.map +1 -0
  4. package/lib/agent/continuation.d.ts +29 -0
  5. package/lib/agent/continuation.d.ts.map +1 -0
  6. package/lib/agent/index.d.ts +8 -0
  7. package/lib/agent/index.d.ts.map +1 -0
  8. package/lib/agent/index.js +1049 -0
  9. package/lib/agent/index.js.map +1 -0
  10. package/lib/agent/index.mjs +1005 -0
  11. package/lib/agent/index.mjs.map +1 -0
  12. package/lib/agent/sources.d.ts +27 -0
  13. package/lib/agent/sources.d.ts.map +1 -0
  14. package/lib/agent/tools.d.ts +39 -0
  15. package/lib/agent/tools.d.ts.map +1 -0
  16. package/lib/agent/types.d.ts +130 -0
  17. package/lib/agent/types.d.ts.map +1 -0
  18. package/lib/http/index.d.ts +1 -0
  19. package/lib/http/index.d.ts.map +1 -1
  20. package/lib/http/index.js +203 -38
  21. package/lib/http/index.js.map +1 -1
  22. package/lib/http/index.mjs +202 -38
  23. package/lib/http/index.mjs.map +1 -1
  24. package/lib/http/localModelGatewayClient.d.ts +27 -0
  25. package/lib/http/localModelGatewayClient.d.ts.map +1 -0
  26. package/lib/http/modelGatewayClient.d.ts.map +1 -1
  27. package/lib/http/modelStream.d.ts +4 -0
  28. package/lib/http/modelStream.d.ts.map +1 -0
  29. package/lib/http/paymentsClient.d.ts +55 -1
  30. package/lib/http/paymentsClient.d.ts.map +1 -1
  31. package/lib/http/previewModelGatewayClient.d.ts +5 -1
  32. package/lib/http/previewModelGatewayClient.d.ts.map +1 -1
  33. package/lib/persistence/compliantField.d.ts.map +1 -1
  34. package/lib/persistence/index.js +6 -1
  35. package/lib/persistence/index.js.map +1 -1
  36. package/lib/persistence/index.mjs +6 -1
  37. package/lib/persistence/index.mjs.map +1 -1
  38. package/package.json +9 -2
@@ -2861,12 +2861,55 @@ function signPlatformEvent(params) {
2861
2861
  });
2862
2862
  }
2863
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
+
2864
2904
  // src/http/modelGatewayClient.ts
2865
2905
  var ModelGatewayRequestError = class extends InstanceGatewayRequestError {
2866
2906
  name = "ModelGatewayRequestError";
2867
2907
  };
2868
2908
  function createModelGatewayClient(options2 = {}) {
2869
- const transport = createInstanceGatewayTransport(options2, "The model gateway");
2909
+ const transport = createInstanceGatewayTransport(
2910
+ options2,
2911
+ "The model gateway"
2912
+ );
2870
2913
  async function send(method, route, body, signal) {
2871
2914
  try {
2872
2915
  return await transport.request(method, route, body, { signal });
@@ -2902,42 +2945,12 @@ function createModelGatewayClient(options2 = {}) {
2902
2945
  request,
2903
2946
  streamOptions.signal
2904
2947
  );
2905
- if (!response.body) return;
2906
- for await (const data of sseData(response.body)) {
2907
- if (data === "[DONE]") return;
2908
- try {
2909
- yield JSON.parse(data);
2910
- } catch {
2911
- }
2912
- }
2948
+ yield* readModelStream(response);
2913
2949
  }
2914
2950
  }
2915
2951
  }
2916
2952
  };
2917
2953
  }
2918
- async function* sseData(stream) {
2919
- const decoder = new TextDecoder();
2920
- let buffer = "";
2921
- const reader = stream.getReader();
2922
- try {
2923
- for (; ; ) {
2924
- const { value, done } = await reader.read();
2925
- if (done) break;
2926
- buffer += decoder.decode(value, { stream: true });
2927
- let boundary;
2928
- while ((boundary = buffer.search(/\r?\n\r?\n/)) !== -1) {
2929
- const event = buffer.slice(0, boundary);
2930
- buffer = buffer.slice(boundary).replace(/^\r?\n\r?\n/, "");
2931
- const data = event.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
2932
- if (data) yield data;
2933
- }
2934
- }
2935
- const tail = buffer.trim();
2936
- if (tail.startsWith("data:")) yield tail.slice(5).trimStart();
2937
- } finally {
2938
- reader.releaseLock();
2939
- }
2940
- }
2941
2954
 
2942
2955
  // src/http/previewModelGatewayClient.ts
2943
2956
  function record(value) {
@@ -2993,9 +3006,9 @@ function createPreviewModelGatewayClient(options2 = {}) {
2993
3006
  options2,
2994
3007
  "The local preview model gateway"
2995
3008
  );
2996
- async function send(method, route, body) {
3009
+ async function send(method, route, body, signal) {
2997
3010
  try {
2998
- return await transport.request(method, route, body);
3011
+ return await transport.request(method, route, body, { signal });
2999
3012
  } catch (error) {
3000
3013
  if (error instanceof InstanceGatewayRequestError)
3001
3014
  throw new ModelGatewayRequestError(
@@ -3013,6 +3026,15 @@ function createPreviewModelGatewayClient(options2 = {}) {
3013
3026
  },
3014
3027
  chat: {
3015
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
+ },
3016
3038
  async create(request) {
3017
3039
  return await (await send(
3018
3040
  "POST",
@@ -3546,6 +3568,19 @@ function createPaymentsClient(options2 = {}) {
3546
3568
  },
3547
3569
  status() {
3548
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", {});
3549
3584
  }
3550
3585
  };
3551
3586
  }
@@ -3630,8 +3665,8 @@ function createStripeClient(options2 = {}) {
3630
3665
  return new StripeClass(options2.apiKey, options2.config);
3631
3666
  }
3632
3667
  const settings = instanceGatewaySettings(options2, "Payments");
3633
- const gateway = new URL(settings.gatewayUrl);
3634
- const protocol = gateway.protocol === "http:" ? "http" : "https";
3668
+ const gateway2 = new URL(settings.gatewayUrl);
3669
+ const protocol = gateway2.protocol === "http:" ? "http" : "https";
3635
3670
  return new StripeClass(
3636
3671
  // No key: the authenticator below replaces it, and the gateway holds the
3637
3672
  // real one. (stripe-node accepts an authenticator in place of a key.)
@@ -3639,8 +3674,8 @@ function createStripeClient(options2 = {}) {
3639
3674
  {
3640
3675
  maxNetworkRetries: 2,
3641
3676
  ...options2.config,
3642
- host: gateway.hostname,
3643
- port: gateway.port || (protocol === "http" ? 80 : 443),
3677
+ host: gateway2.hostname,
3678
+ port: gateway2.port || (protocol === "http" ? 80 : 443),
3644
3679
  protocol,
3645
3680
  telemetry: false,
3646
3681
  // The signed fetch sets `authorization`; nothing else may.
@@ -5767,6 +5802,134 @@ function registerDelegatedSurface(surface, mount, register) {
5767
5802
  }
5768
5803
  return register(mount);
5769
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
+ }
5770
5933
  export {
5771
5934
  ATTR_API_NAME,
5772
5935
  ATTR_APPLICATION_ID,
@@ -5809,6 +5972,7 @@ export {
5809
5972
  createEmailClient,
5810
5973
  createHmacToken,
5811
5974
  createInstanceGatewayTransport,
5975
+ createLocalModelGatewayClient,
5812
5976
  createModelGatewayClient,
5813
5977
  createPaymentsClient,
5814
5978
  createPreviewModelGatewayClient,