@flowcore/sdk 1.24.4 → 1.26.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 (85) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +238 -0
  3. package/esm/commands/ai-agent-coordinator/artifact-get.command.d.ts +22 -0
  4. package/esm/commands/ai-agent-coordinator/artifact-get.command.d.ts.map +1 -0
  5. package/esm/commands/ai-agent-coordinator/artifact-get.command.js +52 -0
  6. package/esm/commands/ai-agent-coordinator/context-add-item.command.d.ts +28 -0
  7. package/esm/commands/ai-agent-coordinator/context-add-item.command.d.ts.map +1 -0
  8. package/esm/commands/ai-agent-coordinator/context-add-item.command.js +34 -0
  9. package/esm/commands/ai-agent-coordinator/context-remove-item.command.d.ts +28 -0
  10. package/esm/commands/ai-agent-coordinator/context-remove-item.command.d.ts.map +1 -0
  11. package/esm/commands/ai-agent-coordinator/context-remove-item.command.js +32 -0
  12. package/esm/commands/ai-agent-coordinator/conversation-delete.command.d.ts +25 -0
  13. package/esm/commands/ai-agent-coordinator/conversation-delete.command.d.ts.map +1 -0
  14. package/esm/commands/ai-agent-coordinator/conversation-delete.command.js +30 -0
  15. package/esm/commands/ai-agent-coordinator/conversation-get.command.d.ts +27 -0
  16. package/esm/commands/ai-agent-coordinator/conversation-get.command.d.ts.map +1 -0
  17. package/esm/commands/ai-agent-coordinator/conversation-get.command.js +36 -0
  18. package/esm/commands/ai-agent-coordinator/conversation-list.command.d.ts +23 -0
  19. package/esm/commands/ai-agent-coordinator/conversation-list.command.d.ts.map +1 -0
  20. package/esm/commands/ai-agent-coordinator/conversation-list.command.js +29 -0
  21. package/esm/commands/ai-agent-coordinator/conversation-stream.command.d.ts +30 -0
  22. package/esm/commands/ai-agent-coordinator/conversation-stream.command.d.ts.map +1 -0
  23. package/esm/commands/ai-agent-coordinator/conversation-stream.command.js +36 -0
  24. package/esm/commands/ai-agent-coordinator/mod.d.ts +8 -0
  25. package/esm/commands/ai-agent-coordinator/mod.d.ts.map +1 -0
  26. package/esm/commands/ai-agent-coordinator/mod.js +7 -0
  27. package/esm/common/command.d.ts.map +1 -1
  28. package/esm/common/command.js +3 -2
  29. package/esm/common/websocket-client.d.ts +109 -0
  30. package/esm/common/websocket-client.d.ts.map +1 -0
  31. package/esm/common/websocket-client.js +391 -0
  32. package/esm/common/websocket-command.d.ts +54 -0
  33. package/esm/common/websocket-command.d.ts.map +1 -0
  34. package/esm/common/websocket-command.js +1 -0
  35. package/esm/contracts/ai-agent-coordinator-stream.d.ts +85 -0
  36. package/esm/contracts/ai-agent-coordinator-stream.d.ts.map +1 -0
  37. package/esm/contracts/ai-agent-coordinator-stream.js +1 -0
  38. package/esm/contracts/ai-agent-coordinator.d.ts +41 -0
  39. package/esm/contracts/ai-agent-coordinator.d.ts.map +1 -0
  40. package/esm/contracts/ai-agent-coordinator.js +1 -0
  41. package/esm/mod.d.ts +8 -2
  42. package/esm/mod.d.ts.map +1 -1
  43. package/esm/mod.js +8 -2
  44. package/package.json +1 -1
  45. package/script/commands/ai-agent-coordinator/artifact-get.command.d.ts +22 -0
  46. package/script/commands/ai-agent-coordinator/artifact-get.command.d.ts.map +1 -0
  47. package/script/commands/ai-agent-coordinator/artifact-get.command.js +56 -0
  48. package/script/commands/ai-agent-coordinator/context-add-item.command.d.ts +28 -0
  49. package/script/commands/ai-agent-coordinator/context-add-item.command.d.ts.map +1 -0
  50. package/script/commands/ai-agent-coordinator/context-add-item.command.js +38 -0
  51. package/script/commands/ai-agent-coordinator/context-remove-item.command.d.ts +28 -0
  52. package/script/commands/ai-agent-coordinator/context-remove-item.command.d.ts.map +1 -0
  53. package/script/commands/ai-agent-coordinator/context-remove-item.command.js +36 -0
  54. package/script/commands/ai-agent-coordinator/conversation-delete.command.d.ts +25 -0
  55. package/script/commands/ai-agent-coordinator/conversation-delete.command.d.ts.map +1 -0
  56. package/script/commands/ai-agent-coordinator/conversation-delete.command.js +34 -0
  57. package/script/commands/ai-agent-coordinator/conversation-get.command.d.ts +27 -0
  58. package/script/commands/ai-agent-coordinator/conversation-get.command.d.ts.map +1 -0
  59. package/script/commands/ai-agent-coordinator/conversation-get.command.js +40 -0
  60. package/script/commands/ai-agent-coordinator/conversation-list.command.d.ts +23 -0
  61. package/script/commands/ai-agent-coordinator/conversation-list.command.d.ts.map +1 -0
  62. package/script/commands/ai-agent-coordinator/conversation-list.command.js +33 -0
  63. package/script/commands/ai-agent-coordinator/conversation-stream.command.d.ts +30 -0
  64. package/script/commands/ai-agent-coordinator/conversation-stream.command.d.ts.map +1 -0
  65. package/script/commands/ai-agent-coordinator/conversation-stream.command.js +40 -0
  66. package/script/commands/ai-agent-coordinator/mod.d.ts +8 -0
  67. package/script/commands/ai-agent-coordinator/mod.d.ts.map +1 -0
  68. package/script/commands/ai-agent-coordinator/mod.js +23 -0
  69. package/script/common/command.d.ts.map +1 -1
  70. package/script/common/command.js +27 -3
  71. package/script/common/websocket-client.d.ts +109 -0
  72. package/script/common/websocket-client.d.ts.map +1 -0
  73. package/script/common/websocket-client.js +418 -0
  74. package/script/common/websocket-command.d.ts +54 -0
  75. package/script/common/websocket-command.d.ts.map +1 -0
  76. package/script/common/websocket-command.js +2 -0
  77. package/script/contracts/ai-agent-coordinator-stream.d.ts +85 -0
  78. package/script/contracts/ai-agent-coordinator-stream.d.ts.map +1 -0
  79. package/script/contracts/ai-agent-coordinator-stream.js +2 -0
  80. package/script/contracts/ai-agent-coordinator.d.ts +41 -0
  81. package/script/contracts/ai-agent-coordinator.d.ts.map +1 -0
  82. package/script/contracts/ai-agent-coordinator.js +2 -0
  83. package/script/mod.d.ts +8 -2
  84. package/script/mod.d.ts.map +1 -1
  85. package/script/mod.js +11 -2
