@love-moon/conductor-sdk 0.2.30 → 0.2.32

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.
package/dist/client.d.ts CHANGED
@@ -47,6 +47,10 @@ export interface StopTaskEvent {
47
47
  requestId?: string;
48
48
  reason?: string;
49
49
  }
50
+ export interface FlushPendingUpstreamEventsOptions {
51
+ timeoutMs?: number;
52
+ retryIntervalMs?: number;
53
+ }
50
54
  export declare class ConductorClient {
51
55
  private readonly config;
52
56
  private readonly env;
@@ -68,6 +72,10 @@ export declare class ConductorClient {
68
72
  constructor(init: ConductorClientInit);
69
73
  static connect(options?: ConductorClientConnectOptions): Promise<ConductorClient>;
70
74
  close(): Promise<void>;
75
+ flushPendingUpstreamEvents(options?: FlushPendingUpstreamEventsOptions): Promise<{
76
+ flushed: boolean;
77
+ remaining: number;
78
+ }>;
71
79
  forceReconnect(reason?: string): Promise<void>;
72
80
  createTaskSession(payload: Record<string, any>): Promise<Record<string, any>>;
73
81
  sendMessage(taskId: string, content: string, metadata?: Record<string, any>): Promise<Record<string, any>>;
package/dist/client.js CHANGED
@@ -91,10 +91,30 @@ export class ConductorClient {
91
91
  if (this.closed) {
92
92
  return;
93
93
  }
94
- this.closed = true;
95
94
  this.clearDurableOutboxTimer();
95
+ await this.flushPendingUpstreamEvents({
96
+ timeoutMs: 2_000,
97
+ retryIntervalMs: 100,
98
+ });
99
+ this.closed = true;
96
100
  await this.wsClient.disconnect();
97
101
  }
102
+ async flushPendingUpstreamEvents(options = {}) {
103
+ const timeoutMs = Math.max(0, options.timeoutMs ?? 5_000);
104
+ const retryIntervalMs = Math.max(10, options.retryIntervalMs ?? 250);
105
+ const startedAt = Date.now();
106
+ while (true) {
107
+ await this.requestDurableOutboxFlush(true);
108
+ const remaining = this.upstreamOutbox.load().length;
109
+ if (remaining === 0) {
110
+ return { flushed: true, remaining: 0 };
111
+ }
112
+ if (Date.now() - startedAt >= timeoutMs) {
113
+ return { flushed: false, remaining };
114
+ }
115
+ await sleep(retryIntervalMs);
116
+ }
117
+ }
98
118
  async forceReconnect(reason = 'manual_reconnect') {
99
119
  if (typeof this.wsClient.forceReconnect === 'function') {
100
120
  await this.wsClient.forceReconnect(reason);
@@ -221,6 +221,7 @@ export class SessionDiskStore {
221
221
  }
222
222
  acquireLock() {
223
223
  const startedAt = Date.now();
224
+ fs.mkdirSync(path.dirname(this.lockPath), { recursive: true });
224
225
  while (true) {
225
226
  try {
226
227
  const fd = fs.openSync(this.lockPath, 'wx');
@@ -41,6 +41,7 @@ export interface WebSocketDisconnectEvent {
41
41
  }
42
42
  export interface WebSocketClientOptions {
43
43
  reconnectDelay?: number;
44
+ duplicateHostReconnectDelay?: number;
44
45
  heartbeatInterval?: number;
45
46
  extraHeaders?: Record<string, string>;
46
47
  connectImpl?: ConnectImpl;
@@ -54,6 +55,7 @@ export declare class ConductorWebSocketClient {
54
55
  private readonly url;
55
56
  private readonly token;
56
57
  private readonly reconnectDelay;
58
+ private readonly duplicateHostReconnectDelay;
57
59
  private readonly heartbeatInterval;
58
60
  private readonly connectImpl;
59
61
  private readonly onConnected?;
@@ -61,7 +63,7 @@ export declare class ConductorWebSocketClient {
61
63
  private readonly onPong?;
62
64
  private readonly onReconnected?;
63
65
  private readonly handlers;
64
- private readonly extraHeaders;
66
+ private extraHeaders;
65
67
  private conn;
66
68
  private runtime;
67
69
  private stop;
@@ -72,6 +74,7 @@ export declare class ConductorWebSocketClient {
72
74
  private hasConnectedAtLeastOnce;
73
75
  constructor(config: ConductorConfig, options?: WebSocketClientOptions);
74
76
  registerHandler(handler: WebSocketHandler): void;
77
+ setExtraHeaders(extraHeaders?: Record<string, string>): void;
75
78
  connect(): Promise<void>;
76
79
  disconnect(): Promise<void>;
77
80
  forceReconnect(reason?: string): Promise<void>;
@@ -82,6 +85,7 @@ export declare class ConductorWebSocketClient {
82
85
  private listenLoop;
83
86
  private heartbeatLoop;
84
87
  private handleConnectionLoss;
88
+ private getReconnectDelay;
85
89
  private dispatch;
86
90
  private notifyConnected;
87
91
  private notifyReconnected;
package/dist/ws/client.js CHANGED
@@ -12,6 +12,7 @@ export class ConductorWebSocketClient {
12
12
  url;
13
13
  token;
14
14
  reconnectDelay;
15
+ duplicateHostReconnectDelay;
15
16
  heartbeatInterval;
16
17
  connectImpl;
17
18
  onConnected;
@@ -32,6 +33,7 @@ export class ConductorWebSocketClient {
32
33
  this.url = config.resolvedWebsocketUrl;
33
34
  this.token = config.agentToken;
34
35
  this.reconnectDelay = options.reconnectDelay ?? 10_000;
36
+ this.duplicateHostReconnectDelay = options.duplicateHostReconnectDelay ?? Math.max(this.reconnectDelay, 2_000);
35
37
  this.heartbeatInterval = options.heartbeatInterval ?? 20_000;
36
38
  this.extraHeaders = {
37
39
  'x-conductor-host': options.hostName ?? defaultHostName(),
@@ -46,6 +48,12 @@ export class ConductorWebSocketClient {
46
48
  registerHandler(handler) {
47
49
  this.handlers.push(handler);
48
50
  }
51
+ setExtraHeaders(extraHeaders = {}) {
52
+ this.extraHeaders = {
53
+ 'x-conductor-host': this.extraHeaders['x-conductor-host'] || defaultHostName(),
54
+ ...extraHeaders,
55
+ };
56
+ }
49
57
  async connect() {
50
58
  this.stop = false;
51
59
  if (this.waitController.signal.aborted) {
@@ -178,8 +186,16 @@ export class ConductorWebSocketClient {
178
186
  this.conn = null;
179
187
  this.runtime = null;
180
188
  this.notifyDisconnected(this.buildDisconnectEvent(runtime));
189
+ await wait(this.getReconnectDelay(runtime), this.waitController.signal);
181
190
  await this.openConnection(true);
182
191
  }
192
+ getReconnectDelay(runtime) {
193
+ if (runtime?.closeCode === 4002 &&
194
+ String(runtime?.closeReason || '').trim().toLowerCase() === 'duplicate-host') {
195
+ return this.duplicateHostReconnectDelay;
196
+ }
197
+ return this.reconnectDelay;
198
+ }
183
199
  async dispatch(message) {
184
200
  const now = Date.now();
185
201
  if (this.runtime) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@love-moon/conductor-sdk",
3
- "version": "0.2.30",
3
+ "version": "0.2.32",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -27,5 +27,5 @@
27
27
  "typescript": "^5.6.3",
28
28
  "vitest": "^2.1.4"
29
29
  },
30
- "gitCommitId": "e6a71ad"
30
+ "gitCommitId": "c749d4b"
31
31
  }