@butlr/butlr-mcp-server 0.1.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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +195 -0
  3. package/dist/cache/occupancy-cache.d.ts +72 -0
  4. package/dist/cache/occupancy-cache.d.ts.map +1 -0
  5. package/dist/cache/occupancy-cache.js +166 -0
  6. package/dist/cache/occupancy-cache.js.map +1 -0
  7. package/dist/cache/topology-cache.d.ts +36 -0
  8. package/dist/cache/topology-cache.d.ts.map +1 -0
  9. package/dist/cache/topology-cache.js +74 -0
  10. package/dist/cache/topology-cache.js.map +1 -0
  11. package/dist/clients/auth-client.d.ts +22 -0
  12. package/dist/clients/auth-client.d.ts.map +1 -0
  13. package/dist/clients/auth-client.js +82 -0
  14. package/dist/clients/auth-client.js.map +1 -0
  15. package/dist/clients/graphql-client.d.ts +6 -0
  16. package/dist/clients/graphql-client.d.ts.map +1 -0
  17. package/dist/clients/graphql-client.js +106 -0
  18. package/dist/clients/graphql-client.js.map +1 -0
  19. package/dist/clients/queries/topology.d.ts +36 -0
  20. package/dist/clients/queries/topology.d.ts.map +1 -0
  21. package/dist/clients/queries/topology.js +252 -0
  22. package/dist/clients/queries/topology.js.map +1 -0
  23. package/dist/clients/reporting-client.d.ts +191 -0
  24. package/dist/clients/reporting-client.d.ts.map +1 -0
  25. package/dist/clients/reporting-client.js +353 -0
  26. package/dist/clients/reporting-client.js.map +1 -0
  27. package/dist/clients/stats-client.d.ts +119 -0
  28. package/dist/clients/stats-client.d.ts.map +1 -0
  29. package/dist/clients/stats-client.js +238 -0
  30. package/dist/clients/stats-client.js.map +1 -0
  31. package/dist/clients/types.d.ts +215 -0
  32. package/dist/clients/types.d.ts.map +1 -0
  33. package/dist/clients/types.js +6 -0
  34. package/dist/clients/types.js.map +1 -0
  35. package/dist/constants.d.ts +3 -0
  36. package/dist/constants.d.ts.map +1 -0
  37. package/dist/constants.js +3 -0
  38. package/dist/constants.js.map +1 -0
  39. package/dist/errors/mcp-errors.d.ts +63 -0
  40. package/dist/errors/mcp-errors.d.ts.map +1 -0
  41. package/dist/errors/mcp-errors.js +144 -0
  42. package/dist/errors/mcp-errors.js.map +1 -0
  43. package/dist/index.d.ts +3 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +43 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/tools/butlr-available-rooms.d.ts +49 -0
  48. package/dist/tools/butlr-available-rooms.d.ts.map +1 -0
  49. package/dist/tools/butlr-available-rooms.js +492 -0
  50. package/dist/tools/butlr-available-rooms.js.map +1 -0
  51. package/dist/tools/butlr-fetch-entity-details.d.ts +42 -0
  52. package/dist/tools/butlr-fetch-entity-details.d.ts.map +1 -0
  53. package/dist/tools/butlr-fetch-entity-details.js +276 -0
  54. package/dist/tools/butlr-fetch-entity-details.js.map +1 -0
  55. package/dist/tools/butlr-get-asset-details.d.ts +51 -0
  56. package/dist/tools/butlr-get-asset-details.d.ts.map +1 -0
  57. package/dist/tools/butlr-get-asset-details.js +391 -0
  58. package/dist/tools/butlr-get-asset-details.js.map +1 -0
  59. package/dist/tools/butlr-get-current-occupancy.d.ts +21 -0
  60. package/dist/tools/butlr-get-current-occupancy.d.ts.map +1 -0
  61. package/dist/tools/butlr-get-current-occupancy.js +126 -0
  62. package/dist/tools/butlr-get-current-occupancy.js.map +1 -0
  63. package/dist/tools/butlr-get-occupancy-timeseries.d.ts +31 -0
  64. package/dist/tools/butlr-get-occupancy-timeseries.d.ts.map +1 -0
  65. package/dist/tools/butlr-get-occupancy-timeseries.js +145 -0
  66. package/dist/tools/butlr-get-occupancy-timeseries.js.map +1 -0
  67. package/dist/tools/butlr-hardware-snapshot.d.ts +55 -0
  68. package/dist/tools/butlr-hardware-snapshot.d.ts.map +1 -0
  69. package/dist/tools/butlr-hardware-snapshot.js +556 -0
  70. package/dist/tools/butlr-hardware-snapshot.js.map +1 -0
  71. package/dist/tools/butlr-list-topology.d.ts +27 -0
  72. package/dist/tools/butlr-list-topology.d.ts.map +1 -0
  73. package/dist/tools/butlr-list-topology.js +241 -0
  74. package/dist/tools/butlr-list-topology.js.map +1 -0
  75. package/dist/tools/butlr-search-assets.d.ts +53 -0
  76. package/dist/tools/butlr-search-assets.d.ts.map +1 -0
  77. package/dist/tools/butlr-search-assets.js +206 -0
  78. package/dist/tools/butlr-search-assets.js.map +1 -0
  79. package/dist/tools/butlr-space-busyness.d.ts +23 -0
  80. package/dist/tools/butlr-space-busyness.d.ts.map +1 -0
  81. package/dist/tools/butlr-space-busyness.js +304 -0
  82. package/dist/tools/butlr-space-busyness.js.map +1 -0
  83. package/dist/tools/butlr-traffic-flow.d.ts +39 -0
  84. package/dist/tools/butlr-traffic-flow.d.ts.map +1 -0
  85. package/dist/tools/butlr-traffic-flow.js +369 -0
  86. package/dist/tools/butlr-traffic-flow.js.map +1 -0
  87. package/dist/types/responses.d.ts +253 -0
  88. package/dist/types/responses.d.ts.map +1 -0
  89. package/dist/types/responses.js +8 -0
  90. package/dist/types/responses.js.map +1 -0
  91. package/dist/utils/asset-flattener.d.ts +50 -0
  92. package/dist/utils/asset-flattener.d.ts.map +1 -0
  93. package/dist/utils/asset-flattener.js +131 -0
  94. package/dist/utils/asset-flattener.js.map +1 -0
  95. package/dist/utils/asset-helpers.d.ts +8 -0
  96. package/dist/utils/asset-helpers.d.ts.map +1 -0
  97. package/dist/utils/asset-helpers.js +24 -0
  98. package/dist/utils/asset-helpers.js.map +1 -0
  99. package/dist/utils/field-validator.d.ts +29 -0
  100. package/dist/utils/field-validator.d.ts.map +1 -0
  101. package/dist/utils/field-validator.js +197 -0
  102. package/dist/utils/field-validator.js.map +1 -0
  103. package/dist/utils/fuzzy-match.d.ts +29 -0
  104. package/dist/utils/fuzzy-match.d.ts.map +1 -0
  105. package/dist/utils/fuzzy-match.js +70 -0
  106. package/dist/utils/fuzzy-match.js.map +1 -0
  107. package/dist/utils/graphql-helpers.d.ts +29 -0
  108. package/dist/utils/graphql-helpers.d.ts.map +1 -0
  109. package/dist/utils/graphql-helpers.js +43 -0
  110. package/dist/utils/graphql-helpers.js.map +1 -0
  111. package/dist/utils/natural-language.d.ts +73 -0
  112. package/dist/utils/natural-language.d.ts.map +1 -0
  113. package/dist/utils/natural-language.js +172 -0
  114. package/dist/utils/natural-language.js.map +1 -0
  115. package/dist/utils/occupancy-helpers.d.ts +63 -0
  116. package/dist/utils/occupancy-helpers.d.ts.map +1 -0
  117. package/dist/utils/occupancy-helpers.js +203 -0
  118. package/dist/utils/occupancy-helpers.js.map +1 -0
  119. package/dist/utils/path-builder.d.ts +10 -0
  120. package/dist/utils/path-builder.d.ts.map +1 -0
  121. package/dist/utils/path-builder.js +34 -0
  122. package/dist/utils/path-builder.js.map +1 -0
  123. package/dist/utils/time-range-validator.d.ts +13 -0
  124. package/dist/utils/time-range-validator.d.ts.map +1 -0
  125. package/dist/utils/time-range-validator.js +65 -0
  126. package/dist/utils/time-range-validator.js.map +1 -0
  127. package/dist/utils/timezone-helpers.d.ts +49 -0
  128. package/dist/utils/timezone-helpers.d.ts.map +1 -0
  129. package/dist/utils/timezone-helpers.js +209 -0
  130. package/dist/utils/timezone-helpers.js.map +1 -0
  131. package/dist/utils/tree-formatter.d.ts +23 -0
  132. package/dist/utils/tree-formatter.d.ts.map +1 -0
  133. package/dist/utils/tree-formatter.js +258 -0
  134. package/dist/utils/tree-formatter.js.map +1 -0
  135. package/package.json +93 -0
