doover-js 0.4.7 → 0.5.0-alpha.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 (63) hide show
  1. package/README.md +31 -0
  2. package/dist/apis/_args.d.ts +16 -0
  3. package/dist/apis/_args.js +26 -0
  4. package/dist/apis/agents-api.d.ts +4 -1
  5. package/dist/apis/agents-api.js +139 -1
  6. package/dist/apis/aggregates-api.d.ts +23 -3
  7. package/dist/apis/aggregates-api.js +33 -4
  8. package/dist/apis/alarms-api.d.ts +30 -0
  9. package/dist/apis/alarms-api.js +51 -6
  10. package/dist/apis/channels-api.d.ts +40 -3
  11. package/dist/apis/channels-api.js +50 -5
  12. package/dist/apis/connections-api.d.ts +21 -5
  13. package/dist/apis/connections-api.js +26 -10
  14. package/dist/apis/messages-api.d.ts +55 -9
  15. package/dist/apis/messages-api.js +96 -11
  16. package/dist/apis/notifications-api.d.ts +52 -0
  17. package/dist/apis/notifications-api.js +99 -13
  18. package/dist/apis/permissions-api.d.ts +8 -0
  19. package/dist/apis/permissions-api.js +11 -2
  20. package/dist/apis/processors-api.d.ts +35 -0
  21. package/dist/apis/processors-api.js +64 -8
  22. package/dist/apis/users-api.d.ts +8 -0
  23. package/dist/apis/users-api.js +13 -0
  24. package/dist/client/doover-client.d.ts +4 -8
  25. package/dist/client/doover-client.js +16 -28
  26. package/dist/client/stats.d.ts +24 -0
  27. package/dist/client/stats.js +62 -0
  28. package/dist/gateway/gateway-client.d.ts +13 -1
  29. package/dist/gateway/gateway-client.js +54 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.js +13 -7
  32. package/dist/react/index.d.ts +2 -0
  33. package/dist/react/index.js +4 -1
  34. package/dist/react/useChannelAggregate.js +8 -1
  35. package/dist/react/useChannelMessages.js +5 -2
  36. package/dist/react/useChannelSubscription.js +8 -18
  37. package/dist/react/useInvocationLogs.d.ts +23 -0
  38. package/dist/react/useInvocationLogs.js +41 -0
  39. package/dist/react/useMultiAgentAggregates.js +8 -14
  40. package/dist/react/useMultiAgentChannelMessages.js +8 -13
  41. package/dist/react/useSendMessage.js +6 -1
  42. package/dist/react/useSendRpc.js +4 -1
  43. package/dist/react/useUpdateAggregate.js +9 -3
  44. package/dist/rpc/errors.d.ts +10 -0
  45. package/dist/rpc/errors.js +23 -0
  46. package/dist/rpc/rpc-dispatcher.d.ts +28 -0
  47. package/dist/rpc/rpc-dispatcher.js +129 -0
  48. package/dist/test/api-args.test.d.ts +1 -0
  49. package/dist/test/api-args.test.js +48 -0
  50. package/dist/test/api-overloads.test.d.ts +9 -0
  51. package/dist/test/api-overloads.test.js +276 -0
  52. package/dist/test/gateway-subscriptions.test.d.ts +1 -0
  53. package/dist/test/gateway-subscriptions.test.js +95 -0
  54. package/dist/test/react.test.js +36 -0
  55. package/dist/test/rpc-dispatcher.test.d.ts +1 -0
  56. package/dist/test/rpc-dispatcher.test.js +190 -0
  57. package/dist/test/stats-rpc.test.d.ts +1 -0
  58. package/dist/test/stats-rpc.test.js +69 -0
  59. package/dist/test/users-api.test.d.ts +1 -0
  60. package/dist/test/users-api.test.js +19 -0
  61. package/dist/viewer/doover-data-provider.d.ts +30 -34
  62. package/dist/viewer/doover-data-provider.js +82 -316
  63. package/package.json +1 -1