@@ -0,0 +1,29 @@
1
+ import { Command as BaseCommandClass } from "../../common/command.js";
2
+ const service = "ai-coordinator";
3
+ const baseUrl = `https://${service}.api.flowcore.io`;
4
+ /**
5
+ * Command to list all conversations accessible by the user.
6
+ */
7
+ export class ConversationListCommand extends BaseCommandClass {
8
+ constructor() {
9
+ super({});
10
+ }
11
+ getBaseUrl() {
12
+ return baseUrl;
13
+ }
14
+ getMethod() {
15
+ return "GET";
16
+ }
17
+ getPath() {
18
+ return `/api/v1/conversations`;
19
+ }
20
+ parseResponse(response) {
21
+ const data = response;
22
+ if (data && Array.isArray(data.conversations)) {
23
+ return data.conversations;
24
+ }
25
+ else {
26
+ throw new Error("Invalid response format for ConversationListCommand");
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,30 @@
1
+ import type { WebSocketCommand } from "../../common/websocket-command.js";
2
+ /**
3
+ * Configuration for the Conversation Stream.
4
+ * Only requires the conversationId.
5
+ */
6
+ export interface ConversationStreamConfig {
7
+ conversationId: string;
8
+ }
9
+ /**
10
+ * Payload type for messages sent *to* the Conversation Stream.
11
+ */
12
+ export interface ConversationStreamSendPayload {
13
+ content: string;
14
+ }
15
+ /**
16
+ * Command to stream conversation events for a specific agent.
17
+ */
18
+ export declare class ConversationStreamCommand implements WebSocketCommand<ConversationStreamConfig, ConversationStreamSendPayload> {
19
+ private config;
20
+ constructor(config: ConversationStreamConfig);
21
+ /** Get the configuration object for the command. */
22
+ getConfig(): ConversationStreamConfig;
23
+ /** Get the base WebSocket URL. */
24
+ getWebSocketBaseUrl(): string;
25
+ /** Get the WebSocket path segment. */
26
+ getWebSocketPathSegment(config: ConversationStreamConfig): string;
27
+ /** Serializer function for outgoing payloads. */
28
+ serializeSendPayload(payload: ConversationStreamSendPayload): string;
29
+ }
30
+ //# sourceMappingURL=conversation-stream.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-stream.command.d.ts","sourceRoot":"","sources":["../../../src/commands/ai-agent-coordinator/conversation-stream.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AAEzE;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;GAEG;AACH,qBAAa,yBACX,YAAW,gBAAgB,CAAC,wBAAwB,EAAE,6BAA6B,CAAC;IACpF,OAAO,CAAC,MAAM,CAA0B;gBAE5B,MAAM,EAAE,wBAAwB;IAO5C,oDAAoD;IACpD,SAAS,IAAI,wBAAwB;IAIrC,kCAAkC;IAClC,mBAAmB,IAAI,MAAM;IAK7B,sCAAsC;IACtC,uBAAuB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM;IAKjE,iDAAiD;IACjD,oBAAoB,CAAC,OAAO,EAAE,6BAA6B,GAAG,MAAM;CAIrE"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Command to stream conversation events for a specific agent.
3
+ */
4
+ export class ConversationStreamCommand {
5
+ constructor(config) {
6
+ Object.defineProperty(this, "config", {
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true,
10
+ value: void 0
11
+ });
12
+ if (!config.conversationId) {
13
+ throw new Error("conversationId is required in the config for ConversationStreamCommand");
14
+ }
15
+ this.config = config;
16
+ }
17
+ /** Get the configuration object for the command. */
18
+ getConfig() {
19
+ return this.config;
20
+ }
21
+ /** Get the base WebSocket URL. */
22
+ getWebSocketBaseUrl() {
23
+ // Specific base URL for AI Coordinator streams
24
+ return "wss://ai-coordinator.api.flowcore.io";
25
+ }
26
+ /** Get the WebSocket path segment. */
27
+ getWebSocketPathSegment(config) {
28
+ // Updated path segment based on likely API structure
29
+ return `/api/v1/stream/conversations/${config.conversationId}`;
30
+ }
31
+ /** Serializer function for outgoing payloads. */
32
+ serializeSendPayload(payload) {
33
+ // Default JSON serialization
34
+ return JSON.stringify(payload);
35
+ }
36
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./artifact-get.command.js";
2
+ export * from "./context-add-item.command.js";
3
+ export * from "./context-remove-item.command.js";
4
+ export * from "./conversation-delete.command.js";
5
+ export * from "./conversation-get.command.js";
6
+ export * from "./conversation-list.command.js";
7
+ export * from "./conversation-stream.command.js";
8
+ //# sourceMappingURL=mod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../../src/commands/ai-agent-coordinator/mod.ts"],"names":[],"mappings":"AAAA,cAAc,2BAA2B,CAAA;AACzC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,kCAAkC,CAAA;AAChD,cAAc,kCAAkC,CAAA;AAChD,cAAc,+BAA+B,CAAA;AAC7C,cAAc,gCAAgC,CAAA;AAC9C,cAAc,kCAAkC,CAAA"}
@@ -0,0 +1,7 @@
1
+ export * from "./artifact-get.command.js";
2
+ export * from "./context-add-item.command.js";
3
+ export * from "./context-remove-item.command.js";
4
+ export * from "./conversation-delete.command.js";
5
+ export * from "./conversation-get.command.js";
6
+ export * from "./conversation-list.command.js";
7
+ export * from "./conversation-stream.command.js";
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/common/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAG1D;;GAEG;AACH,8BAAsB,OAAO,CAAC,KAAK,EAAE,MAAM;IACzC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAO;IAEjD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE9C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAuB;IAE/E;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAEnB,KAAK,EAAE,KAAK;IAOxB;;OAEG;cACa,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAuCnF;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM;IAEvC;;OAEG;IACH,SAAS,CAAC,SAAS,IAAI,MAAM;IAI7B;;OAEG;IACH,SAAS,CAAC,OAAO,IAAI,MAAM;IAI3B;;OAEG;IACH,SAAS,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,SAAS;IAOzE;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQ9C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM;IAE3D;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIrD;;OAEG;IACU,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QACzE,YAAY,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAA;QACrC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,SAAS,CAAA;QACnE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,aAAa,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9D,eAAe,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9E,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;QAC/C,cAAc,EAAE,OAAO,CAAA;QACvB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAC7D,CAAC;IAeF;;OAEG;cAEa,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5F"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/common/command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAA;AAE3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAG1D;;GAEG;AACH,8BAAsB,OAAO,CAAC,KAAK,EAAE,MAAM;IACzC;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAO;IAEjD;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE9C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAuB;IAE/E;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;gBAEnB,KAAK,EAAE,KAAK;IAOxB;;OAEG;cACa,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0CnF;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,MAAM;IAEvC;;OAEG;IACH,SAAS,CAAC,SAAS,IAAI,MAAM;IAI7B;;OAEG;IACH,SAAS,CAAC,OAAO,IAAI,MAAM;IAI3B;;OAEG;IACH,SAAS,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,SAAS;IAOzE;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQ9C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM;IAE3D;;OAEG;IACH,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAIrD;;OAEG;IACU,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;QACzE,YAAY,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAA;QACrC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,SAAS,CAAA;QACnE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC/B,OAAO,EAAE,MAAM,CAAA;QACf,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,MAAM,CAAA;QACd,aAAa,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9D,eAAe,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9E,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;QAC/C,cAAc,EAAE,OAAO,CAAA;QACvB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;KAC7D,CAAC;IAeF;;OAEG;cAEa,eAAe,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5F"}
@@ -1,4 +1,3 @@
1
- import { TenantFetchCommand } from "../commands/tenant/tenant.fetch.js";
2
1
  import { ClientError } from "../exceptions/client-error.js";
3
2
  import { CommandError } from "../exceptions/command-error.js";
4
3
  import { tenantCache } from "./tenant.cache.js";
@@ -62,13 +61,15 @@ export class Command {
62
61
  }
63
62
  let tenant = tenantCache.get(inputTenant);
64
63
  if (!tenant) {
64
+ // Dynamically import TenantFetchCommand only when needed
65
+ const { TenantFetchCommand } = await import("../commands/tenant/tenant.fetch.js");
65
66
  tenant = await client.execute(new TenantFetchCommand({ tenant: inputTenant }))
66
67
  .catch((error) => {
67
68
  if (error instanceof ClientError && error.status === 403) {
68
69
  return {
69
70
  isDedicated: false,
70
71
  dedicated: null,
71
- };
72
+ }; // Cast needed as we are not returning the full Tenant shape
72
73
  }
73
74
  throw error;
74
75
  });
@@ -0,0 +1,109 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { type Logger } from "../utils/logger.js";
3
+ import type { ClientOptions } from "./flowcore-client.js";
4
+ import type { ActiveStreamInterface, WebSocketCommand } from "./websocket-command.js";
5
+ /**
6
+ * Interface for OIDC authentication client (reused from NotificationClient context)
7
+ */
8
+ export type OidcClient = {
9
+ getToken: () => Promise<{
10
+ accessToken: string;
11
+ }>;
12
+ };
13
+ /**
14
+ * Configuration options for the WebSocketClient
15
+ */
16
+ export type WebSocketClientOptions = {
17
+ /** Interval between reconnect attempts (ms). Defaults to 1000. */
18
+ reconnectInterval?: number;
19
+ /** Maximum number of reconnect attempts. Defaults to undefined (infinite). */
20
+ maxReconnects?: number;
21
+ /** Optional logger instance. */
22
+ logger?: Logger;
23
+ };
24
+ interface MinimalWebSocket {
25
+ readyState: number;
26
+ onopen: (() => void) | null;
27
+ onmessage: ((event: {
28
+ data: string | ArrayBuffer | Buffer;
29
+ }) => void) | null;
30
+ onclose: ((event: {
31
+ code: number;
32
+ reason: string;
33
+ wasClean: boolean;
34
+ }) => void) | null;
35
+ onerror: ((event: Event) => void) | null;
36
+ send(data: string): void;
37
+ close(code?: number, reason?: string): void;
38
+ }
39
+ type WebSocketFactory = (url: string) => MinimalWebSocket;
40
+ /**
41
+ * Generic client for managing a single, persistent WebSocket connection based on a command.
42
+ * Handles connection lifecycle, authentication, reconnection, and message sending/receiving.
43
+ */
44
+ export declare class WebSocketClient {
45
+ private readonly authOptions;
46
+ private overrideBaseUrl;
47
+ private webSocket;
48
+ private options;
49
+ private logger;
50
+ private reconnectInterval;
51
+ private reconnectAttempts;
52
+ private _isOpen;
53
+ private _isConnecting;
54
+ private webSocketFactory;
55
+ private internalSubject;
56
+ private currentCommand;
57
+ private currentConfig;
58
+ /**
59
+ * Creates a new WebSocketClient instance.
60
+ * @param authOptions - Authentication options (Bearer token via OIDC client or API Key).
61
+ * @param options - Configuration options for the client.
62
+ * @param webSocketFactory - Optional WebSocket factory for testing.
63
+ */
64
+ constructor(authOptions: ClientOptions, options?: WebSocketClientOptions, webSocketFactory?: WebSocketFactory);
65
+ /**
66
+ * Override the base URL provided by commands.
67
+ * @param baseUrl - The new base URL to use (e.g., "wss://staging-server.api.flowcore.io").
68
+ */
69
+ setBaseUrl(baseUrl: string): void;
70
+ /**
71
+ * Returns true if the WebSocket connection is currently open.
72
+ */
73
+ get isOpen(): boolean;
74
+ /**
75
+ * Returns true if the client is currently attempting to establish a WebSocket connection.
76
+ */
77
+ get isConnecting(): boolean;
78
+ /**
79
+ * Establishes WebSocket connection based on the provided command.
80
+ * Disconnects any existing connection before starting the new one.
81
+ * @param command - The command defining the stream connection details.
82
+ * @returns An interface to interact with the active stream.
83
+ */
84
+ connect<Config, SendPayload>(command: WebSocketCommand<Config, SendPayload>): Promise<ActiveStreamInterface<SendPayload>>;
85
+ /**
86
+ * Sets up the WebSocket event handlers (onopen, onmessage, onclose, onerror).
87
+ */
88
+ private setupEventHandlers;
89
+ /**
90
+ * Attempts to reconnect to the WebSocket server using exponential backoff.
91
+ * Requires `currentConfig` to be set.
92
+ */
93
+ private attemptReconnect;
94
+ /**
95
+ * Sends a message to the currently connected WebSocket.
96
+ * @param message - The message object to send (e.g., { content: "user input" }).
97
+ */
98
+ private sendMessage;
99
+ /**
100
+ * Closes the WebSocket connection gracefully.
101
+ */
102
+ disconnect(): void;
103
+ /**
104
+ * Implements the Disposable interface for clean resource management.
105
+ */
106
+ [Symbol.dispose](): void;
107
+ }
108
+ export {};
109
+ //# sourceMappingURL=websocket-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket-client.d.ts","sourceRoot":"","sources":["../../src/common/websocket-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,391 @@
1
+ import * as dntShim from "../_dnt.shims.js";
2
+ import { Buffer } from "node:buffer";
3
+ import { Subject } from "rxjs";
4
+ import { defaultLogger } from "../utils/logger.js";
5
+ // Maximum reconnection interval in milliseconds
6
+ const MAX_RECONNECT_INTERVAL = 30_000;
7
+ /**
8
+ * Generic client for managing a single, persistent WebSocket connection based on a command.
9
+ * Handles connection lifecycle, authentication, reconnection, and message sending/receiving.
10
+ */
11
+ export class WebSocketClient {
12
+ /**
13
+ * Creates a new WebSocketClient instance.
14
+ * @param authOptions - Authentication options (Bearer token via OIDC client or API Key).
15
+ * @param options - Configuration options for the client.
16
+ * @param webSocketFactory - Optional WebSocket factory for testing.
17
+ */
18
+ constructor(authOptions, options, webSocketFactory) {
19
+ Object.defineProperty(this, "authOptions", {
20
+ enumerable: true,
21
+ configurable: true,
22
+ writable: true,
23
+ value: authOptions
24
+ });
25
+ Object.defineProperty(this, "overrideBaseUrl", {
26
+ enumerable: true,
27
+ configurable: true,
28
+ writable: true,
29
+ value: void 0
30
+ }); // Field to store the override URL
31
+ Object.defineProperty(this, "webSocket", {
32
+ enumerable: true,
33
+ configurable: true,
34
+ writable: true,
35
+ value: void 0
36
+ });
37
+ Object.defineProperty(this, "options", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
43
+ Object.defineProperty(this, "logger", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: void 0
48
+ });
49
+ Object.defineProperty(this, "reconnectInterval", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: void 0
54
+ });
55
+ Object.defineProperty(this, "reconnectAttempts", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: 0
60
+ });
61
+ Object.defineProperty(this, "_isOpen", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: false
66
+ });
67
+ Object.defineProperty(this, "_isConnecting", {
68
+ enumerable: true,
69
+ configurable: true,
70
+ writable: true,
71
+ value: false
72
+ });
73
+ Object.defineProperty(this, "webSocketFactory", {
74
+ enumerable: true,
75
+ configurable: true,
76
+ writable: true,
77
+ value: void 0
78
+ });
79
+ // Internal subject to push received data
80
+ Object.defineProperty(this, "internalSubject", {
81
+ enumerable: true,
82
+ configurable: true,
83
+ writable: true,
84
+ value: new Subject()
85
+ });
86
+ // Store the current command and config for reconnects and sending
87
+ Object.defineProperty(this, "currentCommand", {
88
+ enumerable: true,
89
+ configurable: true,
90
+ writable: true,
91
+ value: null
92
+ });
93
+ Object.defineProperty(this, "currentConfig", {
94
+ enumerable: true,
95
+ configurable: true,
96
+ writable: true,
97
+ value: null
98
+ });
99
+ this.options = { reconnectInterval: 1000, ...options };
100
+ this.logger = options?.logger ?? defaultLogger;
101
+ this.reconnectInterval = this.options.reconnectInterval;
102
+ this.webSocketFactory = webSocketFactory ?? ((url) => new dntShim.WebSocket(url));
103
+ this.overrideBaseUrl = undefined; // Initialize override URL
104
+ if ("getBearerToken" in authOptions === false && ("apiKey" in authOptions === false)) {
105
+ throw new Error("Invalid authOptions: Must provide either getBearerToken or apiKey/apiKeyId");
106
+ }
107
+ }
108
+ /**
109
+ * Override the base URL provided by commands.
110
+ * @param baseUrl - The new base URL to use (e.g., "wss://staging-server.api.flowcore.io").
111
+ */
112
+ setBaseUrl(baseUrl) {
113
+ this.logger.info(`WebSocket base URL overridden to: ${baseUrl}`);
114
+ this.overrideBaseUrl = baseUrl;
115
+ }
116
+ /**
117
+ * Returns true if the WebSocket connection is currently open.
118
+ */
119
+ get isOpen() {
120
+ return this._isOpen;
121
+ }
122
+ /**
123
+ * Returns true if the client is currently attempting to establish a WebSocket connection.
124
+ */
125
+ get isConnecting() {
126
+ return this._isConnecting;
127
+ }
128
+ /**
129
+ * Establishes WebSocket connection based on the provided command.
130
+ * Disconnects any existing connection before starting the new one.
131
+ * @param command - The command defining the stream connection details.
132
+ * @returns An interface to interact with the active stream.
133
+ */
134
+ // Using generic types for the command
135
+ async connect(command) {
136
+ if (this._isConnecting || this._isOpen) {
137
+ this.logger.info("Disconnecting existing stream before starting new one.");
138
+ this.disconnect(); // Ensure clean state
139
+ // Add a small delay to allow disconnect process to settle if needed
140
+ await new Promise((resolve) => setTimeout(resolve, 50));
141
+ }
142
+ this.currentCommand = command; // Store command (cast needed)
143
+ const config = command.getConfig();
144
+ this.currentConfig = config; // Store config
145
+ this._isConnecting = true;
146
+ // Use override URL if set, otherwise use command's base URL
147
+ const baseUrl = this.overrideBaseUrl ?? command.getWebSocketBaseUrl();
148
+ const pathSegment = command.getWebSocketPathSegment(config); // Get path segment from command
149
+ this.logger.info(`Attempting to connect stream: ${baseUrl}${pathSegment}`);
150
+ try {
151
+ const urlParams = new URLSearchParams();
152
+ if ("getBearerToken" in this.authOptions && this.authOptions.getBearerToken) {
153
+ const token = await this.authOptions.getBearerToken();
154
+ if (!token)
155
+ throw new Error("Failed to get bearer token");
156
+ urlParams.set("token", token);
157
+ }
158
+ else if ("apiKey" in this.authOptions && this.authOptions.apiKey && this.authOptions.apiKeyId) {
159
+ urlParams.set("api_key", this.authOptions.apiKey);
160
+ urlParams.set("api_key_id", this.authOptions.apiKeyId);
161
+ }
162
+ else {
163
+ throw new Error("Invalid authentication configuration.");
164
+ }
165
+ const streamUrl = `${baseUrl}${pathSegment}?${urlParams.toString()}`; // Construct URL from command parts
166
+ this.logger.debug(`Connecting to WebSocket URL: ${streamUrl}`);
167
+ this.webSocket = this.webSocketFactory(streamUrl);
168
+ this.setupEventHandlers(); // Sets up handlers that push to internalSubject
169
+ // Return the active stream interface
170
+ return {
171
+ output$: this.internalSubject.asObservable(),
172
+ send: (payload) => this.sendMessage(payload),
173
+ disconnect: () => this.disconnect(),
174
+ };
175
+ }
176
+ catch (error) {
177
+ this.logger.error(`Failed to initiate connection: ${error}`);
178
+ this._isConnecting = false;
179
+ this.currentCommand = null; // Clear command/config on failure
180
+ this.currentConfig = null;
181
+ // Rethrow or handle differently? For now, rethrow.
182
+ throw error; // Make connect promise reject on failure
183
+ }
184
+ }
185
+ /**
186
+ * Sets up the WebSocket event handlers (onopen, onmessage, onclose, onerror).
187
+ */
188
+ setupEventHandlers() {
189
+ this.webSocket.onopen = () => {
190
+ this._isOpen = true;
191
+ this._isConnecting = false;
192
+ // Use potentially overridden base URL in logging
193
+ const baseUrl = this.overrideBaseUrl ?? this.currentCommand?.getWebSocketBaseUrl();
194
+ const pathSegment = this.currentCommand?.getWebSocketPathSegment(this.currentConfig);
195
+ this.logger.info(`WebSocket connection opened: ${baseUrl}${pathSegment}`);
196
+ this.reconnectInterval = this.options.reconnectInterval;
197
+ this.reconnectAttempts = 0;
198
+ };
199
+ this.webSocket.onmessage = (event) => {
200
+ try {
201
+ let parsedData;
202
+ // Handle various data types from WebSocket
203
+ if (event.data instanceof ArrayBuffer) {
204
+ parsedData = new TextDecoder().decode(event.data);
205
+ }
206
+ else if (Buffer.isBuffer(event.data)) {
207
+ parsedData = event.data.toString();
208
+ }
209
+ else {
210
+ parsedData = event.data;
211
+ }
212
+ const chunk = JSON.parse(parsedData); // Assume valid JSON structure
213
+ // Basic validation - check if it has a 'type' property
214
+ if (typeof chunk !== "object" || chunk === null || typeof chunk.type !== "string") {
215
+ this.logger.warn(`Received invalid chunk format: ${parsedData}`);
216
+ return; // Ignore invalid chunks
217
+ }
218
+ this.logger.debug(`Received chunk: ${chunk.type}`); // Log chunk type
219
+ this.internalSubject.next(chunk); // Push to internal subject
220
+ }
221
+ catch (error) {
222
+ this.logger.error(`Error processing received message: ${error}`);
223
+ // Optionally emit an error on the subject?
224
+ // this.internalSubject.error(new Error(`Failed to parse message: ${error.message}`));
225
+ }
226
+ };
227
+ this.webSocket.onclose = (event) => {
228
+ const wasOpen = this._isOpen;
229
+ this._isOpen = false;
230
+ this._isConnecting = false;
231
+ // Use potentially overridden base URL in logging
232
+ const baseUrl = this.overrideBaseUrl ?? this.currentCommand?.getWebSocketBaseUrl();
233
+ const pathSegment = this.currentCommand?.getWebSocketPathSegment(this.currentConfig);
234
+ this.logger.info(`WebSocket connection closed: ${baseUrl}${pathSegment} Code [${event.code}], Reason: ${event.reason || "No reason given"}. Was open: ${wasOpen}`);
235
+ if (wasOpen && event.code !== 1000 && this.currentCommand) { // Only reconnect if command is still set
236
+ this.attemptReconnect();
237
+ }
238
+ else {
239
+ this.logger.info(`Completing internal subject due to close event.`);
240
+ this.internalSubject.complete(); // Complete subject on final close
241
+ this.currentCommand = null; // Clear state
242
+ this.currentConfig = null;
243
+ }
244
+ };
245
+ this.webSocket.onerror = () => {
246
+ // Use potentially overridden base URL in logging
247
+ const baseUrl = this.overrideBaseUrl ?? this.currentCommand?.getWebSocketBaseUrl();
248
+ const pathSegment = this.currentCommand?.getWebSocketPathSegment(this.currentConfig);
249
+ this.logger.error(`WebSocket encountered an error for stream: ${baseUrl}${pathSegment}.`);
250
+ if (this.webSocket.readyState !== dntShim.WebSocket.CLOSED &&
251
+ this.webSocket.readyState !== dntShim.WebSocket.CLOSING) {
252
+ this.webSocket.close(1011, "WebSocket error");
253
+ }
254
+ // Error event usually followed by onclose, which handles completion/reconnect
255
+ // Emit error on subject *before* close handling potentially completes it
256
+ this.internalSubject.error(new Error("WebSocket encountered an error"));
257
+ };
258
+ }
259
+ /**
260
+ * Attempts to reconnect to the WebSocket server using exponential backoff.
261
+ * Requires `currentConfig` to be set.
262
+ */
263
+ attemptReconnect() {
264
+ if (!this.currentCommand || !this.currentConfig) {
265
+ this.logger.error("Cannot reconnect without current command/config.");
266
+ if (!this.internalSubject.closed)
267
+ this.internalSubject.complete();
268
+ return;
269
+ }
270
+ if (this._isConnecting) {
271
+ this.logger.debug("Reconnect attempt already in progress.");
272
+ return;
273
+ }
274
+ // Use potentially overridden base URL
275
+ const baseUrl = this.overrideBaseUrl ?? this.currentCommand.getWebSocketBaseUrl();
276
+ const pathSegment = this.currentCommand.getWebSocketPathSegment(this.currentConfig);
277
+ if (this.options.maxReconnects && this.reconnectAttempts >= this.options.maxReconnects) {
278
+ this.logger.error(`Max reconnect attempts (${this.reconnectAttempts}/${this.options.maxReconnects}) reached. Giving up: ${baseUrl}${pathSegment}`);
279
+ if (!this.internalSubject.closed)
280
+ this.internalSubject.complete();
281
+ this.currentCommand = null;
282
+ this.currentConfig = null;
283
+ return;
284
+ }
285
+ this.reconnectAttempts++;
286
+ this._isConnecting = true;
287
+ this.logger.info(`Attempting reconnection ${this.reconnectAttempts}${this.options.maxReconnects ? `/${this.options.maxReconnects}` : ""}
288
+ for ${baseUrl}${pathSegment} in ${this.reconnectInterval} ms...`);
289
+ setTimeout(async () => {
290
+ // Check if disconnect was called while waiting
291
+ if (!this.currentCommand || !this.currentConfig) {
292
+ this.logger.info("Reconnect cancelled as disconnect was called.");
293
+ this._isConnecting = false; // Ensure flag is reset
294
+ if (!this.internalSubject.closed)
295
+ this.internalSubject.complete();
296
+ return;
297
+ }
298
+ // Directly attempt to establish connection *without* calling this.connect()
299
+ try {
300
+ const urlParams = new URLSearchParams();
301
+ // --- Reuse auth logic from connect() ---
302
+ if ("getBearerToken" in this.authOptions && this.authOptions.getBearerToken) {
303
+ const token = await this.authOptions.getBearerToken();
304
+ if (!token)
305
+ throw new Error("Reconnect failed: Could not get bearer token");
306
+ urlParams.set("token", token);
307
+ }
308
+ else if ("apiKey" in this.authOptions && this.authOptions.apiKey && this.authOptions.apiKeyId) {
309
+ urlParams.set("api_key", this.authOptions.apiKey);
310
+ urlParams.set("api_key_id", this.authOptions.apiKeyId);
311
+ }
312
+ else {
313
+ throw new Error("Reconnect failed: Invalid authentication configuration.");
314
+ }
315
+ // --- End auth logic ---
316
+ // Use potentially overridden base URL for reconnect URL
317
+ const streamUrl = `${baseUrl}${pathSegment}?${urlParams.toString()}`;
318
+ this.logger.debug(`Reconnecting to WebSocket URL: ${streamUrl}`);
319
+ this.webSocket = this.webSocketFactory(streamUrl);
320
+ // Connecting flag will be reset in onopen/onerror/onclose
321
+ this.setupEventHandlers();
322
+ }
323
+ catch (error) {
324
+ this.logger.error(`Reconnect attempt connection failed: ${error}`);
325
+ this._isConnecting = false; // Reset connection flag on immediate error
326
+ // Error during reconnect setup, trigger another attempt after backoff
327
+ // We might get stuck here if auth always fails, consider adding specific error handling
328
+ this.reconnectInterval = Math.min(MAX_RECONNECT_INTERVAL, this.reconnectInterval * 2); // Apply backoff before retrying
329
+ this.attemptReconnect();
330
+ }
331
+ }, this.reconnectInterval);
332
+ // Apply backoff interval for the *next* potential reconnect attempt
333
+ this.reconnectInterval = Math.min(MAX_RECONNECT_INTERVAL, this.reconnectInterval * 2);
334
+ }
335
+ /**
336
+ * Sends a message to the currently connected WebSocket.
337
+ * @param message - The message object to send (e.g., { content: "user input" }).
338
+ */
339
+ sendMessage(payload) {
340
+ const openState = 1; // WebSocket.OPEN is typically 1
341
+ if (!this._isOpen || this.webSocket.readyState !== openState || !this.currentCommand) {
342
+ this.logger.warn("Cannot send message: WebSocket is not open or command not set.");
343
+ return false;
344
+ }
345
+ try {
346
+ // Use command's serializer or default to JSON.stringify
347
+ const serializer = this.currentCommand.serializeSendPayload ?? JSON.stringify;
348
+ const dataToSend = serializer(payload);
349
+ this.webSocket.send(dataToSend);
350
+ this.logger.debug(`Sent message: ${dataToSend}`);
351
+ return true;
352
+ }
353
+ catch (error) {
354
+ this.logger.error(`Failed to send message: ${error}`);
355
+ return false;
356
+ }
357
+ }
358
+ /**
359
+ * Closes the WebSocket connection gracefully.
360
+ */
361
+ disconnect() {
362
+ this.logger.info("Disconnect called by user.");
363
+ const commandToClear = this.currentCommand; // Store ref before clearing
364
+ this.currentCommand = null; // Prevent reconnects
365
+ this.currentConfig = null;
366
+ if (this.webSocket) {
367
+ if (this._isOpen || this._isConnecting) {
368
+ this.webSocket.close(1000, "Disconnected by user");
369
+ }
370
+ else {
371
+ this.logger.debug("WebSocket already closed or closing.");
372
+ }
373
+ }
374
+ else {
375
+ this.logger.debug("No WebSocket instance to disconnect.");
376
+ }
377
+ this._isOpen = false;
378
+ this._isConnecting = false;
379
+ // Complete the subject if it hasn't been completed by onclose
380
+ if (commandToClear && !this.internalSubject.closed) {
381
+ this.logger.info("Completing internal subject due to disconnect call.");
382
+ this.internalSubject.complete();
383
+ }
384
+ }
385
+ /**
386
+ * Implements the Disposable interface for clean resource management.
387
+ */
388
+ [Symbol.dispose]() {
389
+ this.disconnect();
390
+ }
391
+ }