@@ -0,0 +1,82 @@
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+ const BASE_URL = process.env.BUTLR_BASE_URL || "https://api.butlr.io";
4
+ const AUTH_ENDPOINT = `${BASE_URL}/api/v2/clients/login`;
5
+ /**
6
+ * Butlr Authentication Client
7
+ * Manages OAuth2 client credentials flow for API authentication
8
+ */
9
+ class ButlrAuthClient {
10
+ token = null;
11
+ tokenExpiry = null;
12
+ clientId;
13
+ clientSecret;
14
+ constructor() {
15
+ this.clientId = process.env.BUTLR_CLIENT_ID || "";
16
+ this.clientSecret = process.env.BUTLR_CLIENT_SECRET || "";
17
+ }
18
+ /**
19
+ * Get a valid access token, fetching a new one if needed
20
+ */
21
+ async getToken() {
22
+ // Validate credentials on first use (deferred from constructor)
23
+ if (!this.clientId || !this.clientSecret) {
24
+ throw new Error("BUTLR_CLIENT_ID and BUTLR_CLIENT_SECRET environment variables are required. " +
25
+ "See README.md for configuration instructions.");
26
+ }
27
+ // Return cached token if still valid (with 5 minute buffer)
28
+ if (this.token && this.tokenExpiry && Date.now() < this.tokenExpiry.getTime() - 5 * 60 * 1000) {
29
+ if (process.env.DEBUG) {
30
+ console.error("[auth-client] Using cached token");
31
+ }
32
+ return this.token;
33
+ }
34
+ if (process.env.DEBUG) {
35
+ console.error("[auth-client] Fetching new token...");
36
+ }
37
+ // Fetch new token
38
+ try {
39
+ const response = await fetch(AUTH_ENDPOINT, {
40
+ method: "POST",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ },
44
+ body: JSON.stringify({
45
+ client_id: this.clientId,
46
+ client_secret: this.clientSecret,
47
+ audience: "https://butlrauth/",
48
+ grant_type: "client_credentials",
49
+ }),
50
+ });
51
+ if (!response.ok) {
52
+ const errorText = await response.text();
53
+ throw new Error(`Authentication failed (${response.status}): ${errorText}`);
54
+ }
55
+ const data = (await response.json());
56
+ if (!data.access_token) {
57
+ throw new Error("No access_token in response");
58
+ }
59
+ // Cache the token
60
+ this.token = data.access_token;
61
+ this.tokenExpiry = new Date(Date.now() + data.expires_in * 1000);
62
+ if (process.env.DEBUG) {
63
+ console.error(`[auth-client] Token acquired, expires at ${this.tokenExpiry.toISOString()}`);
64
+ }
65
+ return this.token;
66
+ }
67
+ catch (error) {
68
+ console.error("[auth-client] Token fetch failed:", error);
69
+ throw new Error(`Failed to authenticate with Butlr API: ${error instanceof Error ? error.message : String(error)}`);
70
+ }
71
+ }
72
+ /**
73
+ * Clear cached token (useful for testing)
74
+ */
75
+ clearToken() {
76
+ this.token = null;
77
+ this.tokenExpiry = null;
78
+ }
79
+ }
80
+ // Singleton instance
81
+ export const authClient = new ButlrAuthClient();
82
+ //# sourceMappingURL=auth-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../../src/clients/auth-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { ApolloClient } from "@apollo/client";
2
+ /**
3
+ * Apollo Client instance with authentication and error handling
4
+ */
5
+ export declare const apolloClient: ApolloClient;
6
+ //# sourceMappingURL=graphql-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-client.d.ts","sourceRoot":"","sources":["../../src/clients/graphql-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuC,MAAM,gBAAgB,CAAC;AAiFnF;;GAEG;AACH,eAAO,MAAM,YAAY,cAkCvB,CAAC"}
@@ -0,0 +1,106 @@
1
+ import { ApolloClient, InMemoryCache, createHttpLink, from } from "@apollo/client";
2
+ import { setContext } from "@apollo/client/link/context";
3
+ import { onError } from "@apollo/client/link/error";
4
+ import { authClient } from "./auth-client.js";
5
+ const BASE_URL = process.env.BUTLR_BASE_URL || "https://api.butlr.io";
6
+ const GRAPHQL_ENDPOINT = `${BASE_URL}/api/v3/graphql`;
7
+ /**
8
+ * HTTP link for GraphQL endpoint
9
+ */
10
+ const httpLink = createHttpLink({
11
+ uri: GRAPHQL_ENDPOINT,
12
+ fetch,
13
+ });
14
+ /**
15
+ * Authentication link - adds Bearer token to every request
16
+ */
17
+ const authLink = setContext(async (_, { headers }) => {
18
+ try {
19
+ const token = await authClient.getToken();
20
+ return {
21
+ headers: {
22
+ ...headers,
23
+ authorization: `Bearer ${token}`,
24
+ },
25
+ };
26
+ }
27
+ catch (error) {
28
+ console.error("[graphql-client] Failed to get auth token:", error);
29
+ throw error;
30
+ }
31
+ });
32
+ /**
33
+ * Error handling link - logs errors and provides debugging info
34
+ */
35
+ const errorLink = onError((errorResponse) => {
36
+ const { operation } = errorResponse;
37
+ // Apollo 4.x ErrorHandlerOptions has a single 'error' field.
38
+ // Cast to access graphQLErrors/networkError which may exist at runtime
39
+ // depending on the Apollo version's actual implementation.
40
+ const resp = errorResponse;
41
+ const graphQLErrors = resp.graphQLErrors;
42
+ const networkError = resp.networkError;
43
+ const topError = resp.error;
44
+ if (graphQLErrors) {
45
+ for (const err of graphQLErrors) {
46
+ console.error(`[graphql-client] GraphQL error in ${operation.operationName}:`, err.message);
47
+ if (err.extensions?.code === "UNAUTHENTICATED") {
48
+ authClient.clearToken();
49
+ }
50
+ }
51
+ }
52
+ if (networkError) {
53
+ console.error(`[graphql-client] Network error in ${operation.operationName}:`, networkError.message);
54
+ if (networkError.statusCode === 401 || networkError.statusCode === 403) {
55
+ authClient.clearToken();
56
+ }
57
+ }
58
+ // Fallback for Apollo 4.x single error field
59
+ if (!graphQLErrors && !networkError && topError) {
60
+ console.error(`[graphql-client] Error in ${operation.operationName}:`, topError.message);
61
+ if (topError.message?.includes("UNAUTHENTICATED") ||
62
+ topError.message?.includes("401") ||
63
+ topError.message?.includes("403")) {
64
+ authClient.clearToken();
65
+ }
66
+ }
67
+ });
68
+ /**
69
+ * Apollo Client instance with authentication and error handling
70
+ */
71
+ export const apolloClient = new ApolloClient({
72
+ link: from([errorLink, authLink, httpLink]),
73
+ cache: new InMemoryCache({
74
+ typePolicies: {
75
+ // Customize caching behavior if needed
76
+ Site: {
77
+ keyFields: ["id"],
78
+ },
79
+ Building: {
80
+ keyFields: ["id"],
81
+ },
82
+ Floor: {
83
+ keyFields: ["id"],
84
+ },
85
+ Room: {
86
+ keyFields: ["id"],
87
+ },
88
+ Zone: {
89
+ keyFields: ["id"],
90
+ },
91
+ Sensor: {
92
+ keyFields: ["id"],
93
+ },
94
+ Hive: {
95
+ keyFields: ["id"],
96
+ },
97
+ },
98
+ }),
99
+ defaultOptions: {
100
+ query: {
101
+ fetchPolicy: "network-only", // Always fetch fresh data from API
102
+ errorPolicy: "all", // Return partial data on errors
103
+ },
104
+ },
105
+ });
106
+ //# sourceMappingURL=graphql-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphql-client.js","sourceRoot":"","sources":["../../src/clients/graphql-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * GraphQL queries for topology retrieval
3
+ * Adapted from gauntlet-app for MCP server use
4
+ */
5
+ /**
6
+ * Minimal sites list - fast loading, no nested data
7
+ */
8
+ export declare const GET_SITES_LIST: import("@apollo/client").DocumentNode;
9
+ /**
10
+ * Single site structure without sensors/hives
11
+ * Good for quick hierarchy lookup
12
+ */
13
+ export declare const GET_SITE_STRUCTURE: import("@apollo/client").DocumentNode;
14
+ /**
15
+ * Full topology with all sites, buildings, floors, rooms, zones
16
+ * NOTE: Does NOT include sensors/hives in nested fields (broken - only returns 5 sensors)
17
+ * Use GET_ALL_SENSORS and GET_ALL_HIVES separately, then merge by floor_id/room_id
18
+ */
19
+ export declare const GET_FULL_TOPOLOGY: import("@apollo/client").DocumentNode;
20
+ /**
21
+ * Get all sensors for the organization
22
+ * Uses snake_case fields (floor_id, room_id) which work correctly
23
+ * CamelCase fields (floorID, roomID) have buggy resolvers that fail for NULL values
24
+ */
25
+ export declare const GET_ALL_SENSORS: import("@apollo/client").DocumentNode;
26
+ /**
27
+ * Get all hives for the organization
28
+ * Uses snake_case fields (floor_id, room_id) for consistency
29
+ */
30
+ export declare const GET_ALL_HIVES: import("@apollo/client").DocumentNode;
31
+ /**
32
+ * Lightweight topology without devices
33
+ * Faster when sensor/hive data isn't needed
34
+ */
35
+ export declare const GET_TOPOLOGY_NO_DEVICES: import("@apollo/client").DocumentNode;
36
+ //# sourceMappingURL=topology.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topology.d.ts","sourceRoot":"","sources":["../../../src/clients/queries/topology.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,uCAY1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,uCA6B9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,uCAiE7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,uCAwB3B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,aAAa,uCAqBzB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,uCAgEnC,CAAC"}
@@ -0,0 +1,252 @@
1
+ import { gql } from "@apollo/client";
2
+ /**
3
+ * GraphQL queries for topology retrieval
4
+ * Adapted from gauntlet-app for MCP server use
5
+ */
6
+ /**
7
+ * Minimal sites list - fast loading, no nested data
8
+ */
9
+ export const GET_SITES_LIST = gql `
10
+ query GetSitesList {
11
+ sites {
12
+ data {
13
+ id
14
+ name
15
+ timezone
16
+ siteNumber
17
+ customID
18
+ }
19
+ }
20
+ }
21
+ `;
22
+ /**
23
+ * Single site structure without sensors/hives
24
+ * Good for quick hierarchy lookup
25
+ */
26
+ export const GET_SITE_STRUCTURE = gql `
27
+ query GetSiteStructure($siteId: ID!) {
28
+ site(id: $siteId) {
29
+ id
30
+ name
31
+ timezone
32
+ siteNumber
33
+ customID
34
+ buildings {
35
+ id
36
+ name
37
+ building_number
38
+ capacity {
39
+ max
40
+ mid
41
+ }
42
+ address {
43
+ lines
44
+ country
45
+ }
46
+ floors {
47
+ id
48
+ name
49
+ floorNumber
50
+ timezone
51
+ }
52
+ }
53
+ }
54
+ }
55
+ `;
56
+ /**
57
+ * Full topology with all sites, buildings, floors, rooms, zones
58
+ * NOTE: Does NOT include sensors/hives in nested fields (broken - only returns 5 sensors)
59
+ * Use GET_ALL_SENSORS and GET_ALL_HIVES separately, then merge by floor_id/room_id
60
+ */
61
+ export const GET_FULL_TOPOLOGY = gql `
62
+ query GetFullTopology {
63
+ sites {
64
+ data {
65
+ id
66
+ name
67
+ timezone
68
+ siteNumber
69
+ customID
70
+ org_id
71
+ buildings {
72
+ id
73
+ name
74
+ building_number
75
+ site_id
76
+ customID
77
+ capacity {
78
+ max
79
+ mid
80
+ }
81
+ address {
82
+ lines
83
+ country
84
+ }
85
+ floors {
86
+ id
87
+ name
88
+ floorNumber
89
+ building_id
90
+ timezone
91
+ installation_date
92
+ customID
93
+ capacity {
94
+ max
95
+ mid
96
+ }
97
+ area {
98
+ value
99
+ unit
100
+ }
101
+ rooms {
102
+ id
103
+ name
104
+ floor_id
105
+ roomType
106
+ customID
107
+ capacity {
108
+ max
109
+ mid
110
+ }
111
+ coordinates
112
+ }
113
+ zones {
114
+ id
115
+ name
116
+ floor_id
117
+ room_id
118
+ customID
119
+ coordinates
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ }
126
+ `;
127
+ /**
128
+ * Get all sensors for the organization
129
+ * Uses snake_case fields (floor_id, room_id) which work correctly
130
+ * CamelCase fields (floorID, roomID) have buggy resolvers that fail for NULL values
131
+ */
132
+ export const GET_ALL_SENSORS = gql `
133
+ query GetAllSensors {
134
+ sensors {
135
+ data {
136
+ id
137
+ name
138
+ mac_address
139
+ mode
140
+ model
141
+ floor_id
142
+ room_id
143
+ hive_serial
144
+ is_entrance
145
+ is_online
146
+ is_streaming
147
+ power_type
148
+ last_battery_change_date
149
+ next_battery_change_date
150
+ battery_change_by_date
151
+ last_heartbeat
152
+ installation_status
153
+ }
154
+ }
155
+ }
156
+ `;
157
+ /**
158
+ * Get all hives for the organization
159
+ * Uses snake_case fields (floor_id, room_id) for consistency
160
+ */
161
+ export const GET_ALL_HIVES = gql `
162
+ query GetAllHives {
163
+ hives {
164
+ data {
165
+ id
166
+ name
167
+ serialNumber
168
+ floor_id
169
+ room_id
170
+ isOnline
171
+ coordinates
172
+ isStreaming
173
+ hiveVersion
174
+ hiveType
175
+ note
176
+ lastHeartbeat
177
+ netPathStability
178
+ installed
179
+ }
180
+ }
181
+ }
182
+ `;
183
+ /**
184
+ * Lightweight topology without devices
185
+ * Faster when sensor/hive data isn't needed
186
+ */
187
+ export const GET_TOPOLOGY_NO_DEVICES = gql `
188
+ query GetTopologyNoDevices {
189
+ sites {
190
+ data {
191
+ id
192
+ name
193
+ timezone
194
+ siteNumber
195
+ customID
196
+ org_id
197
+ buildings {
198
+ id
199
+ name
200
+ building_number
201
+ site_id
202
+ customID
203
+ capacity {
204
+ max
205
+ mid
206
+ }
207
+ address {
208
+ lines
209
+ country
210
+ }
211
+ floors {
212
+ id
213
+ name
214
+ floorNumber
215
+ building_id
216
+ timezone
217
+ installation_date
218
+ customID
219
+ capacity {
220
+ max
221
+ mid
222
+ }
223
+ area {
224
+ value
225
+ unit
226
+ }
227
+ rooms {
228
+ id
229
+ name
230
+ floor_id
231
+ customID
232
+ capacity {
233
+ max
234
+ mid
235
+ }
236
+ coordinates
237
+ }
238
+ zones {
239
+ id
240
+ name
241
+ floor_id
242
+ room_id
243
+ customID
244
+ coordinates
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ `;
252
+ //# sourceMappingURL=topology.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topology.js","sourceRoot":"","sources":["../../../src/clients/queries/topology.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAErC;;;GAGG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;;;;;;;;;;;;CAYhC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BpC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiEnC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;CAwBjC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEzC,CAAC"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Structured API error with status code for proper error translation
3
+ */
4
+ export declare class ApiError extends Error {
5
+ statusCode: number;
6
+ constructor(statusCode: number, message: string);
7
+ }
8
+ /**
9
+ * v3 Reporting API Request Structure
10
+ * Based on butlr-api-container/pkg/reporting/models/request.go
11
+ */
12
+ export interface ReportingRequest {
13
+ group_by?: {
14
+ order?: string[];
15
+ raw?: boolean;
16
+ };
17
+ window?: {
18
+ every: string;
19
+ function: string;
20
+ offset?: string;
21
+ timezone?: string;
22
+ create_empty?: boolean;
23
+ fill?: {
24
+ use_previous?: boolean;
25
+ value?: number;
26
+ };
27
+ };
28
+ filter: {
29
+ measurements: string[];
30
+ start: string;
31
+ stop?: string;
32
+ spaces?: {
33
+ eq: string[];
34
+ };
35
+ rooms?: {
36
+ eq: string[];
37
+ };
38
+ zones?: {
39
+ eq: string[];
40
+ };
41
+ tags?: {
42
+ eq: string[];
43
+ };
44
+ clients?: {
45
+ eq: string[];
46
+ };
47
+ buildings?: {
48
+ eq: string[];
49
+ };
50
+ value?: {
51
+ gte?: number;
52
+ lte?: number;
53
+ gt?: number;
54
+ lt?: number;
55
+ };
56
+ calibrated?: string;
57
+ time_constraints?: {
58
+ time_ranges?: Array<{
59
+ start: string;
60
+ stop: string;
61
+ }>;
62
+ exclude_days_of_week?: string[];
63
+ };
64
+ };
65
+ options?: {
66
+ format?: "json" | "csv";
67
+ precision?: "s" | "ms" | "us" | "ns";
68
+ timestamp?: "RFC3339";
69
+ includeCalibrationPoints?: boolean;
70
+ };
71
+ paginate?: {
72
+ page: number;
73
+ limit: number;
74
+ };
75
+ calibrationPoints?: Array<{
76
+ timestamp: string;
77
+ occupancy: number;
78
+ type: "user_provided" | "pir_zero";
79
+ }>;
80
+ }
81
+ /**
82
+ * v3 Reporting API Response Structure
83
+ */
84
+ export interface ReportingResponse {
85
+ data: Array<{
86
+ field: string;
87
+ measurement: string;
88
+ time: string;
89
+ value: number;
90
+ timezone_offset?: string;
91
+ building_id?: string;
92
+ building_name?: string;
93
+ space_id?: string;
94
+ space_name?: string;
95
+ room_id?: string;
96
+ room_name?: string;
97
+ zone_id?: string;
98
+ hive_id?: string;
99
+ sensor_id?: string;
100
+ mac_address?: string;
101
+ [key: string]: unknown;
102
+ }>;
103
+ page_info?: {
104
+ page: number;
105
+ page_item_count: number;
106
+ total_item_count: number;
107
+ total_pages: number;
108
+ };
109
+ calibrationPoints?: unknown[];
110
+ }
111
+ /**
112
+ * Normalized data point (RFC3339 → ISO-8601)
113
+ */
114
+ export interface NormalizedDataPoint {
115
+ start: string;
116
+ stop?: string;
117
+ measurement: string;
118
+ value: number;
119
+ asset_id?: string;
120
+ asset_name?: string;
121
+ [key: string]: unknown;
122
+ }
123
+ /**
124
+ * Helper to get filter field name for asset type
125
+ */
126
+ export declare function getFilterField(assetType: string): string;
127
+ /**
128
+ * Helper to get measurement name for asset type
129
+ */
130
+ export declare function getMeasurement(assetType: string): string;
131
+ /**
132
+ * Normalize RFC3339 timestamp to ISO-8601
133
+ */
134
+ export declare function normalizeTimestamp(rfc3339: string): string;
135
+ /**
136
+ * Query v3 Reporting API
137
+ */
138
+ export declare function queryReporting(requestBody: ReportingRequest): Promise<ReportingResponse>;
139
+ /**
140
+ * Request builder for common occupancy queries
141
+ */
142
+ export declare class ReportingRequestBuilder {
143
+ private request;
144
+ constructor();
145
+ /**
146
+ * Set asset IDs to query
147
+ */
148
+ assets(assetType: string, ids: string[]): this;
149
+ /**
150
+ * Set measurements (auto-mapped from asset type if not provided)
151
+ */
152
+ measurements(measurements: string[]): this;
153
+ /**
154
+ * Auto-set measurement based on asset type
155
+ */
156
+ measurementForAssetType(assetType: string): this;
157
+ /**
158
+ * Set time range (ISO-8601 or relative like '-24h')
159
+ */
160
+ timeRange(start: string, stop?: string): this;
161
+ /**
162
+ * Set window aggregation
163
+ */
164
+ window(every: string, func: "mean" | "max" | "min" | "sum" | "first" | "last" | "median", timezone?: string): this;
165
+ /**
166
+ * Set grouping
167
+ */
168
+ groupBy(order: string[], raw?: boolean): this;
169
+ /**
170
+ * Set pagination
171
+ */
172
+ paginate(page: number, limit: number): this;
173
+ /**
174
+ * Set tags filter
175
+ */
176
+ tags(tags: string[]): this;
177
+ /**
178
+ * Build and return the request
179
+ */
180
+ build(): ReportingRequest;
181
+ /**
182
+ * Build and execute the query
183
+ */
184
+ execute(): Promise<ReportingResponse>;
185
+ }
186
+ /**
187
+ * Convenience function: Get current occupancy for assets
188
+ * Implements fallback strategy: presence first, then traffic (matches dashboard behavior)
189
+ */
190
+ export declare function getCurrentOccupancy(assetType: string, assetIds: string[]): Promise<NormalizedDataPoint[]>;
191
+ //# sourceMappingURL=reporting-client.d.ts.map