@@ -5,13 +5,34 @@ class ProcessorsApi {
5
5
  constructor(rest) {
6
6
  this.rest = rest;
7
7
  }
8
- createProcessorSchedule(agentId, scheduleId, body) {
8
+ createProcessorSchedule(...args) {
9
+ if (typeof args[0] === "string") {
10
+ return this._createProcessorSchedule(args[0], args[1], args[2]);
11
+ }
12
+ const id = args[0];
13
+ return this._createProcessorSchedule(id.agentId, args[1], args[2]);
14
+ }
15
+ _createProcessorSchedule(agentId, scheduleId, body) {
9
16
  return this.rest.put(`/agents/${agentId}/processors/schedules/${scheduleId}`, body);
10
17
  }
11
- deleteProcessorSchedule(agentId, scheduleId) {
18
+ deleteProcessorSchedule(...args) {
19
+ if (typeof args[0] === "string") {
20
+ return this._deleteProcessorSchedule(args[0], args[1]);
21
+ }
22
+ const id = args[0];
23
+ return this._deleteProcessorSchedule(id.agentId, args[1]);
24
+ }
25
+ _deleteProcessorSchedule(agentId, scheduleId) {
12
26
  return this.rest.delete(`/agents/${agentId}/processors/schedules/${scheduleId}`);
13
27
  }
14
- regenerateScheduleToken(agentId, scheduleId) {
28
+ regenerateScheduleToken(...args) {
29
+ if (typeof args[0] === "string") {
30
+ return this._regenerateScheduleToken(args[0], args[1]);
31
+ }
32
+ const id = args[0];
33
+ return this._regenerateScheduleToken(id.agentId, args[1]);
34
+ }
35
+ _regenerateScheduleToken(agentId, scheduleId) {
15
36
  return this.rest.post(`/agents/${agentId}/processors/schedules/${scheduleId}/token`, {});
16
37
  }
17
38
  getScheduleInfo(scheduleId) {
@@ -20,10 +41,24 @@ class ProcessorsApi {
20
41
  getScheduleInfoAlias(scheduleId) {
21
42
  return this.rest.get(`/processors/schedules/${scheduleId}/info`);
22
43
  }
23
- createProcessorSubscription(agentId, subscriptionId, body) {
44
+ createProcessorSubscription(...args) {
45
+ if (typeof args[0] === "string") {
46
+ return this._createProcessorSubscription(args[0], args[1], args[2]);
47
+ }
48
+ const id = args[0];
49
+ return this._createProcessorSubscription(id.agentId, args[1], args[2]);
50
+ }
51
+ _createProcessorSubscription(agentId, subscriptionId, body) {
24
52
  return this.rest.put(`/agents/${agentId}/processors/subscriptions/${subscriptionId}`, body);
25
53
  }
26
- deleteProcessorSubscription(agentId, subscriptionId) {
54
+ deleteProcessorSubscription(...args) {
55
+ if (typeof args[0] === "string") {
56
+ return this._deleteProcessorSubscription(args[0], args[1]);
57
+ }
58
+ const id = args[0];
59
+ return this._deleteProcessorSubscription(id.agentId, args[1]);
60
+ }
61
+ _deleteProcessorSubscription(agentId, subscriptionId) {
27
62
  return this.rest.delete(`/agents/${agentId}/processors/subscriptions/${subscriptionId}`);
28
63
  }
29
64
  getProcessorSubscriptionInfo(subscriptionName) {
@@ -32,13 +67,34 @@ class ProcessorsApi {
32
67
  getProcessorSubscriptionInfoAlias(subscriptionArn) {
33
68
  return this.rest.get(`/processors/subscriptions/${subscriptionArn}/info`);
34
69
  }
35
- createIngestionEndpoint(agentId, ingestionId, body) {
70
+ createIngestionEndpoint(...args) {
71
+ if (typeof args[0] === "string") {
72
+ return this._createIngestionEndpoint(args[0], args[1], args[2]);
73
+ }
74
+ const id = args[0];
75
+ return this._createIngestionEndpoint(id.agentId, args[1], args[2]);
76
+ }
77
+ _createIngestionEndpoint(agentId, ingestionId, body) {
36
78
  return this.rest.put(`/agents/${agentId}/processors/ingestions/${ingestionId}`, body);
37
79
  }
38
- deleteIngestionEndpoint(agentId, ingestionId) {
80
+ deleteIngestionEndpoint(...args) {
81
+ if (typeof args[0] === "string") {
82
+ return this._deleteIngestionEndpoint(args[0], args[1]);
83
+ }
84
+ const id = args[0];
85
+ return this._deleteIngestionEndpoint(id.agentId, args[1]);
86
+ }
87
+ _deleteIngestionEndpoint(agentId, ingestionId) {
39
88
  return this.rest.delete(`/agents/${agentId}/processors/ingestions/${ingestionId}`);
40
89
  }
41
- invokeIngestionEndpoint(agentId, ingestionId, body, params) {
90
+ invokeIngestionEndpoint(...args) {
91
+ if (typeof args[0] === "string") {
92
+ return this._invokeIngestionEndpoint(args[0], args[1], args[2], args[3]);
93
+ }
94
+ const id = args[0];
95
+ return this._invokeIngestionEndpoint(id.agentId, args[1], args[2], args[3]);
96
+ }
97
+ _invokeIngestionEndpoint(agentId, ingestionId, body, params) {
42
98
  return this.rest.post(`/agents/${agentId}/processors/ingestions/${ingestionId}/invoke`, body, params);
43
99
  }
44
100
  }
@@ -0,0 +1,8 @@
1
+ import type { RestClient } from "../http/rest-client";
2
+ import type { User } from "../types/viewer";
3
+ export declare class UsersApi {
4
+ private readonly rest;
5
+ private readonly controlApiUrl?;
6
+ constructor(rest: RestClient, controlApiUrl?: string | undefined);
7
+ getMe(): Promise<User>;
8
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UsersApi = void 0;
4
+ class UsersApi {
5
+ constructor(rest, controlApiUrl) {
6
+ this.rest = rest;
7
+ this.controlApiUrl = controlApiUrl;
8
+ }
9
+ getMe() {
10
+ return this.rest.get("/users/me", undefined, this.controlApiUrl);
11
+ }
12
+ }
13
+ exports.UsersApi = UsersApi;
@@ -8,15 +8,18 @@ import { NotificationsApi } from "../apis/notifications-api";
8
8
  import { PermissionsApi } from "../apis/permissions-api";
9
9
  import { ProcessorsApi } from "../apis/processors-api";
10
10
  import { TurnApi } from "../apis/turn-api";
11
+ import { UsersApi } from "../apis/users-api";
11
12
  import type { DooverAuth } from "../auth/doover-auth";
12
13
  import { GatewayClient } from "../gateway/gateway-client";
13
14
  import { RestClient, type DooverClientConfig } from "../http/rest-client";
15
+ import { RpcDispatcher } from "../rpc/rpc-dispatcher";
14
16
  import { DooverDataProvider } from "../viewer/doover-data-provider";
15
17
  import { DooverStatsCollector, type DooverStatsSnapshot } from "./stats";
16
18
  export declare class DooverClient {
17
19
  readonly auth: DooverAuth;
18
20
  readonly rest: RestClient;
19
21
  readonly viewer: DooverDataProvider;
22
+ readonly users: UsersApi;
20
23
  readonly channels: ChannelsApi;
21
24
  readonly messages: MessagesApi;
22
25
  readonly aggregates: AggregatesApi;
@@ -28,17 +31,10 @@ export declare class DooverClient {
28
31
  readonly turn: TurnApi;
29
32
  readonly agents: AgentsApi;
30
33
  readonly gateway: GatewayClient;
31
- /** Opt-in instrumentation. Disabled by default — see {@link enableStats}. */
34
+ readonly rpc: RpcDispatcher;
32
35
  readonly stats: DooverStatsCollector;
33
36
  constructor(config: DooverClientConfig);
34
- /** Start capturing request/message stats. Off by default. */
35
37
  enableStats(): void;
36
- /** Stop capturing stats. Existing counters are retained; call `stats.reset()` to clear. */
37
38
  disableStats(): void;
38
- /**
39
- * Snapshot the current stats. Returns zeroed counters if stats were
40
- * never enabled. Combine with {@link GatewayClient.getSubscriptionCount}
41
- * and {@link GatewayClient.getSession} for a full debug view.
42
- */
43
39
  getStats(): DooverStatsSnapshot;
44
40
  }
@@ -11,8 +11,11 @@ const notifications_api_1 = require("../apis/notifications-api");
11
11
  const permissions_api_1 = require("../apis/permissions-api");
12
12
  const processors_api_1 = require("../apis/processors-api");
13
13
  const turn_api_1 = require("../apis/turn-api");
14
+ const users_api_1 = require("../apis/users-api");
14
15
  const build_auth_1 = require("../auth/build-auth");
16
+ const gateway_client_1 = require("../gateway/gateway-client");
15
17
  const rest_client_1 = require("../http/rest-client");
18
+ const rpc_dispatcher_1 = require("../rpc/rpc-dispatcher");
16
19
  const doover_data_provider_1 = require("../viewer/doover-data-provider");
17
20
  const stats_1 = require("./stats");
18
21
  class DooverClient {
@@ -30,7 +33,13 @@ class DooverClient {
30
33
  fetchImpl: config.fetchImpl,
31
34
  });
32
35
  this.rest = new rest_client_1.RestClient(config, this.auth);
33
- this.viewer = new doover_data_provider_1.DooverDataProvider(config, this.auth);
36
+ this.gateway = new gateway_client_1.GatewayClient(config, this.auth);
37
+ this.viewer = new doover_data_provider_1.DooverDataProvider({
38
+ rest: this.rest,
39
+ gateway: this.gateway,
40
+ controlApiUrl: config.controlApiUrl,
41
+ });
42
+ this.users = new users_api_1.UsersApi(this.rest, config.controlApiUrl);
34
43
  this.channels = new channels_api_1.ChannelsApi(this.rest);
35
44
  this.messages = new messages_api_1.MessagesApi(this.rest);
36
45
  this.aggregates = new aggregates_api_1.AggregatesApi(this.rest);
@@ -40,36 +49,15 @@ class DooverClient {
40
49
  this.permissions = new permissions_api_1.PermissionsApi(this.rest);
41
50
  this.processors = new processors_api_1.ProcessorsApi(this.rest);
42
51
  this.turn = new turn_api_1.TurnApi(this.rest);
43
- this.agents = new agents_api_1.AgentsApi(this.rest);
44
- // Reuse the viewer's gateway so `client.gateway` and
45
- // `client.viewer.gateway` are the same instance → one WebSocket per
46
- // client. Without this, `client.gateway.connect()` and
47
- // `client.viewer.subscribeToChannel(...)` each opened their own socket.
48
- this.gateway = this.viewer.gateway;
49
- // Stats collector, disabled by default. Attached to both REST clients
50
- // (facade + viewer's internal) and the shared gateway so every recorded
51
- // call flows through the same counters. Pay-to-play: record methods
52
- // short-circuit when disabled.
52
+ this.agents = new agents_api_1.AgentsApi(this.rest, config.controlApiUrl);
53
+ this.rpc = new rpc_dispatcher_1.RpcDispatcher(this.gateway, this.messages);
53
54
  this.stats = new stats_1.DooverStatsCollector();
54
55
  this.rest.setStats(this.stats);
55
- this.viewer.rest.setStats(this.stats);
56
56
  this.gateway.setStats(this.stats);
57
+ this.rpc.setStats(this.stats);
57
58
  }
58
- /** Start capturing request/message stats. Off by default. */
59
- enableStats() {
60
- this.stats.setEnabled(true);
61
- }
62
- /** Stop capturing stats. Existing counters are retained; call `stats.reset()` to clear. */
63
- disableStats() {
64
- this.stats.setEnabled(false);
65
- }
66
- /**
67
- * Snapshot the current stats. Returns zeroed counters if stats were
68
- * never enabled. Combine with {@link GatewayClient.getSubscriptionCount}
69
- * and {@link GatewayClient.getSession} for a full debug view.
70
- */
71
- getStats() {
72
- return this.stats.snapshot();
73
- }
59
+ enableStats() { this.stats.setEnabled(true); }
60
+ disableStats() { this.stats.setEnabled(false); }
61
+ getStats() { return this.stats.snapshot(); }
74
62
  }
75
63
  exports.DooverClient = DooverClient;
@@ -26,9 +26,22 @@ export interface GatewayStatsSnapshot {
26
26
  /** Frames we've received on the gateway socket. */
27
27
  messagesReceived: number;
28
28
  }
29
+ export interface RpcStatsSnapshot {
30
+ enabled: boolean;
31
+ totalRpcs: number;
32
+ pendingRpcs: number;
33
+ completedRpcs: number;
34
+ failedRpcs: number;
35
+ timedOutRpcs: number;
36
+ abortedRpcs: number;
37
+ averageLatencyMs: number | null;
38
+ lastLatencyMs: number | null;
39
+ peakPendingRpcs: number;
40
+ }
29
41
  export interface DooverStatsSnapshot {
30
42
  rest: RestStatsSnapshot;
31
43
  gateway: GatewayStatsSnapshot;
44
+ rpc: RpcStatsSnapshot;
32
45
  }
33
46
  export declare class DooverStatsCollector {
34
47
  private enabled;
@@ -40,6 +53,15 @@ export declare class DooverStatsCollector {
40
53
  private rLastLatency;
41
54
  private gSent;
42
55
  private gReceived;
56
+ private rpTotal;
57
+ private rpPending;
58
+ private rpCompleted;
59
+ private rpFailed;
60
+ private rpTimedOut;
61
+ private rpAborted;
62
+ private rpLatencySum;
63
+ private rpLastLatency;
64
+ private rpPeakPending;
43
65
  setEnabled(enabled: boolean): void;
44
66
  isEnabled(): boolean;
45
67
  reset(): void;
@@ -51,5 +73,7 @@ export declare class DooverStatsCollector {
51
73
  recordRestEnd(startedAt: number | null, succeeded: boolean): void;
52
74
  recordGatewaySent(): void;
53
75
  recordGatewayReceived(): void;
76
+ recordRpcStart(): number | null;
77
+ recordRpcEnd(startedAt: number | null, outcome: "success" | "error" | "timeout" | "abort"): void;
54
78
  snapshot(): DooverStatsSnapshot;
55
79
  }
@@ -18,6 +18,15 @@ class DooverStatsCollector {
18
18
  this.rLastLatency = null;
19
19
  this.gSent = 0;
20
20
  this.gReceived = 0;
21
+ this.rpTotal = 0;
22
+ this.rpPending = 0;
23
+ this.rpCompleted = 0;
24
+ this.rpFailed = 0;
25
+ this.rpTimedOut = 0;
26
+ this.rpAborted = 0;
27
+ this.rpLatencySum = 0;
28
+ this.rpLastLatency = null;
29
+ this.rpPeakPending = 0;
21
30
  }
22
31
  setEnabled(enabled) {
23
32
  this.enabled = enabled;
@@ -34,6 +43,15 @@ class DooverStatsCollector {
34
43
  this.rLastLatency = null;
35
44
  this.gSent = 0;
36
45
  this.gReceived = 0;
46
+ this.rpTotal = 0;
47
+ this.rpPending = 0;
48
+ this.rpCompleted = 0;
49
+ this.rpFailed = 0;
50
+ this.rpTimedOut = 0;
51
+ this.rpAborted = 0;
52
+ this.rpLatencySum = 0;
53
+ this.rpLastLatency = null;
54
+ this.rpPeakPending = 0;
37
55
  }
38
56
  /**
39
57
  * Record the start of a REST request. Returns the start timestamp the
@@ -68,8 +86,40 @@ class DooverStatsCollector {
68
86
  return;
69
87
  this.gReceived += 1;
70
88
  }
89
+ recordRpcStart() {
90
+ if (!this.enabled)
91
+ return null;
92
+ this.rpTotal += 1;
93
+ this.rpPending += 1;
94
+ if (this.rpPending > this.rpPeakPending)
95
+ this.rpPeakPending = this.rpPending;
96
+ return now();
97
+ }
98
+ recordRpcEnd(startedAt, outcome) {
99
+ if (!this.enabled || startedAt === null)
100
+ return;
101
+ this.rpPending = Math.max(0, this.rpPending - 1);
102
+ switch (outcome) {
103
+ case "success":
104
+ this.rpCompleted += 1;
105
+ break;
106
+ case "error":
107
+ this.rpFailed += 1;
108
+ break;
109
+ case "timeout":
110
+ this.rpTimedOut += 1;
111
+ break;
112
+ case "abort":
113
+ this.rpAborted += 1;
114
+ break;
115
+ }
116
+ const latency = now() - startedAt;
117
+ this.rpLastLatency = latency;
118
+ this.rpLatencySum += latency;
119
+ }
71
120
  snapshot() {
72
121
  const settled = this.rCompleted + this.rFailed;
122
+ const rpcSettled = this.rpCompleted + this.rpFailed + this.rpTimedOut + this.rpAborted;
73
123
  return {
74
124
  rest: {
75
125
  enabled: this.enabled,
@@ -85,6 +135,18 @@ class DooverStatsCollector {
85
135
  messagesSent: this.gSent,
86
136
  messagesReceived: this.gReceived,
87
137
  },
138
+ rpc: {
139
+ enabled: this.enabled,
140
+ totalRpcs: this.rpTotal,
141
+ pendingRpcs: this.rpPending,
142
+ completedRpcs: this.rpCompleted,
143
+ failedRpcs: this.rpFailed,
144
+ timedOutRpcs: this.rpTimedOut,
145
+ abortedRpcs: this.rpAborted,
146
+ averageLatencyMs: rpcSettled > 0 ? this.rpLatencySum / rpcSettled : null,
147
+ lastLatencyMs: this.rpLastLatency,
148
+ peakPendingRpcs: this.rpPeakPending,
149
+ },
88
150
  };
89
151
  }
90
152
  }
@@ -2,7 +2,12 @@ import type { DooverAuth } from "../auth/doover-auth";
2
2
  import type { DooverStatsCollector } from "../client/stats";
3
3
  import type { DooverClientConfig } from "../http/rest-client";
4
4
  import type { GatewayListenerMap, WebSocketSession } from "./types";
5
- import type { ChannelRef, JSONValue } from "../types/common";
5
+ import type { Aggregate, ChannelRef, JSONValue, MessageStructure } from "../types/common";
6
+ export interface ChannelHandlers {
7
+ onMessage?: (msg: MessageStructure) => void;
8
+ onMessageUpdate?: (msg: MessageStructure, requestData?: JSONValue) => void;
9
+ onAggregate?: (agg: Aggregate) => void;
10
+ }
6
11
  export declare class GatewayClient {
7
12
  private readonly config;
8
13
  private socket;
@@ -15,6 +20,7 @@ export declare class GatewayClient {
15
20
  private opening;
16
21
  private listeners;
17
22
  private subscriptions;
23
+ private channelRegistry;
18
24
  private readonly auth;
19
25
  private stats;
20
26
  constructor(config: DooverClientConfig, auth?: DooverAuth);
@@ -29,6 +35,12 @@ export declare class GatewayClient {
29
35
  diff_only?: boolean;
30
36
  }): void;
31
37
  unsubscribe(channel: ChannelRef): void;
38
+ /**
39
+ * Attach channel-scoped handlers. Returns an idempotent unsubscribe fn.
40
+ * The first handler for a channel triggers a wire-level subscribe; the
41
+ * last detach triggers a wire-level unsubscribe.
42
+ */
43
+ subscribeToChannel(channel: ChannelRef, handlers: ChannelHandlers): () => void;
32
44
  syncChannel(channel: ChannelRef): void;
33
45
  sendOneShotMessage(channel: ChannelRef, data: JSONValue): void;
34
46
  getSession(): WebSocketSession | null;
@@ -32,6 +32,7 @@ class GatewayClient {
32
32
  close: new Set(),
33
33
  };
34
34
  this.subscriptions = new Map();
35
+ this.channelRegistry = new Map();
35
36
  this.stats = null;
36
37
  this.handleVisibilityChange = () => {
37
38
  if (typeof document === "undefined")
@@ -151,6 +152,59 @@ class GatewayClient {
151
152
  },
152
153
  });
153
154
  }
155
+ /**
156
+ * Attach channel-scoped handlers. Returns an idempotent unsubscribe fn.
157
+ * The first handler for a channel triggers a wire-level subscribe; the
158
+ * last detach triggers a wire-level unsubscribe.
159
+ */
160
+ subscribeToChannel(channel, handlers) {
161
+ const key = this.channelKey(channel);
162
+ let entry = this.channelRegistry.get(key);
163
+ if (!entry) {
164
+ entry = { handlers: new Set(), cleanup: [] };
165
+ this.channelRegistry.set(key, entry);
166
+ const matches = (c) => c.agent_id === channel.agent_id && c.name === channel.name;
167
+ const onMsgCreate = (msg) => {
168
+ if (!matches(msg.channel))
169
+ return;
170
+ entry.handlers.forEach((h) => h.onMessage?.(msg));
171
+ };
172
+ const onMsgUpdate = (msg, requestData) => {
173
+ if (!matches(msg.channel))
174
+ return;
175
+ entry.handlers.forEach((h) => h.onMessageUpdate?.(msg, requestData));
176
+ };
177
+ const onAggregate = (event) => {
178
+ if (!matches(event.channel))
179
+ return;
180
+ entry.handlers.forEach((h) => h.onAggregate?.(event.aggregate));
181
+ };
182
+ const onChannelSync = (event) => {
183
+ if (!matches(event.channel))
184
+ return;
185
+ entry.handlers.forEach((h) => h.onAggregate?.(event.aggregate));
186
+ };
187
+ this.on("messageCreate", onMsgCreate);
188
+ this.on("messageUpdate", onMsgUpdate);
189
+ this.on("aggregateUpdate", onAggregate);
190
+ this.on("channelSync", onChannelSync);
191
+ entry.cleanup.push(() => this.off("messageCreate", onMsgCreate), () => this.off("messageUpdate", onMsgUpdate), () => this.off("aggregateUpdate", onAggregate), () => this.off("channelSync", onChannelSync));
192
+ this.subscribe(channel);
193
+ }
194
+ entry.handlers.add(handlers);
195
+ let detached = false;
196
+ return () => {
197
+ if (detached)
198
+ return;
199
+ detached = true;
200
+ entry.handlers.delete(handlers);
201
+ if (entry.handlers.size === 0) {
202
+ entry.cleanup.forEach((fn) => fn());
203
+ this.channelRegistry.delete(key);
204
+ this.unsubscribe(channel);
205
+ }
206
+ };
207
+ }
154
208
  syncChannel(channel) {
155
209
  if (!this.isConnected()) {
156
210
  void this.connect();
package/dist/index.d.ts CHANGED
@@ -12,6 +12,12 @@ export { NotificationsApi } from "./apis/notifications-api";
12
12
  export { PermissionsApi } from "./apis/permissions-api";
13
13
  export { ProcessorsApi } from "./apis/processors-api";
14
14
  export { TurnApi } from "./apis/turn-api";
15
+ export { UsersApi } from "./apis/users-api";
16
+ export { RpcDispatcher } from "./rpc/rpc-dispatcher";
17
+ export type { SendRpcOptions } from "./rpc/rpc-dispatcher";
18
+ export { DooverRpcError } from "./rpc/errors";
19
+ export type { ChannelHandlers } from "./gateway/gateway-client";
20
+ export type { RpcStatsSnapshot } from "./client/stats";
15
21
  export { DooverAuth } from "./auth/doover-auth";
16
22
  export { CookieAuth } from "./auth/cookie-auth";
17
23
  export { DooverTokenAuth } from "./auth/doover-token-auth";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.buildAuth = exports.DooverAuthError = exports.AuthProfile = exports.DooverTokenAuth = exports.CookieAuth = exports.DooverAuth = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverStatsCollector = exports.resetDooverClient = exports.peekDooverClient = exports.getDooverClient = exports.DooverClient = void 0;
3
+ exports.generateSnowflakeIdAtTime = exports.extractSnowflakeId = exports.addTimestampToMessage = exports.getIdentifierFromPath = exports.DooverDataProvider = exports.DooverValidationError = exports.DooverGatewayError = exports.DooverApiError = exports.RestClient = exports.GatewayClient = exports.buildAuth = exports.DooverAuthError = exports.AuthProfile = exports.DooverTokenAuth = exports.CookieAuth = exports.DooverAuth = exports.DooverRpcError = exports.RpcDispatcher = exports.UsersApi = exports.TurnApi = exports.ProcessorsApi = exports.PermissionsApi = exports.NotificationsApi = exports.MessagesApi = exports.ConnectionsApi = exports.ChannelsApi = exports.AlarmsApi = exports.AggregatesApi = exports.AgentsApi = exports.DooverStatsCollector = exports.resetDooverClient = exports.peekDooverClient = exports.getDooverClient = exports.DooverClient = void 0;
4
4
  var doover_client_1 = require("./client/doover-client");
5
5
  Object.defineProperty(exports, "DooverClient", { enumerable: true, get: function () { return doover_client_1.DooverClient; } });
6
6
  var singleton_1 = require("./client/singleton");
@@ -29,6 +29,12 @@ var processors_api_1 = require("./apis/processors-api");
29
29
  Object.defineProperty(exports, "ProcessorsApi", { enumerable: true, get: function () { return processors_api_1.ProcessorsApi; } });
30
30
  var turn_api_1 = require("./apis/turn-api");
31
31
  Object.defineProperty(exports, "TurnApi", { enumerable: true, get: function () { return turn_api_1.TurnApi; } });
32
+ var users_api_1 = require("./apis/users-api");
33
+ Object.defineProperty(exports, "UsersApi", { enumerable: true, get: function () { return users_api_1.UsersApi; } });
34
+ var rpc_dispatcher_1 = require("./rpc/rpc-dispatcher");
35
+ Object.defineProperty(exports, "RpcDispatcher", { enumerable: true, get: function () { return rpc_dispatcher_1.RpcDispatcher; } });
36
+ var errors_1 = require("./rpc/errors");
37
+ Object.defineProperty(exports, "DooverRpcError", { enumerable: true, get: function () { return errors_1.DooverRpcError; } });
32
38
  var doover_auth_1 = require("./auth/doover-auth");
33
39
  Object.defineProperty(exports, "DooverAuth", { enumerable: true, get: function () { return doover_auth_1.DooverAuth; } });
34
40
  var cookie_auth_1 = require("./auth/cookie-auth");
@@ -37,18 +43,18 @@ var doover_token_auth_1 = require("./auth/doover-token-auth");
37
43
  Object.defineProperty(exports, "DooverTokenAuth", { enumerable: true, get: function () { return doover_token_auth_1.DooverTokenAuth; } });
38
44
  var auth_profile_1 = require("./auth/auth-profile");
39
45
  Object.defineProperty(exports, "AuthProfile", { enumerable: true, get: function () { return auth_profile_1.AuthProfile; } });
40
- var errors_1 = require("./auth/errors");
41
- Object.defineProperty(exports, "DooverAuthError", { enumerable: true, get: function () { return errors_1.DooverAuthError; } });
46
+ var errors_2 = require("./auth/errors");
47
+ Object.defineProperty(exports, "DooverAuthError", { enumerable: true, get: function () { return errors_2.DooverAuthError; } });
42
48
  var build_auth_1 = require("./auth/build-auth");
43
49
  Object.defineProperty(exports, "buildAuth", { enumerable: true, get: function () { return build_auth_1.buildAuth; } });
44
50
  var gateway_client_1 = require("./gateway/gateway-client");
45
51
  Object.defineProperty(exports, "GatewayClient", { enumerable: true, get: function () { return gateway_client_1.GatewayClient; } });
46
52
  var rest_client_1 = require("./http/rest-client");
47
53
  Object.defineProperty(exports, "RestClient", { enumerable: true, get: function () { return rest_client_1.RestClient; } });
48
- var errors_2 = require("./http/errors");
49
- Object.defineProperty(exports, "DooverApiError", { enumerable: true, get: function () { return errors_2.DooverApiError; } });
50
- Object.defineProperty(exports, "DooverGatewayError", { enumerable: true, get: function () { return errors_2.DooverGatewayError; } });
51
- Object.defineProperty(exports, "DooverValidationError", { enumerable: true, get: function () { return errors_2.DooverValidationError; } });
54
+ var errors_3 = require("./http/errors");
55
+ Object.defineProperty(exports, "DooverApiError", { enumerable: true, get: function () { return errors_3.DooverApiError; } });
56
+ Object.defineProperty(exports, "DooverGatewayError", { enumerable: true, get: function () { return errors_3.DooverGatewayError; } });
57
+ Object.defineProperty(exports, "DooverValidationError", { enumerable: true, get: function () { return errors_3.DooverValidationError; } });
52
58
  var doover_data_provider_1 = require("./viewer/doover-data-provider");
53
59
  Object.defineProperty(exports, "DooverDataProvider", { enumerable: true, get: function () { return doover_data_provider_1.DooverDataProvider; } });
54
60
  var path_parsing_1 = require("./viewer/path-parsing");
@@ -17,6 +17,8 @@ export { useChannelMessages, channelMessagesQueryKey, } from "./useChannelMessag
17
17
  export type { UseChannelMessagesOptions, UseChannelMessagesResult, } from "./useChannelMessages";
18
18
  export { useChannelMessage, channelMessageQueryKey, } from "./useChannelMessage";
19
19
  export type { UseChannelMessageOptions, UseChannelMessageResult, } from "./useChannelMessage";
20
+ export { useInvocationLogs, invocationLogsQueryKey, } from "./useInvocationLogs";
21
+ export type { UseInvocationLogsOptions } from "./useInvocationLogs";
20
22
  export { useSendRpc } from "./useSendRpc";
21
23
  export type { RpcCommandId, RpcStatusEvent, SendRpcVariables, UseSendRpcOptions, UseSendRpcResult, } from "./useSendRpc";
22
24
  export { useMultiAgentAggregates, multiAgentAggregatesQueryKey, } from "./useMultiAgentAggregates";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resetSharedQueryClient = exports.getSharedQueryClient = exports.useTurnCredentials = exports.multiAgentChannelMessagesQueryKey = exports.useMultiAgentChannelMessages = exports.multiAgentAggregatesQueryKey = exports.useMultiAgentAggregates = exports.useSendRpc = exports.channelMessageQueryKey = exports.useChannelMessage = exports.channelMessagesQueryKey = exports.useChannelMessages = exports.useUpdateMessage = exports.useUpdateAggregate = exports.useSendMessage = exports.useAgentChannel = exports.channelAggregateQueryKey = exports.useChannelAggregate = exports.useChannelSubscription = exports.agentConnectionsQueryKey = exports.useAgentConnections = exports.useConnectionState = exports.useDooverClient = exports.DooverProvider = void 0;
3
+ exports.resetSharedQueryClient = exports.getSharedQueryClient = exports.useTurnCredentials = exports.multiAgentChannelMessagesQueryKey = exports.useMultiAgentChannelMessages = exports.multiAgentAggregatesQueryKey = exports.useMultiAgentAggregates = exports.useSendRpc = exports.invocationLogsQueryKey = exports.useInvocationLogs = exports.channelMessageQueryKey = exports.useChannelMessage = exports.channelMessagesQueryKey = exports.useChannelMessages = exports.useUpdateMessage = exports.useUpdateAggregate = exports.useSendMessage = exports.useAgentChannel = exports.channelAggregateQueryKey = exports.useChannelAggregate = exports.useChannelSubscription = exports.agentConnectionsQueryKey = exports.useAgentConnections = exports.useConnectionState = exports.useDooverClient = exports.DooverProvider = void 0;
4
4
  var context_1 = require("./context");
5
5
  Object.defineProperty(exports, "DooverProvider", { enumerable: true, get: function () { return context_1.DooverProvider; } });
6
6
  Object.defineProperty(exports, "useDooverClient", { enumerable: true, get: function () { return context_1.useDooverClient; } });
@@ -28,6 +28,9 @@ Object.defineProperty(exports, "channelMessagesQueryKey", { enumerable: true, ge
28
28
  var useChannelMessage_1 = require("./useChannelMessage");
29
29
  Object.defineProperty(exports, "useChannelMessage", { enumerable: true, get: function () { return useChannelMessage_1.useChannelMessage; } });
30
30
  Object.defineProperty(exports, "channelMessageQueryKey", { enumerable: true, get: function () { return useChannelMessage_1.channelMessageQueryKey; } });
31
+ var useInvocationLogs_1 = require("./useInvocationLogs");
32
+ Object.defineProperty(exports, "useInvocationLogs", { enumerable: true, get: function () { return useInvocationLogs_1.useInvocationLogs; } });
33
+ Object.defineProperty(exports, "invocationLogsQueryKey", { enumerable: true, get: function () { return useInvocationLogs_1.invocationLogsQueryKey; } });
31
34
  var useSendRpc_1 = require("./useSendRpc");
32
35
  Object.defineProperty(exports, "useSendRpc", { enumerable: true, get: function () { return useSendRpc_1.useSendRpc; } });
33
36
  var useMultiAgentAggregates_1 = require("./useMultiAgentAggregates");
@@ -38,7 +38,14 @@ function useChannelAggregate(identifier, options) {
38
38
  queryKey: key,
39
39
  enabled: fetchInitial && !!agentId && !!channelName,
40
40
  staleTime: Infinity,
41
- queryFn: () => client.viewer.getAggregate(identifier),
41
+ queryFn: async () => {
42
+ if (!agentId || !channelName)
43
+ return undefined;
44
+ const channel = await client.channels.getChannel({ agentId, channelName });
45
+ if (channel.aggregate)
46
+ return channel.aggregate;
47
+ return (await client.aggregates.getAggregate({ agentId, channelName }));
48
+ },
42
49
  // A 404 means the aggregate doesn't exist — retrying won't change that,
43
50
  // and the caller needs the error promptly to render an empty/"not
44
51
  // installed" state. Fall back to the react-query default (3 retries) for
@@ -48,13 +48,16 @@ function useChannelMessages(identifier, options) {
48
48
  initialPageParam: initialBefore,
49
49
  getNextPageParam: (lastPage) => lastPage && lastPage.length > 0 ? lastPage[0]?.id : undefined,
50
50
  queryFn: async ({ pageParam }) => {
51
- const page = await client.viewer.getMessages(identifier, {
51
+ if (!agentId || !channelName)
52
+ return [];
53
+ const page = await client.messages.listMessages({ agentId, channelName }, {
52
54
  ...(typeof pageParam === "string" ? { before: pageParam } : {}),
53
55
  ...(limit !== undefined ? { limit } : {}),
54
56
  ...(fields && fields.length > 0 ? { field_name: fields } : {}),
55
57
  ...(after !== undefined ? { after } : {}),
58
+ order: "asc",
56
59
  });
57
- return (page ?? []);
60
+ return page;
58
61
  },
59
62
  });
60
63
  (0, react_1.useEffect)(() => {
@@ -18,24 +18,14 @@ function useChannelSubscription(identifier, handlers) {
18
18
  (0, react_1.useEffect)(() => {
19
19
  if (!identifier?.agentId || !identifier?.channelName)
20
20
  return;
21
- const messageCallback = (_id, message) => {
22
- handlersRef.current.onMessage?.(message);
23
- };
24
- const aggregateCallback = (_id, aggregate) => {
25
- handlersRef.current.onAggregate?.(aggregate);
26
- };
27
- const messageUpdateCallback = (_id, message, request_data) => {
28
- handlersRef.current.onMessageUpdate?.(message, request_data);
29
- };
30
- void client.viewer.subscribeToChannel(identifier, messageCallback, aggregateCallback, messageUpdateCallback);
31
- return () => {
32
- client.viewer
33
- .unsubscribeFromChannel(identifier, messageCallback)
34
- .catch(() => {
35
- // Already unsubscribed — race between strict-mode double-effect
36
- // and async subscribe is harmless.
37
- });
38
- };
21
+ const channel = { agent_id: identifier.agentId, name: identifier.channelName };
22
+ const off = client.gateway.subscribeToChannel(channel, {
23
+ onMessage: (msg) => handlersRef.current.onMessage?.(msg),
24
+ onMessageUpdate: (msg, rd) => handlersRef.current.onMessageUpdate?.(msg, rd),
25
+ onAggregate: (agg) => handlersRef.current.onAggregate?.(agg),
26
+ });
27
+ void client.gateway.connect();
28
+ return () => off();
39
29
  // Identifier fields are the real deps; we intentionally leave `handlers`
40
30
  // out because it's read through handlersRef.
41
31
  // eslint-disable-next-line react-hooks/exhaustive-deps