@dollhousemcp/mcp-server 2.0.0-rc.6 → 2.0.1

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 (229) hide show
  1. package/CHANGELOG.md +52 -61
  2. package/README.github.md +2 -2
  3. package/README.md +284 -224
  4. package/README.md.backup +284 -224
  5. package/README.npm.md +284 -224
  6. package/dist/cache/LRUCache.d.ts +3 -0
  7. package/dist/cache/LRUCache.d.ts.map +1 -1
  8. package/dist/cache/LRUCache.js +36 -26
  9. package/dist/config/env.d.ts +14 -4
  10. package/dist/config/env.d.ts.map +1 -1
  11. package/dist/config/env.js +20 -6
  12. package/dist/di/Container.d.ts +21 -0
  13. package/dist/di/Container.d.ts.map +1 -1
  14. package/dist/di/Container.js +250 -53
  15. package/dist/elements/BaseElement.d.ts.map +1 -1
  16. package/dist/elements/BaseElement.js +5 -10
  17. package/dist/elements/base/BaseElementManager.d.ts +22 -0
  18. package/dist/elements/base/BaseElementManager.d.ts.map +1 -1
  19. package/dist/elements/base/BaseElementManager.js +47 -7
  20. package/dist/elements/memories/Memory.d.ts +1 -0
  21. package/dist/elements/memories/Memory.d.ts.map +1 -1
  22. package/dist/elements/memories/Memory.js +12 -8
  23. package/dist/elements/memories/MemoryManager.d.ts.map +1 -1
  24. package/dist/elements/memories/MemoryManager.js +23 -42
  25. package/dist/elements/memories/MemorySearchIndex.js +2 -2
  26. package/dist/generated/version.d.ts +2 -2
  27. package/dist/generated/version.d.ts.map +1 -1
  28. package/dist/generated/version.js +3 -3
  29. package/dist/handlers/EnhancedIndexHandler.js +6 -6
  30. package/dist/handlers/element-crud/listElements.d.ts +2 -0
  31. package/dist/handlers/element-crud/listElements.d.ts.map +1 -1
  32. package/dist/handlers/element-crud/listElements.js +3 -1
  33. package/dist/handlers/mcp-aql/Gatekeeper.d.ts.map +1 -1
  34. package/dist/handlers/mcp-aql/Gatekeeper.js +23 -17
  35. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts +14 -0
  36. package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
  37. package/dist/handlers/mcp-aql/MCPAQLHandler.js +110 -14
  38. package/dist/handlers/mcp-aql/OperationRouter.d.ts.map +1 -1
  39. package/dist/handlers/mcp-aql/OperationRouter.js +13 -1
  40. package/dist/handlers/mcp-aql/OperationSchema.d.ts +7 -0
  41. package/dist/handlers/mcp-aql/OperationSchema.d.ts.map +1 -1
  42. package/dist/handlers/mcp-aql/OperationSchema.js +52 -1
  43. package/dist/handlers/mcp-aql/evaluatePermission.d.ts +53 -0
  44. package/dist/handlers/mcp-aql/evaluatePermission.d.ts.map +1 -0
  45. package/dist/handlers/mcp-aql/evaluatePermission.js +132 -0
  46. package/dist/handlers/mcp-aql/policies/ToolClassification.d.ts.map +1 -1
  47. package/dist/handlers/mcp-aql/policies/ToolClassification.js +2 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +3 -3
  50. package/dist/logging/LogHooks.js +11 -11
  51. package/dist/logging/LogManager.d.ts +0 -2
  52. package/dist/logging/LogManager.d.ts.map +1 -1
  53. package/dist/logging/LogManager.js +1 -3
  54. package/dist/logging/sinks/MemoryLogSink.d.ts +2 -0
  55. package/dist/logging/sinks/MemoryLogSink.d.ts.map +1 -1
  56. package/dist/logging/sinks/MemoryLogSink.js +12 -3
  57. package/dist/logging/types.d.ts +0 -2
  58. package/dist/logging/types.d.ts.map +1 -1
  59. package/dist/logging/types.js +1 -1
  60. package/dist/metrics/GatekeeperMetricsTracker.d.ts +32 -0
  61. package/dist/metrics/GatekeeperMetricsTracker.d.ts.map +1 -0
  62. package/dist/metrics/GatekeeperMetricsTracker.js +42 -0
  63. package/dist/metrics/MetricsManager.d.ts +47 -0
  64. package/dist/metrics/MetricsManager.d.ts.map +1 -0
  65. package/dist/metrics/MetricsManager.js +232 -0
  66. package/dist/metrics/OperationMetricsTracker.d.ts +32 -0
  67. package/dist/metrics/OperationMetricsTracker.d.ts.map +1 -0
  68. package/dist/metrics/OperationMetricsTracker.js +53 -0
  69. package/dist/metrics/collectors/DefaultElementProviderCollector.d.ts +27 -0
  70. package/dist/metrics/collectors/DefaultElementProviderCollector.d.ts.map +1 -0
  71. package/dist/metrics/collectors/DefaultElementProviderCollector.js +69 -0
  72. package/dist/metrics/collectors/FileLockManagerCollector.d.ts +16 -0
  73. package/dist/metrics/collectors/FileLockManagerCollector.d.ts.map +1 -0
  74. package/dist/metrics/collectors/FileLockManagerCollector.js +51 -0
  75. package/dist/metrics/collectors/GatekeeperMetricsCollector.d.ts +16 -0
  76. package/dist/metrics/collectors/GatekeeperMetricsCollector.d.ts.map +1 -0
  77. package/dist/metrics/collectors/GatekeeperMetricsCollector.js +76 -0
  78. package/dist/metrics/collectors/LRUCacheCollector.d.ts +18 -0
  79. package/dist/metrics/collectors/LRUCacheCollector.d.ts.map +1 -0
  80. package/dist/metrics/collectors/LRUCacheCollector.js +95 -0
  81. package/dist/metrics/collectors/OperationMetricsCollector.d.ts +16 -0
  82. package/dist/metrics/collectors/OperationMetricsCollector.d.ts.map +1 -0
  83. package/dist/metrics/collectors/OperationMetricsCollector.js +80 -0
  84. package/dist/metrics/collectors/OperationalTelemetryCollector.d.ts +17 -0
  85. package/dist/metrics/collectors/OperationalTelemetryCollector.d.ts.map +1 -0
  86. package/dist/metrics/collectors/OperationalTelemetryCollector.js +26 -0
  87. package/dist/metrics/collectors/PerformanceMonitorCollector.d.ts +14 -0
  88. package/dist/metrics/collectors/PerformanceMonitorCollector.d.ts.map +1 -0
  89. package/dist/metrics/collectors/PerformanceMonitorCollector.js +141 -0
  90. package/dist/metrics/collectors/SecurityMonitorCollector.d.ts +21 -0
  91. package/dist/metrics/collectors/SecurityMonitorCollector.d.ts.map +1 -0
  92. package/dist/metrics/collectors/SecurityMonitorCollector.js +56 -0
  93. package/dist/metrics/collectors/SecurityTelemetryCollector.d.ts +15 -0
  94. package/dist/metrics/collectors/SecurityTelemetryCollector.d.ts.map +1 -0
  95. package/dist/metrics/collectors/SecurityTelemetryCollector.js +112 -0
  96. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.d.ts +16 -0
  97. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.d.ts.map +1 -0
  98. package/dist/metrics/collectors/TriggerMetricsTrackerCollector.js +26 -0
  99. package/dist/metrics/collectors/index.d.ts +11 -0
  100. package/dist/metrics/collectors/index.d.ts.map +1 -0
  101. package/dist/metrics/collectors/index.js +11 -0
  102. package/dist/metrics/sinks/MemoryMetricsSink.d.ts +22 -0
  103. package/dist/metrics/sinks/MemoryMetricsSink.d.ts.map +1 -0
  104. package/dist/metrics/sinks/MemoryMetricsSink.js +121 -0
  105. package/dist/metrics/types.d.ts +98 -0
  106. package/dist/metrics/types.d.ts.map +1 -0
  107. package/dist/metrics/types.js +24 -0
  108. package/dist/portfolio/DefaultElementProvider.d.ts.map +1 -1
  109. package/dist/portfolio/DefaultElementProvider.js +1 -7
  110. package/dist/portfolio/EnhancedIndexManager.d.ts.map +1 -1
  111. package/dist/portfolio/EnhancedIndexManager.js +18 -18
  112. package/dist/portfolio/NLPScoringManager.d.ts.map +1 -1
  113. package/dist/portfolio/NLPScoringManager.js +5 -9
  114. package/dist/portfolio/PortfolioIndexManager.js +2 -2
  115. package/dist/portfolio/RelationshipManager.js +2 -2
  116. package/dist/portfolio/VerbTriggerManager.d.ts.map +1 -1
  117. package/dist/portfolio/VerbTriggerManager.js +5 -19
  118. package/dist/portfolio/config/IndexConfig.d.ts.map +1 -1
  119. package/dist/portfolio/config/IndexConfig.js +1 -12
  120. package/dist/portfolio/enhanced-index/ElementDefinitionBuilder.d.ts.map +1 -1
  121. package/dist/portfolio/enhanced-index/ElementDefinitionBuilder.js +3 -15
  122. package/dist/portfolio/enhanced-index/SemanticRelationshipService.d.ts.map +1 -1
  123. package/dist/portfolio/enhanced-index/SemanticRelationshipService.js +2 -16
  124. package/dist/portfolio/types/RelationshipTypes.d.ts.map +1 -1
  125. package/dist/portfolio/types/RelationshipTypes.js +3 -17
  126. package/dist/security/audit/config/suppressions.d.ts.map +1 -1
  127. package/dist/security/audit/config/suppressions.js +36 -8
  128. package/dist/security/constants.d.ts.map +1 -1
  129. package/dist/security/constants.js +10 -6
  130. package/dist/security/fileLockManager.d.ts.map +1 -1
  131. package/dist/security/fileLockManager.js +8 -6
  132. package/dist/security/secureYamlParser.d.ts.map +1 -1
  133. package/dist/security/secureYamlParser.js +1 -13
  134. package/dist/security/securityMonitor.d.ts +2 -1
  135. package/dist/security/securityMonitor.d.ts.map +1 -1
  136. package/dist/security/securityMonitor.js +14 -3
  137. package/dist/security/telemetry/SecurityTelemetry.d.ts +16 -0
  138. package/dist/security/telemetry/SecurityTelemetry.d.ts.map +1 -1
  139. package/dist/security/telemetry/SecurityTelemetry.js +30 -2
  140. package/dist/security/tokenManager.d.ts +3 -0
  141. package/dist/security/tokenManager.d.ts.map +1 -1
  142. package/dist/security/tokenManager.js +13 -5
  143. package/dist/security/validation/BackgroundValidator.d.ts.map +1 -1
  144. package/dist/security/validation/BackgroundValidator.js +7 -7
  145. package/dist/server/startup.d.ts.map +1 -1
  146. package/dist/server/startup.js +8 -24
  147. package/dist/server/tools/MCPAQLTools.js +4 -1
  148. package/dist/services/ActivationStore.d.ts.map +1 -1
  149. package/dist/services/ActivationStore.js +9 -3
  150. package/dist/services/FileWatchService.d.ts +1 -0
  151. package/dist/services/FileWatchService.d.ts.map +1 -1
  152. package/dist/services/FileWatchService.js +83 -48
  153. package/dist/services/MetadataService.d.ts.map +1 -1
  154. package/dist/services/MetadataService.js +7 -2
  155. package/dist/services/query/ElementQueryService.d.ts.map +1 -1
  156. package/dist/services/query/ElementQueryService.js +1 -41
  157. package/dist/services/query/PaginationService.d.ts.map +1 -1
  158. package/dist/services/query/PaginationService.js +1 -14
  159. package/dist/services/query/SortService.d.ts.map +1 -1
  160. package/dist/services/query/SortService.js +1 -6
  161. package/dist/services/validation/ValidationService.d.ts.map +1 -1
  162. package/dist/services/validation/ValidationService.js +3 -8
  163. package/dist/storage/ElementStorageLayer.d.ts.map +1 -1
  164. package/dist/storage/ElementStorageLayer.js +5 -2
  165. package/dist/storage/MemoryStorageLayer.d.ts.map +1 -1
  166. package/dist/storage/MemoryStorageLayer.js +5 -2
  167. package/dist/telemetry/OperationalTelemetry.js +2 -2
  168. package/dist/utils/EventDeduplicator.d.ts +44 -0
  169. package/dist/utils/EventDeduplicator.d.ts.map +1 -0
  170. package/dist/utils/EventDeduplicator.js +93 -0
  171. package/dist/utils/FileLock.d.ts.map +1 -1
  172. package/dist/utils/FileLock.js +1 -9
  173. package/dist/utils/PerformanceMonitor.d.ts.map +1 -1
  174. package/dist/utils/PerformanceMonitor.js +5 -5
  175. package/dist/utils/SlidingWindowRateLimiter.d.ts +13 -0
  176. package/dist/utils/SlidingWindowRateLimiter.d.ts.map +1 -0
  177. package/dist/utils/SlidingWindowRateLimiter.js +23 -0
  178. package/dist/web/console/IngestRoutes.d.ts +84 -0
  179. package/dist/web/console/IngestRoutes.d.ts.map +1 -0
  180. package/dist/web/console/IngestRoutes.js +252 -0
  181. package/dist/web/console/LeaderElection.d.ts +89 -0
  182. package/dist/web/console/LeaderElection.d.ts.map +1 -0
  183. package/dist/web/console/LeaderElection.js +205 -0
  184. package/dist/web/console/LeaderForwardingSink.d.ts +61 -0
  185. package/dist/web/console/LeaderForwardingSink.d.ts.map +1 -0
  186. package/dist/web/console/LeaderForwardingSink.js +197 -0
  187. package/dist/web/console/SessionNames.d.ts +46 -0
  188. package/dist/web/console/SessionNames.d.ts.map +1 -0
  189. package/dist/web/console/SessionNames.js +257 -0
  190. package/dist/web/console/UnifiedConsole.d.ts +64 -0
  191. package/dist/web/console/UnifiedConsole.d.ts.map +1 -0
  192. package/dist/web/console/UnifiedConsole.js +119 -0
  193. package/dist/web/contentPipeline.d.ts +58 -0
  194. package/dist/web/contentPipeline.d.ts.map +1 -0
  195. package/dist/web/contentPipeline.js +112 -0
  196. package/dist/web/portDiscovery.d.ts +58 -0
  197. package/dist/web/portDiscovery.d.ts.map +1 -0
  198. package/dist/web/portDiscovery.js +143 -0
  199. package/dist/web/public/app.js +148 -60
  200. package/dist/web/public/logs.js +638 -0
  201. package/dist/web/public/metrics.js +682 -0
  202. package/dist/web/public/permissions.js +394 -0
  203. package/dist/web/public/sessions.js +369 -0
  204. package/dist/web/routes/healthRoutes.d.ts +16 -0
  205. package/dist/web/routes/healthRoutes.d.ts.map +1 -0
  206. package/dist/web/routes/healthRoutes.js +29 -0
  207. package/dist/web/routes/logRoutes.d.ts +18 -0
  208. package/dist/web/routes/logRoutes.d.ts.map +1 -0
  209. package/dist/web/routes/logRoutes.js +126 -0
  210. package/dist/web/routes/metricsRoutes.d.ts +17 -0
  211. package/dist/web/routes/metricsRoutes.d.ts.map +1 -0
  212. package/dist/web/routes/metricsRoutes.js +90 -0
  213. package/dist/web/routes/permissionRoutes.d.ts +16 -0
  214. package/dist/web/routes/permissionRoutes.d.ts.map +1 -0
  215. package/dist/web/routes/permissionRoutes.js +133 -0
  216. package/dist/web/routes.d.ts.map +1 -1
  217. package/dist/web/routes.js +309 -339
  218. package/dist/web/server.d.ts +21 -1
  219. package/dist/web/server.d.ts.map +1 -1
  220. package/dist/web/server.js +42 -4
  221. package/dist/web/sinks/WebSSELogSink.d.ts +15 -0
  222. package/dist/web/sinks/WebSSELogSink.d.ts.map +1 -0
  223. package/dist/web/sinks/WebSSELogSink.js +22 -0
  224. package/dist/web/sinks/WebSSEMetricsSink.d.ts +16 -0
  225. package/dist/web/sinks/WebSSEMetricsSink.d.ts.map +1 -0
  226. package/dist/web/sinks/WebSSEMetricsSink.js +23 -0
  227. package/package.json +2 -2
  228. package/server.json +2 -2
  229. package/dist/web/public/public/app.js +0 -1878
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Forwarding sinks for follower MCP servers.
3
+ *
4
+ * When a server becomes a follower in the unified console election, it
5
+ * registers these sinks with its LogManager and MetricsManager. Instead
6
+ * of broadcasting to local SSE clients, entries are batch-POSTed to
7
+ * the leader's ingestion endpoints.
8
+ *
9
+ * Features:
10
+ * - Batch buffering (50 entries or 1s flush, whichever comes first)
11
+ * - In-memory buffer up to 10,000 entries on leader failure
12
+ * - Exponential backoff on POST failure (1s → 2s → 4s, max 30s)
13
+ * - Automatic drain on leader recovery
14
+ *
15
+ * @since v2.1.0 — Issue #1700
16
+ */
17
+ import type { ILogSink, UnifiedLogEntry } from '../../logging/types.js';
18
+ import type { MetricSnapshot } from '../../metrics/types.js';
19
+ /**
20
+ * ILogSink that batch-POSTs entries to the leader's /api/ingest/logs.
21
+ */
22
+ export declare class LeaderForwardingLogSink implements ILogSink {
23
+ private readonly leaderUrl;
24
+ private readonly sessionId;
25
+ private readonly buffer;
26
+ private flushTimer;
27
+ private backoffMs;
28
+ private flushing;
29
+ constructor(leaderUrl: string, sessionId: string);
30
+ write(entry: UnifiedLogEntry): void;
31
+ flush(): Promise<void>;
32
+ close(): Promise<void>;
33
+ private flushBuffer;
34
+ private requeueBatch;
35
+ private scheduleRetry;
36
+ }
37
+ /**
38
+ * Forwards metric snapshots to the leader's /api/ingest/metrics.
39
+ */
40
+ export declare class LeaderForwardingMetricsSink {
41
+ private readonly leaderUrl;
42
+ private readonly sessionId;
43
+ constructor(leaderUrl: string, sessionId: string);
44
+ onSnapshot(snapshot: MetricSnapshot): Promise<void>;
45
+ }
46
+ /**
47
+ * Sends session lifecycle events to the leader.
48
+ */
49
+ export declare class SessionHeartbeat {
50
+ private readonly leaderUrl;
51
+ private readonly sessionId;
52
+ private readonly pid;
53
+ private heartbeatTimer;
54
+ constructor(leaderUrl: string, sessionId: string, pid: number);
55
+ /** Notify the leader that this session has started */
56
+ start(): Promise<void>;
57
+ /** Notify the leader that this session is stopping */
58
+ stop(): Promise<void>;
59
+ private sendEvent;
60
+ }
61
+ //# sourceMappingURL=LeaderForwardingSink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LeaderForwardingSink.d.ts","sourceRoot":"","sources":["../../../src/web/console/LeaderForwardingSink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAsB7D;;GAEG;AACH,qBAAa,uBAAwB,YAAW,QAAQ;IAOpD,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;IAChD,OAAO,CAAC,UAAU,CAA+C;IACjE,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAS;gBAGN,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM;IAOpC,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAkB7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAQd,WAAW;IA+BzB,OAAO,CAAC,YAAY;IAUpB,OAAO,CAAC,aAAa;CAItB;AAED;;GAEG;AACH,qBAAa,2BAA2B;IAEpC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM;IAG9B,UAAU,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB1D;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAIzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,cAAc,CAA+C;gBAGlD,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM;IAG9B,sDAAsD;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,sDAAsD;IAChD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAQb,SAAS;CAqBxB"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Forwarding sinks for follower MCP servers.
3
+ *
4
+ * When a server becomes a follower in the unified console election, it
5
+ * registers these sinks with its LogManager and MetricsManager. Instead
6
+ * of broadcasting to local SSE clients, entries are batch-POSTed to
7
+ * the leader's ingestion endpoints.
8
+ *
9
+ * Features:
10
+ * - Batch buffering (50 entries or 1s flush, whichever comes first)
11
+ * - In-memory buffer up to 10,000 entries on leader failure
12
+ * - Exponential backoff on POST failure (1s → 2s → 4s, max 30s)
13
+ * - Automatic drain on leader recovery
14
+ *
15
+ * @since v2.1.0 — Issue #1700
16
+ */
17
+ import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
18
+ import { logger } from '../../utils/logger.js';
19
+ /** Maximum entries to buffer when leader is unreachable */
20
+ const MAX_BUFFER_SIZE = 10_000;
21
+ /** Batch size before flushing */
22
+ const BATCH_SIZE = 50;
23
+ /** Time-based flush interval (ms) */
24
+ const FLUSH_INTERVAL_MS = 1_000;
25
+ /** Initial backoff delay on failure (ms) */
26
+ const INITIAL_BACKOFF_MS = 1_000;
27
+ /** Maximum backoff delay (ms) */
28
+ const MAX_BACKOFF_MS = 30_000;
29
+ /** HTTP request timeout (ms) */
30
+ const REQUEST_TIMEOUT_MS = 5_000;
31
+ /**
32
+ * ILogSink that batch-POSTs entries to the leader's /api/ingest/logs.
33
+ */
34
+ export class LeaderForwardingLogSink {
35
+ leaderUrl;
36
+ sessionId;
37
+ buffer = [];
38
+ flushTimer = null;
39
+ backoffMs = INITIAL_BACKOFF_MS;
40
+ flushing = false;
41
+ constructor(leaderUrl, sessionId) {
42
+ this.leaderUrl = leaderUrl;
43
+ this.sessionId = sessionId;
44
+ this.sessionId = UnicodeValidator.normalize(sessionId).normalizedContent;
45
+ this.flushTimer = setInterval(() => this.flushBuffer(), FLUSH_INTERVAL_MS);
46
+ this.flushTimer.unref();
47
+ }
48
+ write(entry) {
49
+ // Stamp session ID before buffering
50
+ const stamped = {
51
+ ...entry,
52
+ data: { ...entry.data, _sessionId: this.sessionId },
53
+ };
54
+ if (this.buffer.length >= MAX_BUFFER_SIZE) {
55
+ // Evict oldest entry (FIFO)
56
+ this.buffer.shift();
57
+ }
58
+ this.buffer.push(stamped);
59
+ if (this.buffer.length >= BATCH_SIZE) {
60
+ this.flushBuffer();
61
+ }
62
+ }
63
+ async flush() {
64
+ await this.flushBuffer();
65
+ }
66
+ async close() {
67
+ if (this.flushTimer) {
68
+ clearInterval(this.flushTimer);
69
+ this.flushTimer = null;
70
+ }
71
+ await this.flushBuffer();
72
+ }
73
+ async flushBuffer() {
74
+ if (this.flushing || this.buffer.length === 0)
75
+ return;
76
+ this.flushing = true;
77
+ const batch = this.buffer.splice(0, BATCH_SIZE);
78
+ try {
79
+ const controller = new AbortController();
80
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
81
+ const response = await fetch(`${this.leaderUrl}/api/ingest/logs`, {
82
+ method: 'POST',
83
+ headers: { 'Content-Type': 'application/json' },
84
+ body: JSON.stringify({ sessionId: this.sessionId, entries: batch }),
85
+ signal: controller.signal,
86
+ });
87
+ clearTimeout(timeout);
88
+ if (response.ok) {
89
+ this.backoffMs = INITIAL_BACKOFF_MS;
90
+ }
91
+ else {
92
+ this.requeueBatch(batch);
93
+ this.scheduleRetry();
94
+ }
95
+ }
96
+ catch {
97
+ this.requeueBatch(batch);
98
+ this.scheduleRetry();
99
+ }
100
+ finally {
101
+ this.flushing = false;
102
+ }
103
+ }
104
+ requeueBatch(batch) {
105
+ const spaceAvailable = MAX_BUFFER_SIZE - this.buffer.length;
106
+ if (spaceAvailable > 0) {
107
+ const toRequeue = batch.slice(0, spaceAvailable);
108
+ this.buffer.unshift(...toRequeue);
109
+ }
110
+ else {
111
+ logger.warn(`[ForwardingSink] Buffer full (${MAX_BUFFER_SIZE}), dropping ${batch.length} entries`);
112
+ }
113
+ }
114
+ scheduleRetry() {
115
+ logger.debug(`[ForwardingSink] Leader unreachable, backoff ${this.backoffMs}ms (buffered: ${this.buffer.length})`);
116
+ this.backoffMs = Math.min(this.backoffMs * 2, MAX_BACKOFF_MS);
117
+ }
118
+ }
119
+ /**
120
+ * Forwards metric snapshots to the leader's /api/ingest/metrics.
121
+ */
122
+ export class LeaderForwardingMetricsSink {
123
+ leaderUrl;
124
+ sessionId;
125
+ constructor(leaderUrl, sessionId) {
126
+ this.leaderUrl = leaderUrl;
127
+ this.sessionId = sessionId;
128
+ }
129
+ async onSnapshot(snapshot) {
130
+ try {
131
+ const controller = new AbortController();
132
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
133
+ await fetch(`${this.leaderUrl}/api/ingest/metrics`, {
134
+ method: 'POST',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ body: JSON.stringify({ sessionId: this.sessionId, snapshot }),
137
+ signal: controller.signal,
138
+ });
139
+ clearTimeout(timeout);
140
+ }
141
+ catch {
142
+ logger.debug('[ForwardingSink] Failed to forward metrics snapshot');
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * Sends session lifecycle events to the leader.
148
+ */
149
+ export class SessionHeartbeat {
150
+ leaderUrl;
151
+ sessionId;
152
+ pid;
153
+ heartbeatTimer = null;
154
+ constructor(leaderUrl, sessionId, pid) {
155
+ this.leaderUrl = leaderUrl;
156
+ this.sessionId = sessionId;
157
+ this.pid = pid;
158
+ }
159
+ /** Notify the leader that this session has started */
160
+ async start() {
161
+ await this.sendEvent('started');
162
+ this.heartbeatTimer = setInterval(() => {
163
+ this.sendEvent('heartbeat').catch(() => { });
164
+ }, 10_000);
165
+ this.heartbeatTimer.unref();
166
+ }
167
+ /** Notify the leader that this session is stopping */
168
+ async stop() {
169
+ if (this.heartbeatTimer) {
170
+ clearInterval(this.heartbeatTimer);
171
+ this.heartbeatTimer = null;
172
+ }
173
+ await this.sendEvent('stopped');
174
+ }
175
+ async sendEvent(event) {
176
+ try {
177
+ const controller = new AbortController();
178
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
179
+ await fetch(`${this.leaderUrl}/api/ingest/session`, {
180
+ method: 'POST',
181
+ headers: { 'Content-Type': 'application/json' },
182
+ body: JSON.stringify({
183
+ sessionId: this.sessionId,
184
+ event,
185
+ pid: this.pid,
186
+ startedAt: new Date().toISOString(),
187
+ }),
188
+ signal: controller.signal,
189
+ });
190
+ clearTimeout(timeout);
191
+ }
192
+ catch {
193
+ logger.debug(`[SessionHeartbeat] Failed to send ${event} event`);
194
+ }
195
+ }
196
+ }
197
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTGVhZGVyRm9yd2FyZGluZ1NpbmsuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvd2ViL2NvbnNvbGUvTGVhZGVyRm9yd2FyZGluZ1NpbmsudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHO0FBSUgsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDakYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRS9DLDJEQUEyRDtBQUMzRCxNQUFNLGVBQWUsR0FBRyxNQUFNLENBQUM7QUFFL0IsaUNBQWlDO0FBQ2pDLE1BQU0sVUFBVSxHQUFHLEVBQUUsQ0FBQztBQUV0QixxQ0FBcUM7QUFDckMsTUFBTSxpQkFBaUIsR0FBRyxLQUFLLENBQUM7QUFFaEMsNENBQTRDO0FBQzVDLE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxDQUFDO0FBRWpDLGlDQUFpQztBQUNqQyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUM7QUFFOUIsZ0NBQWdDO0FBQ2hDLE1BQU0sa0JBQWtCLEdBQUcsS0FBSyxDQUFDO0FBRWpDOztHQUVHO0FBQ0gsTUFBTSxPQUFPLHVCQUF1QjtJQU9mO0lBQ0E7SUFQRixNQUFNLEdBQXNCLEVBQUUsQ0FBQztJQUN4QyxVQUFVLEdBQTBDLElBQUksQ0FBQztJQUN6RCxTQUFTLEdBQUcsa0JBQWtCLENBQUM7SUFDL0IsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUV6QixZQUNtQixTQUFpQixFQUNqQixTQUFpQjtRQURqQixjQUFTLEdBQVQsU0FBUyxDQUFRO1FBQ2pCLGNBQVMsR0FBVCxTQUFTLENBQVE7UUFFbEMsSUFBSSxDQUFDLFNBQVMsR0FBRyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsaUJBQWlCLENBQUM7UUFDekUsSUFBSSxDQUFDLFVBQVUsR0FBRyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFdB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O1lBQ3hCLGFBQWEsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7WUFDbkMsSUFBSSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUM7UUFDN0IsQ0FBQztRQUNELE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUNsQyxDQUFDO0lBRU8sS0FBSyxDQUFDLFNBQVMsQ0FBQyxLQUEwQztRQUNoRSxJQUFJLENBQUM7WUFDSCxNQUFNLFVBQVUsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO1lBQ3pDLE1BQU0sT0FBTyxHQUFHLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLEVBQUUsa0JBQWtCLENBQUMsQ0FBQztZQUV6RSxNQUFNLEtBQUssQ0FBQyxHQUFHLElBQUksQ0FBQyxTQUFTLHFCQUFxQixFQUFFO2dCQUNsRCxNQUFNLEVBQUUsTUFBTTtnQkFDZCxPQUFPLEVBQUUsRUFBRSxjQUFjLEVBQUUsa0JBQWtCLEVBQUU7Z0JBQy9DLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO29CQUNuQixTQUFTLEVBQUUsSUFBSSxDQUFDLFNBQVM7b0JBQ3pCLEtBQUs7b0JBQ0wsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO29CQUNiLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRSxDQUFDLFdBQVcsRUFBRTtpQkFDcEMsQ0FBQztnQkFDRixNQUFNLEVBQUUsVUFBVSxDQUFDLE1BQU07YUFDMUIsQ0FBQyxDQUFDO1lBQ0gsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3hCLENBQUM7UUFBQyxNQUFNLENBQUM7WUFDUCxNQUFNLENBQUMsS0FBSyxDQUFDLHFDQUFxQyxLQUFLLFFBQVEsQ0FBQyxDQUFDO1FBQ25FLENBQUM7SUFDSCxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEZvcndhcmRpbmcgc2lua3MgZm9yIGZvbGxvd2VyIE1DUCBzZXJ2ZXJzLlxuICpcbiAqIFdoZW4gYSBzZXJ2ZXIgYmVjb21lcyBhIGZvbGxvd2VyIGluIHRoZSB1bmlmaWVkIGNvbnNvbGUgZWxlY3Rpb24sIGl0XG4gKiByZWdpc3RlcnMgdGhlc2Ugc2lua3Mgd2l0aCBpdHMgTG9nTWFuYWdlciBhbmQgTWV0cmljc01hbmFnZXIuIEluc3RlYWRcbiAqIG9mIGJyb2FkY2FzdGluZyB0byBsb2NhbCBTU0UgY2xpZW50cywgZW50cmllcyBhcmUgYmF0Y2gtUE9TVGVkIHRvXG4gKiB0aGUgbGVhZGVyJ3MgaW5nZXN0aW9uIGVuZHBvaW50cy5cbiAqXG4gKiBGZWF0dXJlczpcbiAqIC0gQmF0Y2ggYnVmZmVyaW5nICg1MCBlbnRyaWVzIG9yIDFzIGZsdXNoLCB3aGljaGV2ZXIgY29tZXMgZmlyc3QpXG4gKiAtIEluLW1lbW9yeSBidWZmZXIgdXAgdG8gMTAsMDAwIGVudHJpZXMgb24gbGVhZGVyIGZhaWx1cmVcbiAqIC0gRXhwb25lbnRpYWwgYmFja29mZiBvbiBQT1NUIGZhaWx1cmUgKDFzIOKGkiAycyDihpIgNHMsIG1heCAzMHMpXG4gKiAtIEF1dG9tYXRpYyBkcmFpbiBvbiBsZWFkZXIgcmVjb3ZlcnlcbiAqXG4gKiBAc2luY2UgdjIuMS4wIOKAlCBJc3N1ZSAjMTcwMFxuICovXG5cbmltcG9ydCB0eXBlIHsgSUxvZ1NpbmssIFVuaWZpZWRMb2dFbnRyeSB9IGZyb20gJy4uLy4uL2xvZ2dpbmcvdHlwZXMuanMnO1xuaW1wb3J0IHR5cGUgeyBNZXRyaWNTbmFwc2hvdCB9IGZyb20gJy4uLy4uL21ldHJpY3MvdHlwZXMuanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tICcuLi8uLi91dGlscy9sb2dnZXIuanMnO1xuXG4vKiogTWF4aW11bSBlbnRyaWVzIHRvIGJ1ZmZlciB3aGVuIGxlYWRlciBpcyB1bnJlYWNoYWJsZSAqL1xuY29uc3QgTUFYX0JVRkZFUl9TSVpFID0gMTBfMDAwO1xuXG4vKiogQmF0Y2ggc2l6ZSBiZWZvcmUgZmx1c2hpbmcgKi9cbmNvbnN0IEJBVENIX1NJWkUgPSA1MDtcblxuLyoqIFRpbWUtYmFzZWQgZmx1c2ggaW50ZXJ2YWwgKG1zKSAqL1xuY29uc3QgRkxVU0hfSU5URVJWQUxfTVMgPSAxXzAwMDtcblxuLyoqIEluaXRpYWwgYmFja29mZiBkZWxheSBvbiBmYWlsdXJlIChtcykgKi9cbmNvbnN0IElOSVRJQUxfQkFDS09GRl9NUyA9IDFfMDAwO1xuXG4vKiogTWF4aW11bSBiYWNrb2ZmIGRlbGF5IChtcykgKi9cbmNvbnN0IE1BWF9CQUNLT0ZGX01TID0gMzBfMDAwO1xuXG4vKiogSFRUUCByZXF1ZXN0IHRpbWVvdXQgKG1zKSAqL1xuY29uc3QgUkVRVUVTVF9USU1FT1VUX01TID0gNV8wMDA7XG5cbi8qKlxuICogSUxvZ1NpbmsgdGhhdCBiYXRjaC1QT1NUcyBlbnRyaWVzIHRvIHRoZSBsZWFkZXIncyAvYXBpL2luZ2VzdC9sb2dzLlxuICovXG5leHBvcnQgY2xhc3MgTGVhZGVyRm9yd2FyZGluZ0xvZ1NpbmsgaW1wbGVtZW50cyBJTG9nU2luayB7XG4gIHByaXZhdGUgcmVhZG9ubHkgYnVmZmVyOiBVbmlmaWVkTG9nRW50cnlbXSA9IFtdO1xuICBwcml2YXRlIGZsdXNoVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPiB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGJhY2tvZmZNcyA9IElOSVRJQUxfQkFDS09GRl9NUztcbiAgcHJpdmF0ZSBmbHVzaGluZyA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbGVhZGVyVXJsOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXNzaW9uSWQ6IHN0cmluZyxcbiAgKSB7XG4gICAgdGhpcy5zZXNzaW9uSWQgPSBVbmljb2RlVmFsaWRhdG9yLm5vcm1hbGl6ZShzZXNzaW9uSWQpLm5vcm1hbGl6ZWRDb250ZW50O1xuICAgIHRoaXMuZmx1c2hUaW1lciA9IHNldEludGVydmFsKCgpID0+IHRoaXMuZmx1c2hCdWZmZXIoKSwgRkxVU0hfSU5URVJWQUxfTVMpO1xuICAgIHRoaXMuZmx1c2hUaW1lci51bnJlZigpO1xuICB9XG5cbiAgd3JpdGUoZW50cnk6IFVuaWZpZWRMb2dFbnRyeSk6IHZvaWQge1xuICAgIC8vIFN0YW1wIHNlc3Npb24gSUQgYmVmb3JlIGJ1ZmZlcmluZ1xuICAgIGNvbnN0IHN0YW1wZWQ6IFVuaWZpZWRMb2dFbnRyeSA9IHtcbiAgICAgIC4uLmVudHJ5LFxuICAgICAgZGF0YTogeyAuLi5lbnRyeS5kYXRhLCBfc2Vzc2lvbklkOiB0aGlzLnNlc3Npb25JZCB9LFxuICAgIH07XG5cbiAgICBpZiAodGhpcy5idWZmZXIubGVuZ3RoID49IE1BWF9CVUZGRVJfU0laRSkge1xuICAgICAgLy8gRXZpY3Qgb2xkZXN0IGVudHJ5IChGSUZPKVxuICAgICAgdGhpcy5idWZmZXIuc2hpZnQoKTtcbiAgICB9XG4gICAgdGhpcy5idWZmZXIucHVzaChzdGFtcGVkKTtcblxuICAgIGlmICh0aGlzLmJ1ZmZlci5sZW5ndGggPj0gQkFUQ0hfU0laRSkge1xuICAgICAgdGhpcy5mbHVzaEJ1ZmZlcigpO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGZsdXNoKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuZmx1c2hCdWZmZXIoKTtcbiAgfVxuXG4gIGFzeW5jIGNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmZsdXNoVGltZXIpIHtcbiAgICAgIGNsZWFySW50ZXJ2YWwodGhpcy5mbHVzaFRpbWVyKTtcbiAgICAgIHRoaXMuZmx1c2hUaW1lciA9IG51bGw7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMuZmx1c2hCdWZmZXIoKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgZmx1c2hCdWZmZXIoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMuZmx1c2hpbmcgfHwgdGhpcy5idWZmZXIubGVuZ3RoID09PSAwKSByZXR1cm47XG4gICAgdGhpcy5mbHVzaGluZyA9IHRydWU7XG5cbiAgICBjb25zdCBiYXRjaCA9IHRoaXMuYnVmZmVyLnNwbGljZSgwLCBCQVRDSF9TSVpFKTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IGNvbnRyb2xsZXIuYWJvcnQoKSwgUkVRVUVTVF9USU1FT1VUX01TKTtcblxuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHt0aGlzLmxlYWRlclVybH0vYXBpL2luZ2VzdC9sb2dzYCwge1xuICAgICAgICBtZXRob2Q6ICdQT1NUJyxcbiAgICAgICAgaGVhZGVyczogeyAnQ29udGVudC1UeXBlJzogJ2FwcGxpY2F0aW9uL2pzb24nIH0sXG4gICAgICAgIGJvZHk6IEpTT04uc3RyaW5naWZ5KHsgc2Vzc2lvbklkOiB0aGlzLnNlc3Npb25JZCwgZW50cmllczogYmF0Y2ggfSksXG4gICAgICAgIHNpZ25hbDogY29udHJvbGxlci5zaWduYWwsXG4gICAgICB9KTtcbiAgICAgIGNsZWFyVGltZW91dCh0aW1lb3V0KTtcblxuICAgICAgaWYgKHJlc3BvbnNlLm9rKSB7XG4gICAgICAgIHRoaXMuYmFja29mZk1zID0gSU5JVElBTF9CQUNLT0ZGX01TO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhpcy5yZXF1ZXVlQmF0Y2goYmF0Y2gpO1xuICAgICAgICB0aGlzLnNjaGVkdWxlUmV0cnkoKTtcbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIHRoaXMucmVxdWV1ZUJhdGNoKGJhdGNoKTtcbiAgICAgIHRoaXMuc2NoZWR1bGVSZXRyeSgpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICB0aGlzLmZsdXNoaW5nID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSByZXF1ZXVlQmF0Y2goYmF0Y2g6IFVuaWZpZWRMb2dFbnRyeVtdKTogdm9pZCB7XG4gICAgY29uc3Qgc3BhY2VBdmFpbGFibGUgPSBNQVhfQlVGRkVSX1NJWkUgLSB0aGlzLmJ1ZmZlci5sZW5ndGg7XG4gICAgaWYgKHNwYWNlQXZhaWxhYmxlID4gMCkge1xuICAgICAgY29uc3QgdG9SZXF1ZXVlID0gYmF0Y2guc2xpY2UoMCwgc3BhY2VBdmFpbGFibGUpO1xuICAgICAgdGhpcy5idWZmZXIudW5zaGlmdCguLi50b1JlcXVldWUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2dnZXIud2FybihgW0ZvcndhcmRpbmdTaW5rXSBCdWZmZXIgZnVsbCAoJHtNQVhfQlVGRkVSX1NJWkV9KSwgZHJvcHBpbmcgJHtiYXRjaC5sZW5ndGh9IGVudHJpZXNgKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIHNjaGVkdWxlUmV0cnkoKTogdm9pZCB7XG4gICAgbG9nZ2VyLmRlYnVnKGBbRm9yd2FyZGluZ1NpbmtdIExlYWRlciB1bnJlYWNoYWJsZSwgYmFja29mZiAke3RoaXMuYmFja29mZk1zfW1zIChidWZmZXJlZDogJHt0aGlzLmJ1ZmZlci5sZW5ndGh9KWApO1xuICAgIHRoaXMuYmFja29mZk1zID0gTWF0aC5taW4odGhpcy5iYWNrb2ZmTXMgKiAyLCBNQVhfQkFDS09GRl9NUyk7XG4gIH1cbn1cblxuLyoqXG4gKiBGb3J3YXJkcyBtZXRyaWMgc25hcHNob3RzIHRvIHRoZSBsZWFkZXIncyAvYXBpL2luZ2VzdC9tZXRyaWNzLlxuICovXG5leHBvcnQgY2xhc3MgTGVhZGVyRm9yd2FyZGluZ01ldHJpY3NTaW5rIHtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSByZWFkb25seSBsZWFkZXJVcmw6IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlc3Npb25JZDogc3RyaW5nLFxuICApIHt9XG5cbiAgYXN5bmMgb25TbmFwc2hvdChzbmFwc2hvdDogTWV0cmljU25hcHNob3QpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IGNvbnRyb2xsZXIuYWJvcnQoKSwgUkVRVUVTVF9USU1FT1VUX01TKTtcblxuICAgICAgYXdhaXQgZmV0Y2goYCR7dGhpcy5sZWFkZXJVcmx9L2FwaS9pbmdlc3QvbWV0cmljc2AsIHtcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7IHNlc3Npb25JZDogdGhpcy5zZXNzaW9uSWQsIHNuYXBzaG90IH0pLFxuICAgICAgICBzaWduYWw6IGNvbnRyb2xsZXIuc2lnbmFsLFxuICAgICAgfSk7XG4gICAgICBjbGVhclRpbWVvdXQodGltZW91dCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICBsb2dnZXIuZGVidWcoJ1tGb3J3YXJkaW5nU2lua10gRmFpbGVkIHRvIGZvcndhcmQgbWV0cmljcyBzbmFwc2hvdCcpO1xuICAgIH1cbiAgfVxufVxuXG4vKipcbiAqIFNlbmRzIHNlc3Npb24gbGlmZWN5Y2xlIGV2ZW50cyB0byB0aGUgbGVhZGVyLlxuICovXG5leHBvcnQgY2xhc3MgU2Vzc2lvbkhlYXJ0YmVhdCB7XG4gIHByaXZhdGUgaGVhcnRiZWF0VGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldEludGVydmFsPiB8IG51bGwgPSBudWxsO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgbGVhZGVyVXJsOiBzdHJpbmcsXG4gICAgcHJpdmF0ZSByZWFkb25seSBzZXNzaW9uSWQ6IHN0cmluZyxcbiAgICBwcml2YXRlIHJlYWRvbmx5IHBpZDogbnVtYmVyLFxuICApIHt9XG5cbiAgLyoqIE5vdGlmeSB0aGUgbGVhZGVyIHRoYXQgdGhpcyBzZXNzaW9uIGhhcyBzdGFydGVkICovXG4gIGFzeW5jIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IHRoaXMuc2VuZEV2ZW50KCdzdGFydGVkJyk7XG5cbiAgICB0aGlzLmhlYXJ0YmVhdFRpbWVyID0gc2V0SW50ZXJ2YWwoKCkgPT4ge1xuICAgICAgdGhpcy5zZW5kRXZlbnQoJ2hlYXJ0YmVhdCcpLmNhdGNoKCgpID0+IHt9KTtcbiAgICB9LCAxMF8wMDApO1xuICAgIHRoaXMuaGVhcnRiZWF0VGltZXIudW5yZWYoKTtcbiAgfVxuXG4gIC8qKiBOb3RpZnkgdGhlIGxlYWRlciB0aGF0IHRoaXMgc2Vzc2lvbiBpcyBzdG9wcGluZyAqL1xuICBhc3luYyBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmhlYXJ0YmVhdFRpbWVyKSB7XG4gICAgICBjbGVhckludGVydmFsKHRoaXMuaGVhcnRiZWF0VGltZXIpO1xuICAgICAgdGhpcy5oZWFydGJlYXRUaW1lciA9IG51bGw7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMuc2VuZEV2ZW50KCdzdG9wcGVkJyk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHNlbmRFdmVudChldmVudDogJ3N0YXJ0ZWQnIHwgJ3N0b3BwZWQnIHwgJ2hlYXJ0YmVhdCcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY29udHJvbGxlciA9IG5ldyBBYm9ydENvbnRyb2xsZXIoKTtcbiAgICAgIGNvbnN0IHRpbWVvdXQgPSBzZXRUaW1lb3V0KCgpID0+IGNvbnRyb2xsZXIuYWJvcnQoKSwgUkVRVUVTVF9USU1FT1VUX01TKTtcblxuICAgICAgYXdhaXQgZmV0Y2goYCR7dGhpcy5sZWFkZXJVcmx9L2FwaS9pbmdlc3Qvc2Vzc2lvbmAsIHtcbiAgICAgICAgbWV0aG9kOiAnUE9TVCcsXG4gICAgICAgIGhlYWRlcnM6IHsgJ0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJyB9LFxuICAgICAgICBib2R5OiBKU09OLnN0cmluZ2lmeSh7XG4gICAgICAgICAgc2Vzc2lvbklkOiB0aGlzLnNlc3Npb25JZCxcbiAgICAgICAgICBldmVudCxcbiAgICAgICAgICBwaWQ6IHRoaXMucGlkLFxuICAgICAgICAgIHN0YXJ0ZWRBdDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpLFxuICAgICAgICB9KSxcbiAgICAgICAgc2lnbmFsOiBjb250cm9sbGVyLnNpZ25hbCxcbiAgICAgIH0pO1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXQpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbU2Vzc2lvbkhlYXJ0YmVhdF0gRmFpbGVkIHRvIHNlbmQgJHtldmVudH0gZXZlbnRgKTtcbiAgICB9XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Friendly session names drawn from famous puppets, marionettes,
3
+ * and puppet characters throughout history.
4
+ *
5
+ * Names are assigned from a pool and returned after a cooldown period
6
+ * when sessions end. This keeps the active session list human-readable
7
+ * and on-brand for DollhouseMCP.
8
+ *
9
+ * @since v2.1.0 — Issue #1700
10
+ */
11
+ /**
12
+ * Manages friendly session name assignment from the puppet name pool.
13
+ */
14
+ export declare class SessionNamePool {
15
+ /** Names currently assigned to active sessions: sessionId → name */
16
+ private readonly assigned;
17
+ /** Reverse lookup: name → sessionId */
18
+ private readonly nameToSession;
19
+ /** Names in cooldown after session end */
20
+ private cooldown;
21
+ /**
22
+ * Assign a friendly name to a session.
23
+ * Returns an existing assignment if the session already has one.
24
+ *
25
+ * @param isLeader - If true, follower-only names (e.g., Punch) are excluded
26
+ */
27
+ assign(sessionId: string, isLeader?: boolean): string;
28
+ /**
29
+ * Release a name back to the pool with a cooldown period.
30
+ */
31
+ release(sessionId: string): void;
32
+ /**
33
+ * Get the friendly name for a session, or undefined if not assigned.
34
+ */
35
+ getName(sessionId: string): string | undefined;
36
+ /**
37
+ * Get the canonical color for an assigned session name.
38
+ */
39
+ getColor(sessionId: string): string | undefined;
40
+ /**
41
+ * Get all current assignments.
42
+ */
43
+ getAll(): Map<string, string>;
44
+ private flushCooldowns;
45
+ }
46
+ //# sourceMappingURL=SessionNames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionNames.d.ts","sourceRoot":"","sources":["../../../src/web/console/SessionNames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAyLH;;GAEG;AACH,qBAAa,eAAe;IAC1B,oEAAoE;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,uCAAuC;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,0CAA0C;IAC1C,OAAO,CAAC,QAAQ,CAAuB;IAEvC;;;;;OAKG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,MAAM;IAuCnD;;OAEG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAehC;;OAEG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI9C;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK/C;;OAEG;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAI7B,OAAO,CAAC,cAAc;CAIvB"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Friendly session names drawn from famous puppets, marionettes,
3
+ * and puppet characters throughout history.
4
+ *
5
+ * Names are assigned from a pool and returned after a cooldown period
6
+ * when sessions end. This keeps the active session list human-readable
7
+ * and on-brand for DollhouseMCP.
8
+ *
9
+ * @since v2.1.0 — Issue #1700
10
+ */
11
+ import { randomInt } from 'node:crypto';
12
+ import { logger } from '../../utils/logger.js';
13
+ /**
14
+ * Famous puppets, marionettes, and puppet characters from around the world.
15
+ * Order doesn't matter — the pool is shuffled on startup.
16
+ */
17
+ const ALL_PUPPET_NAMES = [
18
+ // Classic & traditional
19
+ 'Punch',
20
+ 'Judy',
21
+ 'Pinocchio',
22
+ 'Petrouchka',
23
+ 'Pulcinella',
24
+ 'Guignol',
25
+ 'Kasperle',
26
+ // Muppets & Jim Henson
27
+ 'Kermit',
28
+ 'Piggy',
29
+ 'Fozzie',
30
+ 'Gonzo',
31
+ 'Scooter',
32
+ 'Rowlf',
33
+ 'Waldorf',
34
+ 'Statler',
35
+ // Kukla, Fran and Ollie
36
+ 'Kukla',
37
+ 'Ollie',
38
+ // Howdy Doody era
39
+ 'Howdy',
40
+ 'Clarabell',
41
+ // Sesame Street
42
+ 'Grover',
43
+ 'Elmo',
44
+ 'Ernie',
45
+ 'Bert',
46
+ 'Oscar',
47
+ // Ventriloquist & variety
48
+ 'Mortimer',
49
+ 'Lambchop',
50
+ 'Madame',
51
+ 'Topo',
52
+ // International puppetry
53
+ 'Bunraku',
54
+ 'Wayang',
55
+ 'Petrushka',
56
+ 'Hanneschen',
57
+ 'Vitezslav',
58
+ // Modern & pop culture
59
+ 'Salem',
60
+ 'Triumph',
61
+ 'Peanut',
62
+ 'Achmed',
63
+ // Marionette traditions
64
+ 'Fantoccini',
65
+ 'Saltimbanque',
66
+ 'Burattino',
67
+ 'Harlequin',
68
+ // Dollhouse (TV series)
69
+ 'Echo',
70
+ // Inside jokes
71
+ 'Spike',
72
+ 'Angel',
73
+ // Classic dolls
74
+ 'Barbie',
75
+ 'Ken',
76
+ 'Skipper',
77
+ 'Midge',
78
+ 'Christie',
79
+ 'Annie',
80
+ 'Andy',
81
+ 'Cathy',
82
+ 'Teddy',
83
+ 'Xavier',
84
+ 'Strawberry',
85
+ 'Blythe',
86
+ 'Ginny',
87
+ 'Betsy',
88
+ 'Madeline',
89
+ ];
90
+ /** Names that can never be assigned to a leader session */
91
+ const FOLLOWER_ONLY_NAMES = new Set(['Punch']);
92
+ /** Fisher-Yates shuffle using crypto.randomInt (unbiased, no modulo) */
93
+ function shuffleArray(arr) {
94
+ for (let i = arr.length - 1; i > 0; i--) {
95
+ const j = randomInt(i + 1);
96
+ [arr[i], arr[j]] = [arr[j], arr[i]];
97
+ }
98
+ return arr;
99
+ }
100
+ /** Shuffled copy of the name pool — randomized on each process start */
101
+ const PUPPET_NAMES = shuffleArray([...ALL_PUPPET_NAMES]);
102
+ /**
103
+ * Canonical colors for each puppet character.
104
+ * Adjusted from true canonical colors for UI readability in both light/dark themes.
105
+ */
106
+ const PUPPET_COLORS = {
107
+ 'Punch': '#DC143C', // crimson red costume
108
+ 'Judy': '#1E90FF', // blue dress
109
+ 'Pinocchio': '#DAA520', // goldenrod (wooden, yellow hat)
110
+ 'Petrouchka': '#B0BEC5', // blue-gray (white costume, adjusted for visibility)
111
+ 'Pulcinella': '#90A4AE', // gray-blue (white costume, adjusted)
112
+ 'Guignol': '#8B4513', // saddle brown
113
+ 'Kasperle': '#FF0000', // red pointed cap
114
+ 'Kermit': '#4CAF50', // green frog
115
+ 'Piggy': '#E91E8C', // hot pink (glamorous pig)
116
+ 'Fozzie': '#CC7722', // ochre brown bear
117
+ 'Gonzo': '#4169E1', // royal blue
118
+ 'Scooter': '#FF8C00', // dark orange
119
+ 'Rowlf': '#8B6914', // dark goldenrod brown dog
120
+ 'Waldorf': '#556B2F', // dark olive green
121
+ 'Statler': '#708090', // slate gray
122
+ 'Kukla': '#FF0000', // red nose and costume
123
+ 'Ollie': '#228B22', // forest green dragon
124
+ 'Howdy': '#E2725B', // terra cotta
125
+ 'Clarabell': '#FFCC00', // bright yellow clown
126
+ 'Grover': '#4682B4', // steel blue
127
+ 'Elmo': '#FF2400', // scarlet red
128
+ 'Ernie': '#F4A460', // sandy brown
129
+ 'Bert': '#FFD700', // gold yellow
130
+ 'Oscar': '#6B8E23', // olive drab green
131
+ 'Mortimer': '#DEB887', // burlywood
132
+ 'Lambchop': '#D4C5A9', // warm cream (adjusted from pure white)
133
+ 'Madame': '#800080', // purple
134
+ 'Topo': '#A0A0A0', // silver gray
135
+ 'Bunraku': '#B22222', // firebrick red
136
+ 'Wayang': '#6B4226', // dark leather brown (lightened for visibility)
137
+ 'Petrushka': '#FF4500', // orange red
138
+ 'Hanneschen': '#CD5C5C', // indian red
139
+ 'Vitezslav': '#B8860B', // dark goldenrod
140
+ 'Salem': '#4A4A4A', // dark gray (black cat, lightened for visibility)
141
+ 'Triumph': '#6F4E37', // coffee brown
142
+ 'Peanut': '#9370DB', // medium purple
143
+ 'Achmed': '#C8BFA9', // bone/parchment (lightened from beige)
144
+ 'Fantoccini': '#C41E3A', // cardinal red
145
+ 'Saltimbanque': '#DAA520', // goldenrod
146
+ 'Burattino': '#D2691E', // chocolate brown
147
+ 'Harlequin': '#E60026', // diamond red
148
+ 'Echo': '#5C6370', // slate (dark attire, lightened for visibility)
149
+ 'Spike': '#E8DCC8', // platinum/bleach (lightened for readability)
150
+ 'Angel': '#3D3D3D', // charcoal (black duster, lightened for visibility)
151
+ // Classic dolls
152
+ 'Barbie': '#E91E90', // Barbie pink
153
+ 'Ken': '#4A90D9', // Ken blue
154
+ 'Skipper': '#FF6B6B', // coral red
155
+ 'Midge': '#E87040', // warm auburn
156
+ 'Christie': '#C06030', // warm brown
157
+ 'Annie': '#E03030', // Raggedy Ann red yarn hair
158
+ 'Andy': '#3070C0', // Raggedy Andy blue sailor outfit
159
+ 'Cathy': '#D4A574', // Chatty Cathy vintage tan
160
+ 'Teddy': '#A0784A', // Teddy Ruxpin bear brown
161
+ 'Xavier': '#5AAF4A', // Xavier Roberts / Cabbage Patch green
162
+ 'Strawberry': '#E8445A', // strawberry red-pink
163
+ 'Blythe': '#7B68EE', // big-eyed purple
164
+ 'Ginny': '#5B9BD5', // classic blue dress
165
+ 'Betsy': '#DD7694', // rose pink
166
+ 'Madeline': '#FFD700', // yellow hat
167
+ };
168
+ /** Cooldown period before a released name can be reused (ms) */
169
+ const NAME_COOLDOWN_MS = 5 * 60_000; // 5 minutes
170
+ /**
171
+ * Manages friendly session name assignment from the puppet name pool.
172
+ */
173
+ export class SessionNamePool {
174
+ /** Names currently assigned to active sessions: sessionId → name */
175
+ assigned = new Map();
176
+ /** Reverse lookup: name → sessionId */
177
+ nameToSession = new Map();
178
+ /** Names in cooldown after session end */
179
+ cooldown = [];
180
+ /**
181
+ * Assign a friendly name to a session.
182
+ * Returns an existing assignment if the session already has one.
183
+ *
184
+ * @param isLeader - If true, follower-only names (e.g., Punch) are excluded
185
+ */
186
+ assign(sessionId, isLeader = false) {
187
+ // Already assigned?
188
+ const existing = this.assigned.get(sessionId);
189
+ if (existing)
190
+ return existing;
191
+ // Flush expired cooldowns
192
+ this.flushCooldowns();
193
+ // Find an available name, respecting leader restrictions
194
+ const cooldownNames = new Set(this.cooldown.map(c => c.name));
195
+ const availableName = PUPPET_NAMES.find(name => !this.nameToSession.has(name) &&
196
+ !cooldownNames.has(name) &&
197
+ !(isLeader && FOLLOWER_ONLY_NAMES.has(name)));
198
+ if (availableName) {
199
+ this.assigned.set(sessionId, availableName);
200
+ this.nameToSession.set(availableName, sessionId);
201
+ logger.debug(`[SessionNames] Assigned '${availableName}' to ${sessionId}`);
202
+ return availableName;
203
+ }
204
+ // All names in use or cooling down — try cooldown names (oldest first)
205
+ if (this.cooldown.length > 0) {
206
+ const oldest = this.cooldown.shift();
207
+ this.assigned.set(sessionId, oldest.name);
208
+ this.nameToSession.set(oldest.name, sessionId);
209
+ logger.debug(`[SessionNames] Assigned '${oldest.name}' to ${sessionId} (early cooldown release)`);
210
+ return oldest.name;
211
+ }
212
+ // Truly exhausted — fall back to truncated session ID
213
+ const fallback = sessionId.split('-')[1] || sessionId.slice(0, 8);
214
+ this.assigned.set(sessionId, fallback);
215
+ logger.warn(`[SessionNames] Name pool exhausted, using fallback '${fallback}' for ${sessionId}`);
216
+ return fallback;
217
+ }
218
+ /**
219
+ * Release a name back to the pool with a cooldown period.
220
+ */
221
+ release(sessionId) {
222
+ const name = this.assigned.get(sessionId);
223
+ if (!name)
224
+ return;
225
+ this.assigned.delete(sessionId);
226
+ this.nameToSession.delete(name);
227
+ // Only cooldown puppet names, not fallback IDs
228
+ if (PUPPET_NAMES.includes(name)) {
229
+ this.cooldown.push({ name, releasedAt: Date.now() });
230
+ }
231
+ logger.debug(`[SessionNames] Released '${name}' from ${sessionId} (cooldown ${NAME_COOLDOWN_MS / 1000}s)`);
232
+ }
233
+ /**
234
+ * Get the friendly name for a session, or undefined if not assigned.
235
+ */
236
+ getName(sessionId) {
237
+ return this.assigned.get(sessionId);
238
+ }
239
+ /**
240
+ * Get the canonical color for an assigned session name.
241
+ */
242
+ getColor(sessionId) {
243
+ const name = this.assigned.get(sessionId);
244
+ return name ? (PUPPET_COLORS[name] ?? undefined) : undefined;
245
+ }
246
+ /**
247
+ * Get all current assignments.
248
+ */
249
+ getAll() {
250
+ return new Map(this.assigned);
251
+ }
252
+ flushCooldowns() {
253
+ const now = Date.now();
254
+ this.cooldown = this.cooldown.filter(c => (now - c.releasedAt) < NAME_COOLDOWN_MS);
255
+ }
256
+ }
257
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2Vzc2lvbk5hbWVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3dlYi9jb25zb2xlL1Nlc3Npb25OYW1lcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUUvQzs7O0dBR0c7QUFDSCxNQUFNLGdCQUFnQixHQUFzQjtJQUMxQyx3QkFBd0I7SUFDeEIsT0FBTztJQUNQLE1BQU07SUFDTixXQUFXO0lBQ1gsWUFBWTtJQUNaLFlBQVk7SUFDWixTQUFTO0lBQ1QsVUFBVTtJQUVWLHVCQUF1QjtJQUN2QixRQUFRO0lBQ1IsT0FBTztJQUNQLFFBQVE7SUFDUixPQUFPO0lBQ1AsU0FBUztJQUNULE9BQU87SUFDUCxTQUFTO0lBQ1QsU0FBUztJQUVULHdCQUF3QjtJQUN4QixPQUFPO0lBQ1AsT0FBTztJQUVQLGtCQUFrQjtJQUNsQixPQUFPO0lBQ1AsV0FBVztJQUVYLGdCQUFnQjtJQUNoQixRQUFRO0lBQ1IsTUFBTTtJQUNOLE9BQU87SUFDUCxNQUFNO0lBQ04sT0FBTztJQUVQLDBCQUEwQjtJQUMxQixVQUFVO0lBQ1YsVUFBVTtJQUNWLFFBQVE7SUFDUixNQUFNO0lBRU4seUJBQXlCO0lBQ3pCLFNBQVM7SUFDVCxRQUFRO0lBQ1IsV0FBVztJQUNYLFlBQVk7SUFDWixXQUFXO0lBRVgsdUJBQXVCO0lBQ3ZCLE9BQU87SUFDUCxTQUFTO0lBQ1QsUUFBUTtJQUNSLFFBQVE7SUFFUix3QkFBd0I7SUFDeEIsWUFBWTtJQUNaLGNBQWM7SUFDZCxXQUFXO0lBQ1gsV0FBVztJQUVYLHdCQUF3QjtJQUN4QixNQUFNO0lBRU4sZUFBZTtJQUNmLE9BQU87SUFDUCxPQUFPO0lBRVAsZ0JBQWdCO0lBQ2hCLFFBQVE7SUFDUixLQUFLO0lBQ0wsU0FBUztJQUNULE9B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WyIvKipcbiAqIEZyaWVuZGx5IHNlc3Npb24gbmFtZXMgZHJhd24gZnJvbSBmYW1vdXMgcHVwcGV0cywgbWFyaW9uZXR0ZXMsXG4gKiBhbmQgcHVwcGV0IGNoYXJhY3RlcnMgdGhyb3VnaG91dCBoaXN0b3J5LlxuICpcbiAqIE5hbWVzIGFyZSBhc3NpZ25lZCBmcm9tIGEgcG9vbCBhbmQgcmV0dXJuZWQgYWZ0ZXIgYSBjb29sZG93biBwZXJpb2RcbiAqIHdoZW4gc2Vzc2lvbnMgZW5kLiBUaGlzIGtlZXBzIHRoZSBhY3RpdmUgc2Vzc2lvbiBsaXN0IGh1bWFuLXJlYWRhYmxlXG4gKiBhbmQgb24tYnJhbmQgZm9yIERvbGxob3VzZU1DUC5cbiAqXG4gKiBAc2luY2UgdjIuMS4wIOKAlCBJc3N1ZSAjMTcwMFxuICovXG5cbmltcG9ydCB7IHJhbmRvbUludCB9IGZyb20gJ25vZGU6Y3J5cHRvJztcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gJy4uLy4uL3V0aWxzL2xvZ2dlci5qcyc7XG5cbi8qKlxuICogRmFtb3VzIHB1cHBldHMsIG1hcmlvbmV0dGVzLCBhbmQgcHVwcGV0IGNoYXJhY3RlcnMgZnJvbSBhcm91bmQgdGhlIHdvcmxkLlxuICogT3JkZXIgZG9lc24ndCBtYXR0ZXIg4oCUIHRoZSBwb29sIGlzIHNodWZmbGVkIG9uIHN0YXJ0dXAuXG4gKi9cbmNvbnN0IEFMTF9QVVBQRVRfTkFNRVM6IHJlYWRvbmx5IHN0cmluZ1tdID0gW1xuICAvLyBDbGFzc2ljICYgdHJhZGl0aW9uYWxcbiAgJ1B1bmNoJyxcbiAgJ0p1ZHknLFxuICAnUGlub2NjaGlvJyxcbiAgJ1BldHJvdWNoa2EnLFxuICAnUHVsY2luZWxsYScsXG4gICdHdWlnbm9sJyxcbiAgJ0thc3BlcmxlJyxcblxuICAvLyBNdXBwZXRzICYgSmltIEhlbnNvblxuICAnS2VybWl0JyxcbiAgJ1BpZ2d5JyxcbiAgJ0ZvenppZScsXG4gICdHb256bycsXG4gICdTY29vdGVyJyxcbiAgJ1Jvd2xmJyxcbiAgJ1dhbGRvcmYnLFxuICAnU3RhdGxlcicsXG5cbiAgLy8gS3VrbGEsIEZyYW4gYW5kIE9sbGllXG4gICdLdWtsYScsXG4gICdPbGxpZScsXG5cbiAgLy8gSG93ZHkgRG9vZHkgZXJhXG4gICdIb3dkeScsXG4gICdDbGFyYWJlbGwnLFxuXG4gIC8vIFNlc2FtZSBTdHJlZXRcbiAgJ0dyb3ZlcicsXG4gICdFbG1vJyxcbiAgJ0VybmllJyxcbiAgJ0JlcnQnLFxuICAnT3NjYXInLFxuXG4gIC8vIFZlbnRyaWxvcXVpc3QgJiB2YXJpZXR5XG4gICdNb3J0aW1lcicsXG4gICdMYW1iY2hvcCcsXG4gICdNYWRhbWUnLFxuICAnVG9wbycsXG5cbiAgLy8gSW50ZXJuYXRpb25hbCBwdXBwZXRyeVxuICAnQnVucmFrdScsXG4gICdXYXlhbmcnLFxuICAnUGV0cnVzaGthJyxcbiAgJ0hhbm5lc2NoZW4nLFxuICAnVml0ZXpzbGF2JyxcblxuICAvLyBNb2Rlcm4gJiBwb3AgY3VsdHVyZVxuICAnU2FsZW0nLFxuICAnVHJpdW1waCcsXG4gICdQZWFudXQnLFxuICAnQWNobWVkJyxcblxuICAvLyBNYXJpb25ldHRlIHRyYWRpdGlvbnNcbiAgJ0ZhbnRvY2NpbmknLFxuICAnU2FsdGltYmFucXVlJyxcbiAgJ0J1cmF0dGlubycsXG4gICdIYXJsZXF1aW4nLFxuXG4gIC8vIERvbGxob3VzZSAoVFYgc2VyaWVzKVxuICAnRWNobycsXG5cbiAgLy8gSW5zaWRlIGpva2VzXG4gICdTcGlrZScsXG4gICdBbmdlbCcsXG5cbiAgLy8gQ2xhc3NpYyBkb2xsc1xuICAnQmFyYmllJyxcbiAgJ0tlbicsXG4gICdTa2lwcGVyJyxcbiAgJ01pZGdlJyxcbiAgJ0NocmlzdGllJyxcbiAgJ0FubmllJyxcbiAgJ0FuZHknLFxuICAnQ2F0aHknLFxuICAnVGVkZHknLFxuICAnWGF2aWVyJyxcbiAgJ1N0cmF3YmVycnknLFxuICAnQmx5dGhlJyxcbiAgJ0dpbm55JyxcbiAgJ0JldHN5JyxcbiAgJ01hZGVsaW5lJyxcbl07XG5cbi8qKiBOYW1lcyB0aGF0IGNhbiBuZXZlciBiZSBhc3NpZ25lZCB0byBhIGxlYWRlciBzZXNzaW9uICovXG5jb25zdCBGT0xMT1dFUl9PTkxZX05BTUVTID0gbmV3IFNldChbJ1B1bmNoJ10pO1xuXG4vKiogRmlzaGVyLVlhdGVzIHNodWZmbGUgdXNpbmcgY3J5cHRvLnJhbmRvbUludCAodW5iaWFzZWQsIG5vIG1vZHVsbykgKi9cbmZ1bmN0aW9uIHNodWZmbGVBcnJheTxUPihhcnI6IFRbXSk6IFRbXSB7XG4gIGZvciAobGV0IGkgPSBhcnIubGVuZ3RoIC0gMTsgaSA+IDA7IGktLSkge1xuICAgIGNvbnN0IGogPSByYW5kb21JbnQoaSArIDEpO1xuICAgIFthcnJbaV0sIGFycltqXV0gPSBbYXJyW2pdLCBhcnJbaV1dO1xuICB9XG4gIHJldHVybiBhcnI7XG59XG5cbi8qKiBTaHVmZmxlZCBjb3B5IG9mIHRoZSBuYW1lIHBvb2wg4oCUIHJhbmRvbWl6ZWQgb24gZWFjaCBwcm9jZXNzIHN0YXJ0ICovXG5jb25zdCBQVVBQRVRfTkFNRVM6IHN0cmluZ1tdID0gc2h1ZmZsZUFycmF5KFsuLi5BTExfUFVQUEVUX05BTUVTXSk7XG5cbi8qKlxuICogQ2Fub25pY2FsIGNvbG9ycyBmb3IgZWFjaCBwdXBwZXQgY2hhcmFjdGVyLlxuICogQWRqdXN0ZWQgZnJvbSB0cnVlIGNhbm9uaWNhbCBjb2xvcnMgZm9yIFVJIHJlYWRhYmlsaXR5IGluIGJvdGggbGlnaHQvZGFyayB0aGVtZXMuXG4gKi9cbmNvbnN0IFBVUFBFVF9DT0xPUlM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4gPSB7XG4gICdQdW5jaCc6ICAgICAgICAnI0RDMTQzQycsIC8vIGNyaW1zb24gcmVkIGNvc3R1bWVcbiAgJ0p1ZHknOiAgICAgICAgICcjMUU5MEZGJywgLy8gYmx1ZSBkcmVzc1xuICAnUGlub2NjaGlvJzogICAgJyNEQUE1MjAnLCAvLyBnb2xkZW5yb2QgKHdvb2RlbiwgeWVsbG93IGhhdClcbiAgJ1BldHJvdWNoa2EnOiAgICcjQjBCRUM1JywgLy8gYmx1ZS1ncmF5ICh3aGl0ZSBjb3N0dW1lLCBhZGp1c3RlZCBmb3IgdmlzaWJpbGl0eSlcbiAgJ1B1bGNpbmVsbGEnOiAgICcjOTBBNEFFJywgLy8gZ3JheS1ibHVlICh3aGl0ZSBjb3N0dW1lLCBhZGp1c3RlZClcbiAgJ0d1aWdub2wnOiAgICAgICcjOEI0NTEzJywgLy8gc2FkZGxlIGJyb3duXG4gICdLYXNwZXJsZSc6ICAgICAnI0ZGMDAwMCcsIC8vIHJlZCBwb2ludGVkIGNhcFxuICAnS2VybWl0JzogICAgICAgJyM0Q0FGNTAnLCAvLyBncmVlbiBmcm9nXG4gICdQaWdneSc6ICAgICAgICAnI0U5MUU4QycsIC8vIGhvdCBwaW5rIChnbGFtb3JvdXMgcGlnKVxuICAnRm96emllJzogICAgICAgJyNDQzc3MjInLCAvLyBvY2hyZSBicm93biBiZWFyXG4gICdHb256byc6ICAgICAgICAnIzQxNjlFMScsIC8vIHJveWFsIGJsdWVcbiAgJ1Njb290ZXInOiAgICAgICcjRkY4QzAwJywgLy8gZGFyayBvcmFuZ2VcbiAgJ1Jvd2xmJzogICAgICAgICcjOEI2OTE0JywgLy8gZGFyayBnb2xkZW5yb2QgYnJvd24gZG9nXG4gICdXYWxkb3JmJzogICAgICAnIzU1NkIyRicsIC8vIGRhcmsgb2xpdmUgZ3JlZW5cbiAgJ1N0YXRsZXInOiAgICAgICcjNzA4MDkwJywgLy8gc2xhdGUgZ3JheVxuICAnS3VrbGEnOiAgICAgICAgJyNGRjAwMDAnLCAvLyByZWQgbm9zZSBhbmQgY29zdHVtZVxuICAnT2xsaWUnOiAgICAgICAgJyMyMjhCMjInLCAvLyBmb3Jlc3QgZ3JlZW4gZHJhZ29uXG4gICdIb3dkeSc6ICAgICAgICAnI0UyNzI1QicsIC8vIHRlcnJhIGNvdHRhXG4gICdDbGFyYWJlbGwnOiAgICAnI0ZGQ0MwMCcsIC8vIGJyaWdodCB5ZWxsb3cgY2xvd25cbiAgJ0dyb3Zlcic6ICAgICAgICcjNDY4MkI0JywgLy8gc3RlZWwgYmx1ZVxuICAnRWxtbyc6ICAgICAgICAgJyNGRjI0MDAnLCAvLyBzY2FybGV0IHJlZFxuICAnRXJuaWUnOiAgICAgICAgJyNGNEE0NjAnLCAvLyBzYW5keSBicm93blxuICAnQmVydCc6ICAgICAgICAgJyNGRkQ3MDAnLCAvLyBnb2xkIHllbGxvd1xuICAnT3NjYXInOiAgICAgICAgJyM2QjhFMjMnLCAvLyBvbGl2ZSBkcmFiIGdyZWVuXG4gICdNb3J0aW1lcic6ICAgICAnI0RFQjg4NycsIC8vIGJ1cmx5d29vZFxuICAnTGFtYmNob3AnOiAgICAgJyNENEM1QTknLCAvLyB3YXJtIGNyZWFtIChhZGp1c3RlZCBmcm9tIHB1cmUgd2hpdGUpXG4gICdNYWRhbWUnOiAgICAgICAnIzgwMDA4MCcsIC8vIHB1cnBsZVxuICAnVG9wbyc6ICAgICAgICAgJyNBMEEwQTAnLCAvLyBzaWx2ZXIgZ3JheVxuICAnQnVucmFrdSc6ICAgICAgJyNCMjIyMjInLCAvLyBmaXJlYnJpY2sgcmVkXG4gICdXYXlhbmcnOiAgICAgICAnIzZCNDIyNicsIC8vIGRhcmsgbGVhdGhlciBicm93biAobGlnaHRlbmVkIGZvciB2aXNpYmlsaXR5KVxuICAnUGV0cnVzaGthJzogICAgJyNGRjQ1MDAnLCAvLyBvcmFuZ2UgcmVkXG4gICdIYW5uZXNjaGVuJzogICAnI0NENUM1QycsIC8vIGluZGlhbiByZWRcbiAgJ1ZpdGV6c2xhdic6ICAgICcjQjg4NjBCJywgLy8gZGFyayBnb2xkZW5yb2RcbiAgJ1NhbGVtJzogICAgICAgICcjNEE0QTRBJywgLy8gZGFyayBncmF5IChibGFjayBjYXQsIGxpZ2h0ZW5lZCBmb3IgdmlzaWJpbGl0eSlcbiAgJ1RyaXVtcGgnOiAgICAgICcjNkY0RTM3JywgLy8gY29mZmVlIGJyb3duXG4gICdQZWFudXQnOiAgICAgICAnIzkzNzBEQicsIC8vIG1lZGl1bSBwdXJwbGVcbiAgJ0FjaG1lZCc6ICAgICAgICcjQzhCRkE5JywgLy8gYm9uZS9wYXJjaG1lbnQgKGxpZ2h0ZW5lZCBmcm9tIGJlaWdlKVxuICAnRmFudG9jY2luaSc6ICAgJyNDNDFFM0EnLCAvLyBjYXJkaW5hbCByZWRcbiAgJ1NhbHRpbWJhbnF1ZSc6ICcjREFBNTIwJywgLy8gZ29sZGVucm9kXG4gICdCdXJhdHRpbm8nOiAgICAnI0QyNjkxRScsIC8vIGNob2NvbGF0ZSBicm93blxuICAnSGFybGVxdWluJzogICAgJyNFNjAwMjYnLCAvLyBkaWFtb25kIHJlZFxuICAnRWNobyc6ICAgICAgICAgJyM1QzYzNzAnLCAvLyBzbGF0ZSAoZGFyayBhdHRpcmUsIGxpZ2h0ZW5lZCBmb3IgdmlzaWJpbGl0eSlcbiAgJ1NwaWtlJzogICAgICAgICcjRThEQ0M4JywgLy8gcGxhdGludW0vYmxlYWNoIChsaWdodGVuZWQgZm9yIHJlYWRhYmlsaXR5KVxuICAnQW5nZWwnOiAgICAgICAgJyMzRDNEM0QnLCAvLyBjaGFyY29hbCAoYmxhY2sgZHVzdGVyLCBsaWdodGVuZWQgZm9yIHZpc2liaWxpdHkpXG5cbiAgLy8gQ2xhc3NpYyBkb2xsc1xuICAnQmFyYmllJzogICAgICAgJyNFOTFFOTAnLCAvLyBCYXJiaWUgcGlua1xuICAnS2VuJzogICAgICAgICAgJyM0QTkwRDknLCAvLyBLZW4gYmx1ZVxuICAnU2tpcHBlcic6ICAgICAgJyNGRjZCNkInLCAvLyBjb3JhbCByZWRcbiAgJ01pZGdlJzogICAgICAgICcjRTg3MDQwJywgLy8gd2FybSBhdWJ1cm5cbiAgJ0NocmlzdGllJzogICAgICcjQzA2MDMwJywgLy8gd2FybSBicm93blxuICAnQW5uaWUnOiAgICAgICAgJyNFMDMwMzAnLCAvLyBSYWdnZWR5IEFubiByZWQgeWFybiBoYWlyXG4gICdBbmR5JzogICAgICAgICAnIzMwNzBDMCcsIC8vIFJhZ2dlZHkgQW5keSBibHVlIHNhaWxvciBvdXRmaXRcbiAgJ0NhdGh5JzogICAgICAgICcjRDRBNTc0JywgLy8gQ2hhdHR5IENhdGh5IHZpbnRhZ2UgdGFuXG4gICdUZWRkeSc6ICAgICAgICAnI0EwNzg0QScsIC8vIFRlZGR5IFJ1eHBpbiBiZWFyIGJyb3duXG4gICdYYXZpZXInOiAgICAgICAnIzVBQUY0QScsIC8vIFhhdmllciBSb2JlcnRzIC8gQ2FiYmFnZSBQYXRjaCBncmVlblxuICAnU3RyYXdiZXJyeSc6ICAgJyNFODQ0NUEnLCAvLyBzdHJhd2JlcnJ5IHJlZC1waW5rXG4gICdCbHl0aGUnOiAgICAgICAnIzdCNjhFRScsIC8vIGJpZy1leWVkIHB1cnBsZVxuICAnR2lubnknOiAgICAgICAgJyM1QjlCRDUnLCAvLyBjbGFzc2ljIGJsdWUgZHJlc3NcbiAgJ0JldHN5JzogICAgICAgICcjREQ3Njk0JywgLy8gcm9zZSBwaW5rXG4gICdNYWRlbGluZSc6ICAgICAnI0ZGRDcwMCcsIC8vIHllbGxvdyBoYXRcbn07XG5cbi8qKiBDb29sZG93biBwZXJpb2QgYmVmb3JlIGEgcmVsZWFzZWQgbmFtZSBjYW4gYmUgcmV1c2VkIChtcykgKi9cbmNvbnN0IE5BTUVfQ09PTERPV05fTVMgPSA1ICogNjBfMDAwOyAvLyA1IG1pbnV0ZXNcblxuaW50ZXJmYWNlIENvb2xkb3duRW50cnkge1xuICBuYW1lOiBzdHJpbmc7XG4gIHJlbGVhc2VkQXQ6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBNYW5hZ2VzIGZyaWVuZGx5IHNlc3Npb24gbmFtZSBhc3NpZ25tZW50IGZyb20gdGhlIHB1cHBldCBuYW1lIHBvb2wuXG4gKi9cbmV4cG9ydCBjbGFzcyBTZXNzaW9uTmFtZVBvb2wge1xuICAvKiogTmFtZXMgY3VycmVudGx5IGFzc2lnbmVkIHRvIGFjdGl2ZSBzZXNzaW9uczogc2Vzc2lvbklkIOKGkiBuYW1lICovXG4gIHByaXZhdGUgcmVhZG9ubHkgYXNzaWduZWQgPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICAvKiogUmV2ZXJzZSBsb29rdXA6IG5hbWUg4oaSIHNlc3Npb25JZCAqL1xuICBwcml2YXRlIHJlYWRvbmx5IG5hbWVUb1Nlc3Npb24gPSBuZXcgTWFwPHN0cmluZywgc3RyaW5nPigpO1xuICAvKiogTmFtZXMgaW4gY29vbGRvd24gYWZ0ZXIgc2Vzc2lvbiBlbmQgKi9cbiAgcHJpdmF0ZSBjb29sZG93bjogQ29vbGRvd25FbnRyeVtdID0gW107XG5cbiAgLyoqXG4gICAqIEFzc2lnbiBhIGZyaWVuZGx5IG5hbWUgdG8gYSBzZXNzaW9uLlxuICAgKiBSZXR1cm5zIGFuIGV4aXN0aW5nIGFzc2lnbm1lbnQgaWYgdGhlIHNlc3Npb24gYWxyZWFkeSBoYXMgb25lLlxuICAgKlxuICAgKiBAcGFyYW0gaXNMZWFkZXIgLSBJZiB0cnVlLCBmb2xsb3dlci1vbmx5IG5hbWVzIChlLmcuLCBQdW5jaCkgYXJlIGV4Y2x1ZGVkXG4gICAqL1xuICBhc3NpZ24oc2Vzc2lvbklkOiBzdHJpbmcsIGlzTGVhZGVyID0gZmFsc2UpOiBzdHJpbmcge1xuICAgIC8vIEFscmVhZHkgYXNzaWduZWQ/XG4gICAgY29uc3QgZXhpc3RpbmcgPSB0aGlzLmFzc2lnbmVkLmdldChzZXNzaW9uSWQpO1xuICAgIGlmIChleGlzdGluZykgcmV0dXJuIGV4aXN0aW5nO1xuXG4gICAgLy8gRmx1c2ggZXhwaXJlZCBjb29sZG93bnNcbiAgICB0aGlzLmZsdXNoQ29vbGRvd25zKCk7XG5cbiAgICAvLyBGaW5kIGFuIGF2YWlsYWJsZSBuYW1lLCByZXNwZWN0aW5nIGxlYWRlciByZXN0cmljdGlvbnNcbiAgICBjb25zdCBjb29sZG93bk5hbWVzID0gbmV3IFNldCh0aGlzLmNvb2xkb3duLm1hcChjID0+IGMubmFtZSkpO1xuICAgIGNvbnN0IGF2YWlsYWJsZU5hbWUgPSBQVVBQRVRfTkFNRVMuZmluZChcbiAgICAgIG5hbWUgPT4gIXRoaXMubmFtZVRvU2Vzc2lvbi5oYXMobmFtZSkgJiZcbiAgICAgICAgICAgICAgIWNvb2xkb3duTmFtZXMuaGFzKG5hbWUpICYmXG4gICAgICAgICAgICAgICEoaXNMZWFkZXIgJiYgRk9MTE9XRVJfT05MWV9OQU1FUy5oYXMobmFtZSkpXG4gICAgKTtcblxuICAgIGlmIChhdmFpbGFibGVOYW1lKSB7XG4gICAgICB0aGlzLmFzc2lnbmVkLnNldChzZXNzaW9uSWQsIGF2YWlsYWJsZU5hbWUpO1xuICAgICAgdGhpcy5uYW1lVG9TZXNzaW9uLnNldChhdmFpbGFibGVOYW1lLCBzZXNzaW9uSWQpO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbU2Vzc2lvbk5hbWVzXSBBc3NpZ25lZCAnJHthdmFpbGFibGVOYW1lfScgdG8gJHtzZXNzaW9uSWR9YCk7XG4gICAgICByZXR1cm4gYXZhaWxhYmxlTmFtZTtcbiAgICB9XG5cbiAgICAvLyBBbGwgbmFtZXMgaW4gdXNlIG9yIGNvb2xpbmcgZG93biDigJQgdHJ5IGNvb2xkb3duIG5hbWVzIChvbGRlc3QgZmlyc3QpXG4gICAgaWYgKHRoaXMuY29vbGRvd24ubGVuZ3RoID4gMCkge1xuICAgICAgY29uc3Qgb2xkZXN0ID0gdGhpcy5jb29sZG93bi5zaGlmdCgpITtcbiAgICAgIHRoaXMuYXNzaWduZWQuc2V0KHNlc3Npb25JZCwgb2xkZXN0Lm5hbWUpO1xuICAgICAgdGhpcy5uYW1lVG9TZXNzaW9uLnNldChvbGRlc3QubmFtZSwgc2Vzc2lvbklkKTtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgW1Nlc3Npb25OYW1lc10gQXNzaWduZWQgJyR7b2xkZXN0Lm5hbWV9JyB0byAke3Nlc3Npb25JZH0gKGVhcmx5IGNvb2xkb3duIHJlbGVhc2UpYCk7XG4gICAgICByZXR1cm4gb2xkZXN0Lm5hbWU7XG4gICAgfVxuXG4gICAgLy8gVHJ1bHkgZXhoYXVzdGVkIOKAlCBmYWxsIGJhY2sgdG8gdHJ1bmNhdGVkIHNlc3Npb24gSURcbiAgICBjb25zdCBmYWxsYmFjayA9IHNlc3Npb25JZC5zcGxpdCgnLScpWzFdIHx8IHNlc3Npb25JZC5zbGljZSgwLCA4KTtcbiAgICB0aGlzLmFzc2lnbmVkLnNldChzZXNzaW9uSWQsIGZhbGxiYWNrKTtcbiAgICBsb2dnZXIud2FybihgW1Nlc3Npb25OYW1lc10gTmFtZSBwb29sIGV4aGF1c3RlZCwgdXNpbmcgZmFsbGJhY2sgJyR7ZmFsbGJhY2t9JyBmb3IgJHtzZXNzaW9uSWR9YCk7XG4gICAgcmV0dXJuIGZhbGxiYWNrO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbGVhc2UgYSBuYW1lIGJhY2sgdG8gdGhlIHBvb2wgd2l0aCBhIGNvb2xkb3duIHBlcmlvZC5cbiAgICovXG4gIHJlbGVhc2Uoc2Vzc2lvbklkOiBzdHJpbmcpOiB2b2lkIHtcbiAgICBjb25zdCBuYW1lID0gdGhpcy5hc3NpZ25lZC5nZXQoc2Vzc2lvbklkKTtcbiAgICBpZiAoIW5hbWUpIHJldHVybjtcblxuICAgIHRoaXMuYXNzaWduZWQuZGVsZXRlKHNlc3Npb25JZCk7XG4gICAgdGhpcy5uYW1lVG9TZXNzaW9uLmRlbGV0ZShuYW1lKTtcblxuICAgIC8vIE9ubHkgY29vbGRvd24gcHVwcGV0IG5hbWVzLCBub3QgZmFsbGJhY2sgSURzXG4gICAgaWYgKFBVUFBFVF9OQU1FUy5pbmNsdWRlcyhuYW1lKSkge1xuICAgICAgdGhpcy5jb29sZG93bi5wdXNoKHsgbmFtZSwgcmVsZWFzZWRBdDogRGF0ZS5ub3coKSB9KTtcbiAgICB9XG5cbiAgICBsb2dnZXIuZGVidWcoYFtTZXNzaW9uTmFtZXNdIFJlbGVhc2VkICcke25hbWV9JyBmcm9tICR7c2Vzc2lvbklkfSAoY29vbGRvd24gJHtOQU1FX0NPT0xET1dOX01TIC8gMTAwMH1zKWApO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCB0aGUgZnJpZW5kbHkgbmFtZSBmb3IgYSBzZXNzaW9uLCBvciB1bmRlZmluZWQgaWYgbm90IGFzc2lnbmVkLlxuICAgKi9cbiAgZ2V0TmFtZShzZXNzaW9uSWQ6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgcmV0dXJuIHRoaXMuYXNzaWduZWQuZ2V0KHNlc3Npb25JZCk7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSBjYW5vbmljYWwgY29sb3IgZm9yIGFuIGFzc2lnbmVkIHNlc3Npb24gbmFtZS5cbiAgICovXG4gIGdldENvbG9yKHNlc3Npb25JZDogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBuYW1lID0gdGhpcy5hc3NpZ25lZC5nZXQoc2Vzc2lvbklkKTtcbiAgICByZXR1cm4gbmFtZSA/IChQVVBQRVRfQ09MT1JTW25hbWVdID8/IHVuZGVmaW5lZCkgOiB1bmRlZmluZWQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGFsbCBjdXJyZW50IGFzc2lnbm1lbnRzLlxuICAgKi9cbiAgZ2V0QWxsKCk6IE1hcDxzdHJpbmcsIHN0cmluZz4ge1xuICAgIHJldHVybiBuZXcgTWFwKHRoaXMuYXNzaWduZWQpO1xuICB9XG5cbiAgcHJpdmF0ZSBmbHVzaENvb2xkb3ducygpOiB2b2lkIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIHRoaXMuY29vbGRvd24gPSB0aGlzLmNvb2xkb3duLmZpbHRlcihjID0+IChub3cgLSBjLnJlbGVhc2VkQXQpIDwgTkFNRV9DT09MRE9XTl9NUyk7XG4gIH1cbn1cbiJdfQ==