agent-embassy 2.0.1 → 3.0.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 (144) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CONTRIBUTING.md +19 -34
  3. package/README.md +110 -228
  4. package/SECURITY.md +55 -89
  5. package/dist/src/errors.d.ts +10 -1
  6. package/dist/src/errors.js +3 -1
  7. package/dist/src/errors.js.map +1 -1
  8. package/dist/src/gateway/claude-helper-protocol.d.ts +8 -11
  9. package/dist/src/gateway/claude-helper-protocol.js +12 -11
  10. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  11. package/dist/src/gateway/claude-helper-supervisor.d.ts +1 -5
  12. package/dist/src/gateway/claude-helper-supervisor.js +10 -9
  13. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  14. package/dist/src/gateway/claude-helper.js +6 -6
  15. package/dist/src/gateway/claude-helper.js.map +1 -1
  16. package/dist/src/gateway/claude-peer.d.ts +0 -3
  17. package/dist/src/gateway/claude-peer.js +6 -18
  18. package/dist/src/gateway/claude-peer.js.map +1 -1
  19. package/dist/src/gateway/cli.d.ts +17 -6
  20. package/dist/src/gateway/cli.js +912 -261
  21. package/dist/src/gateway/cli.js.map +1 -1
  22. package/dist/src/gateway/codex-socket-holder.d.ts +26 -0
  23. package/dist/src/gateway/codex-socket-holder.js +76 -0
  24. package/dist/src/gateway/codex-socket-holder.js.map +1 -0
  25. package/dist/src/gateway/codex-stateless-transport.js +1 -1
  26. package/dist/src/gateway/codex-stateless-transport.js.map +1 -1
  27. package/dist/src/gateway/config.d.ts +1 -10
  28. package/dist/src/gateway/config.js +4 -10
  29. package/dist/src/gateway/config.js.map +1 -1
  30. package/dist/src/gateway/control.d.ts +45 -77
  31. package/dist/src/gateway/control.js +56 -141
  32. package/dist/src/gateway/control.js.map +1 -1
  33. package/dist/src/gateway/federation-nodes.d.ts +29 -2
  34. package/dist/src/gateway/federation-nodes.js +177 -7
  35. package/dist/src/gateway/federation-nodes.js.map +1 -1
  36. package/dist/src/gateway/peer-client.d.ts +4 -3
  37. package/dist/src/gateway/peer-client.js +22 -13
  38. package/dist/src/gateway/peer-client.js.map +1 -1
  39. package/dist/src/gateway/peer-protocol.d.ts +8 -11
  40. package/dist/src/gateway/peer-protocol.js +5 -12
  41. package/dist/src/gateway/peer-protocol.js.map +1 -1
  42. package/dist/src/gateway/provenance-envelope.d.ts +0 -1
  43. package/dist/src/gateway/provenance-envelope.js +4 -19
  44. package/dist/src/gateway/provenance-envelope.js.map +1 -1
  45. package/dist/src/gateway/providers.d.ts +7 -4
  46. package/dist/src/gateway/providers.js +16 -20
  47. package/dist/src/gateway/providers.js.map +1 -1
  48. package/dist/src/gateway/server.d.ts +3 -12
  49. package/dist/src/gateway/server.js +31 -50
  50. package/dist/src/gateway/server.js.map +1 -1
  51. package/dist/src/gateway/service-agent.d.ts +187 -0
  52. package/dist/src/gateway/service-agent.js +758 -0
  53. package/dist/src/gateway/service-agent.js.map +1 -0
  54. package/dist/src/gateway/service.d.ts +114 -31
  55. package/dist/src/gateway/service.js +461 -566
  56. package/dist/src/gateway/service.js.map +1 -1
  57. package/dist/src/gateway/status-view.d.ts +167 -0
  58. package/dist/src/gateway/status-view.js +488 -0
  59. package/dist/src/gateway/status-view.js.map +1 -0
  60. package/dist/src/gateway/store.d.ts +103 -21
  61. package/dist/src/gateway/store.js +454 -529
  62. package/dist/src/gateway/store.js.map +1 -1
  63. package/dist/src/gateway/types.d.ts +48 -99
  64. package/dist/src/gateway/types.js +15 -52
  65. package/dist/src/gateway/types.js.map +1 -1
  66. package/docs/CONFIGURATION.md +173 -44
  67. package/docs/DELIVERY.md +11 -11
  68. package/docs/GATEWAY-ARCHITECTURE.md +277 -375
  69. package/package.json +4 -12
  70. package/skills/embassy-peer/SKILL.md +65 -90
  71. package/skills/embassy-peer/agents/openai.yaml +1 -1
  72. package/README.zh-CN.md +0 -275
  73. package/assets/live-dashboard/app.css +0 -1619
  74. package/assets/vendor/react/LICENSE +0 -21
  75. package/assets/vendor/react/react-dom.production.min.js +0 -267
  76. package/assets/vendor/react/react.production.min.js +0 -31
  77. package/dist/src/gateway/acp-client.d.ts +0 -110
  78. package/dist/src/gateway/acp-client.js +0 -407
  79. package/dist/src/gateway/acp-client.js.map +0 -1
  80. package/dist/src/gateway/acp-provider.d.ts +0 -66
  81. package/dist/src/gateway/acp-provider.js +0 -275
  82. package/dist/src/gateway/acp-provider.js.map +0 -1
  83. package/dist/src/gateway/cli-copy.d.ts +0 -8
  84. package/dist/src/gateway/cli-copy.en.d.ts +0 -22
  85. package/dist/src/gateway/cli-copy.en.js +0 -62
  86. package/dist/src/gateway/cli-copy.en.js.map +0 -1
  87. package/dist/src/gateway/cli-copy.js +0 -27
  88. package/dist/src/gateway/cli-copy.js.map +0 -1
  89. package/dist/src/gateway/cli-copy.zh-CN.d.ts +0 -22
  90. package/dist/src/gateway/cli-copy.zh-CN.js +0 -62
  91. package/dist/src/gateway/cli-copy.zh-CN.js.map +0 -1
  92. package/dist/src/gateway/codex-doctor.d.ts +0 -36
  93. package/dist/src/gateway/codex-doctor.js +0 -127
  94. package/dist/src/gateway/codex-doctor.js.map +0 -1
  95. package/dist/src/gateway/dashboard-copy.d.ts +0 -7
  96. package/dist/src/gateway/dashboard-copy.en.d.ts +0 -504
  97. package/dist/src/gateway/dashboard-copy.en.js +0 -505
  98. package/dist/src/gateway/dashboard-copy.en.js.map +0 -1
  99. package/dist/src/gateway/dashboard-copy.js +0 -514
  100. package/dist/src/gateway/dashboard-copy.js.map +0 -1
  101. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +0 -504
  102. package/dist/src/gateway/dashboard-copy.zh-CN.js +0 -505
  103. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +0 -1
  104. package/dist/src/gateway/dashboard-model.d.ts +0 -343
  105. package/dist/src/gateway/dashboard-model.js +0 -1061
  106. package/dist/src/gateway/dashboard-model.js.map +0 -1
  107. package/dist/src/gateway/dashboard.d.ts +0 -20
  108. package/dist/src/gateway/dashboard.js +0 -874
  109. package/dist/src/gateway/dashboard.js.map +0 -1
  110. package/dist/src/gateway/deepseek-detect.d.ts +0 -14
  111. package/dist/src/gateway/deepseek-detect.js +0 -41
  112. package/dist/src/gateway/deepseek-detect.js.map +0 -1
  113. package/dist/src/gateway/live-dashboard-app/app.js +0 -2385
  114. package/dist/src/gateway/live-dashboard-assets.d.ts +0 -10
  115. package/dist/src/gateway/live-dashboard-assets.js +0 -74
  116. package/dist/src/gateway/live-dashboard-assets.js.map +0 -1
  117. package/dist/src/gateway/live-dashboard-command.d.ts +0 -60
  118. package/dist/src/gateway/live-dashboard-command.js +0 -334
  119. package/dist/src/gateway/live-dashboard-command.js.map +0 -1
  120. package/dist/src/gateway/live-dashboard-http.d.ts +0 -39
  121. package/dist/src/gateway/live-dashboard-http.js +0 -383
  122. package/dist/src/gateway/live-dashboard-http.js.map +0 -1
  123. package/dist/src/gateway/live-dashboard-protocol.d.ts +0 -34
  124. package/dist/src/gateway/live-dashboard-protocol.js +0 -114
  125. package/dist/src/gateway/live-dashboard-protocol.js.map +0 -1
  126. package/dist/src/gateway/live-dashboard-server.d.ts +0 -33
  127. package/dist/src/gateway/live-dashboard-server.js +0 -144
  128. package/dist/src/gateway/live-dashboard-server.js.map +0 -1
  129. package/dist/src/gateway/live-dashboard-stream.d.ts +0 -46
  130. package/dist/src/gateway/live-dashboard-stream.js +0 -234
  131. package/dist/src/gateway/live-dashboard-stream.js.map +0 -1
  132. package/dist/src/gateway/live-dashboard.d.ts +0 -28
  133. package/dist/src/gateway/live-dashboard.js +0 -154
  134. package/dist/src/gateway/live-dashboard.js.map +0 -1
  135. package/dist/src/gateway/locale.d.ts +0 -4
  136. package/dist/src/gateway/locale.js +0 -10
  137. package/dist/src/gateway/locale.js.map +0 -1
  138. package/dist/src/gateway/progress-watch-machine.d.ts +0 -45
  139. package/dist/src/gateway/progress-watch-machine.js +0 -70
  140. package/dist/src/gateway/progress-watch-machine.js.map +0 -1
  141. package/docs/CONFIGURATION.zh-CN.md +0 -97
  142. package/docs/DASHBOARD.md +0 -98
  143. package/docs/DASHBOARD.zh-CN.md +0 -49
  144. package/docs/DELIVERY.zh-CN.md +0 -55
@@ -1,2385 +0,0 @@
1
- "use strict";
2
- var Embassy;
3
- (function (Embassy) {
4
- Embassy.DEFAULT_CLOCK_TICK_MS = 30_000;
5
- function useNowMs(tickMs = Embassy.DEFAULT_CLOCK_TICK_MS) {
6
- const [nowMs, setNowMs] = React.useState(() => Date.now());
7
- React.useEffect(() => {
8
- const handle = window.setInterval(() => {
9
- setNowMs(Date.now());
10
- }, tickMs);
11
- return () => {
12
- window.clearInterval(handle);
13
- };
14
- }, [tickMs]);
15
- return nowMs;
16
- }
17
- Embassy.useNowMs = useNowMs;
18
- const absoluteFormatters = new Map();
19
- const relativeFormatters = new Map();
20
- function absoluteFormatter(locale) {
21
- const existing = absoluteFormatters.get(locale);
22
- if (existing !== undefined)
23
- return existing;
24
- const formatter = new Intl.DateTimeFormat(locale, {
25
- month: "short",
26
- day: "numeric",
27
- hour: "2-digit",
28
- minute: "2-digit",
29
- second: "2-digit",
30
- });
31
- absoluteFormatters.set(locale, formatter);
32
- return formatter;
33
- }
34
- function relativeFormatter(locale) {
35
- const existing = relativeFormatters.get(locale);
36
- if (existing !== undefined)
37
- return existing;
38
- const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
39
- relativeFormatters.set(locale, formatter);
40
- return formatter;
41
- }
42
- function fmtAbs(iso, locale) {
43
- const milliseconds = Date.parse(iso);
44
- if (!Number.isFinite(milliseconds))
45
- return iso;
46
- return absoluteFormatter(locale).format(new Date(milliseconds));
47
- }
48
- Embassy.fmtAbs = fmtAbs;
49
- function fmtRel(iso, nowMs, locale) {
50
- const milliseconds = Date.parse(iso);
51
- if (!Number.isFinite(milliseconds))
52
- return iso;
53
- const deltaSeconds = Math.round((milliseconds - nowMs) / 1000);
54
- const magnitude = Math.abs(deltaSeconds);
55
- const formatter = relativeFormatter(locale);
56
- if (magnitude < 60)
57
- return formatter.format(deltaSeconds, "second");
58
- if (magnitude < 3_600) {
59
- return formatter.format(Math.trunc(deltaSeconds / 60), "minute");
60
- }
61
- if (magnitude < 86_400) {
62
- return formatter.format(Math.trunc(deltaSeconds / 3_600), "hour");
63
- }
64
- return formatter.format(Math.trunc(deltaSeconds / 86_400), "day");
65
- }
66
- Embassy.fmtRel = fmtRel;
67
- function fmtAge(ms) {
68
- const totalSeconds = Math.max(0, Math.floor(ms / 1000));
69
- if (totalSeconds < 60)
70
- return `${totalSeconds}s`;
71
- const totalMinutes = Math.floor(totalSeconds / 60);
72
- if (totalMinutes < 60)
73
- return `${totalMinutes}m ${totalSeconds % 60}s`;
74
- const totalHours = Math.floor(totalMinutes / 60);
75
- if (totalHours < 24)
76
- return `${totalHours}h ${totalMinutes % 60}m`;
77
- return `${Math.floor(totalHours / 24)}d ${totalHours % 24}h`;
78
- }
79
- Embassy.fmtAge = fmtAge;
80
- })(Embassy || (Embassy = {}));
81
- var Embassy;
82
- (function (Embassy) {
83
- Embassy.CHIP_KINDS = [
84
- "positive",
85
- "qualified",
86
- "active",
87
- "progress",
88
- "warning",
89
- "indeterminate",
90
- "failure",
91
- "inert",
92
- "unknown",
93
- ];
94
- function tableValue(table, key) {
95
- return Object.prototype.hasOwnProperty.call(table, key)
96
- ? table[key]
97
- : undefined;
98
- }
99
- function presentation(domain, state) {
100
- const table = window.EMBASSY_BOOT.semantics.statePresentation[domain];
101
- return tableValue(table, state)?.chip ?? "unknown";
102
- }
103
- function deliveryOverride(table, state, discriminator) {
104
- if (discriminator === undefined)
105
- return undefined;
106
- const byState = tableValue(table, state);
107
- return byState === undefined
108
- ? undefined
109
- : tableValue(byState, discriminator);
110
- }
111
- function chipKindFor(state, direction, safeErrorCode) {
112
- const semantics = window.EMBASSY_BOOT.semantics;
113
- return (deliveryOverride(semantics.deliveryChipBySafeErrorCode, state, safeErrorCode) ??
114
- deliveryOverride(semantics.deliveryChipByTargetProvider, state, direction === undefined
115
- ? undefined
116
- : Embassy.parseDirection(direction)?.targetProvider) ??
117
- presentation("delivery", state));
118
- }
119
- Embassy.chipKindFor = chipKindFor;
120
- function routeChipKind(state) {
121
- return presentation("route", state);
122
- }
123
- Embassy.routeChipKind = routeChipKind;
124
- function peerChipKind(state) {
125
- return presentation("peer", state);
126
- }
127
- Embassy.peerChipKind = peerChipKind;
128
- function healthChipKind(state) {
129
- return presentation("health", state);
130
- }
131
- Embassy.healthChipKind = healthChipKind;
132
- function overallChipKind(state) {
133
- return presentation("overall", state);
134
- }
135
- Embassy.overallChipKind = overallChipKind;
136
- function partyChipKind(state) {
137
- return presentation("party", state);
138
- }
139
- Embassy.partyChipKind = partyChipKind;
140
- function severityChipKind(state) {
141
- return presentation("severity", state);
142
- }
143
- Embassy.severityChipKind = severityChipKind;
144
- function connectionChipKind(state) {
145
- return presentation("connection", state);
146
- }
147
- Embassy.connectionChipKind = connectionChipKind;
148
- function chipKindByDomain(domain, state, direction) {
149
- return domain === "delivery"
150
- ? chipKindFor(state, direction)
151
- : presentation(domain, state);
152
- }
153
- Embassy.chipKindByDomain = chipKindByDomain;
154
- function camelCaseToken(token) {
155
- return token.replace(/_([a-z])/g, (_match, character) => character.toUpperCase());
156
- }
157
- Embassy.camelCaseToken = camelCaseToken;
158
- function deliveryMeaningKey(state, direction, safeErrorCode) {
159
- const semantics = window.EMBASSY_BOOT.semantics;
160
- if (state === "rejected" && safeErrorCode === "SENDER_NOT_PAIRED") {
161
- return semantics.deliveryMeaningBySafeErrorCode.SENDER_NOT_PAIRED;
162
- }
163
- if (state === "delivered" && direction !== undefined) {
164
- const target = Embassy.parseDirection(direction)?.targetProvider;
165
- if (target !== undefined) {
166
- return semantics.deliveryMeaningByTargetProvider[target];
167
- }
168
- }
169
- if (state === "abandoned" && safeErrorCode !== undefined) {
170
- const byCode = semantics.deliveryMeaningBySafeErrorCode;
171
- return tableValue(byCode, safeErrorCode) ??
172
- semantics.deliveryMeaningKeys.abandoned;
173
- }
174
- const byState = semantics.deliveryMeaningKeys;
175
- return tableValue(byState, state) ?? "activity.meaning.other";
176
- }
177
- Embassy.deliveryMeaningKey = deliveryMeaningKey;
178
- function meaningKeyFor(domain, state, safeErrorCode, direction) {
179
- if (domain === "delivery") {
180
- return deliveryMeaningKey(state, direction, safeErrorCode);
181
- }
182
- switch (domain) {
183
- case "route":
184
- return `route.meaning.${camelCaseToken(state)}`;
185
- case "peer":
186
- return `peer.meaning.${camelCaseToken(state)}`;
187
- case "health":
188
- return `health.meaning.${camelCaseToken(state)}`;
189
- case "overall":
190
- return `overall.${camelCaseToken(state)}`;
191
- case "party":
192
- return `status.${camelCaseToken(state)}`;
193
- case "severity":
194
- return `severity.${camelCaseToken(state)}`;
195
- case "connection":
196
- return `live.connection.${state}`;
197
- }
198
- }
199
- Embassy.meaningKeyFor = meaningKeyFor;
200
- })(Embassy || (Embassy = {}));
201
- var Embassy;
202
- (function (Embassy) {
203
- const PLACEHOLDER_PATTERN = /\{([A-Za-z][A-Za-z0-9]*)\}/g;
204
- function interpolate(template, values = {}) {
205
- return template.replace(PLACEHOLDER_PATTERN, (match, name) => Object.prototype.hasOwnProperty.call(values, name)
206
- ? String(values[name])
207
- : match);
208
- }
209
- Embassy.interpolate = interpolate;
210
- const I18nContext = React.createContext(null);
211
- function I18nProvider(props) {
212
- const { copy, onLocaleChange } = props;
213
- const [locale, setLocaleState] = React.useState(props.initialLocale);
214
- const setLocale = React.useCallback((next) => {
215
- setLocaleState(next);
216
- if (onLocaleChange !== undefined)
217
- onLocaleChange(next);
218
- }, [onLocaleChange]);
219
- const t = React.useCallback((key, values) => {
220
- const template = copy[locale][key] ?? key;
221
- return interpolate(template, values ?? {});
222
- }, [copy, locale]);
223
- const value = React.useMemo(() => ({ locale, setLocale, t }), [locale, setLocale, t]);
224
- return (React.createElement(I18nContext.Provider, { value: value }, props.children));
225
- }
226
- Embassy.I18nProvider = I18nProvider;
227
- function useI18n() {
228
- const value = React.useContext(I18nContext);
229
- if (value === null) {
230
- throw new Error("Embassy.I18nProvider is missing above this component.");
231
- }
232
- return value;
233
- }
234
- function useT() {
235
- return useI18n().t;
236
- }
237
- Embassy.useT = useT;
238
- function useLocale() {
239
- const { locale, setLocale } = useI18n();
240
- return [locale, setLocale];
241
- }
242
- Embassy.useLocale = useLocale;
243
- })(Embassy || (Embassy = {}));
244
- var Embassy;
245
- (function (Embassy) {
246
- Embassy.GATEWAY_PROVIDERS = ["claude", "codex", "deepseek", "grok", "peer"];
247
- function parseDirection(direction) {
248
- const separator = direction.indexOf("_to_");
249
- if (separator < 1 || separator !== direction.lastIndexOf("_to_")) {
250
- return undefined;
251
- }
252
- const sourceProvider = direction.slice(0, separator);
253
- const targetProvider = direction.slice(separator + 4);
254
- const providers = Embassy.GATEWAY_PROVIDERS;
255
- if (sourceProvider === targetProvider || !providers.includes(sourceProvider) ||
256
- !providers.includes(targetProvider))
257
- return undefined;
258
- return {
259
- sourceProvider: sourceProvider,
260
- targetProvider: targetProvider,
261
- };
262
- }
263
- Embassy.parseDirection = parseDirection;
264
- Embassy.TERMINAL_DELIVERY_STATES = [
265
- "delivered",
266
- "unconfirmed",
267
- "failed",
268
- "ambiguous",
269
- "expired",
270
- "cancelled",
271
- "abandoned",
272
- "rejected",
273
- ];
274
- const TERMINAL_DELIVERY_STATE_SET = new Set(Embassy.TERMINAL_DELIVERY_STATES);
275
- Embassy.PULSE_WINDOW_MS = 3_600_000;
276
- const HEALTH_WORST_FIRST = [
277
- "offline",
278
- "degraded",
279
- "connecting",
280
- "healthy",
281
- ];
282
- function compareText(left, right) {
283
- return left < right ? -1 : left > right ? 1 : 0;
284
- }
285
- function isTerminalDeliveryState(state) {
286
- return TERMINAL_DELIVERY_STATE_SET.has(state);
287
- }
288
- Embassy.isTerminalDeliveryState = isTerminalDeliveryState;
289
- function parseTimestampMs(iso) {
290
- if (iso === undefined)
291
- return undefined;
292
- const milliseconds = Date.parse(iso);
293
- return Number.isFinite(milliseconds) ? milliseconds : undefined;
294
- }
295
- Embassy.parseTimestampMs = parseTimestampMs;
296
- function queueSplit(model, targetProvider, nowMs) {
297
- let depth = 0;
298
- let oldestQueuedAt;
299
- let oldestQueuedMs;
300
- for (const route of model.routes) {
301
- if (route.provider !== targetProvider)
302
- continue;
303
- depth += route.queueDepth;
304
- if (route.queueDepth === 0 || route.oldestQueuedAt === undefined) {
305
- continue;
306
- }
307
- const queuedMs = parseTimestampMs(route.oldestQueuedAt);
308
- if (queuedMs === undefined)
309
- continue;
310
- if (oldestQueuedMs === undefined || queuedMs < oldestQueuedMs) {
311
- oldestQueuedMs = queuedMs;
312
- oldestQueuedAt = route.oldestQueuedAt;
313
- }
314
- }
315
- return {
316
- depth,
317
- depthIsLowerBound: model.omissions.routes > 0,
318
- oldestQueuedAt,
319
- oldestAgeMs: oldestQueuedMs === undefined
320
- ? undefined
321
- : Math.max(0, nowMs - oldestQueuedMs),
322
- };
323
- }
324
- Embassy.queueSplit = queueSplit;
325
- function routeOldestAgeMs(route, nowMs) {
326
- if (route.queueDepth === 0 || route.oldestQueuedAt === undefined) {
327
- return undefined;
328
- }
329
- const queuedMs = parseTimestampMs(route.oldestQueuedAt);
330
- return queuedMs === undefined ? undefined : Math.max(0, nowMs - queuedMs);
331
- }
332
- Embassy.routeOldestAgeMs = routeOldestAgeMs;
333
- function worstConnectorHealth(model, provider) {
334
- let worst;
335
- for (const connector of model.connectors) {
336
- if (connector.provider !== provider)
337
- continue;
338
- if (worst === undefined ||
339
- HEALTH_WORST_FIRST.indexOf(connector.health) <
340
- HEALTH_WORST_FIRST.indexOf(worst)) {
341
- worst = connector.health;
342
- }
343
- }
344
- return worst;
345
- }
346
- Embassy.worstConnectorHealth = worstConnectorHealth;
347
- function pulse(model) {
348
- const generatedMs = parseTimestampMs(model.generatedAt);
349
- const cutoffMs = generatedMs === undefined ? undefined : generatedMs - Embassy.PULSE_WINDOW_MS;
350
- const counts = new Map();
351
- for (const group of model.activity) {
352
- if (!isTerminalDeliveryState(group.state))
353
- continue;
354
- if (cutoffMs !== undefined) {
355
- const timestampMs = parseTimestampMs(group.timestamp);
356
- if (timestampMs === undefined || timestampMs < cutoffMs)
357
- continue;
358
- }
359
- counts.set(group.state, (counts.get(group.state) ?? 0) + 1);
360
- }
361
- const bars = Embassy.TERMINAL_DELIVERY_STATES.map((state) => ({ state, count: counts.get(state) ?? 0 }));
362
- return {
363
- bars,
364
- total: bars.reduce((sum, bar) => sum + bar.count, 0),
365
- isLowerBound: model.omissions.messageGroups > 0 ||
366
- model.omissions.upstreamMessageEvents > 0,
367
- };
368
- }
369
- Embassy.pulse = pulse;
370
- function hasLifecycleTruncation(group) {
371
- for (let index = 1; index < group.events.length; index += 1) {
372
- const previous = group.events[index - 1];
373
- const current = group.events[index];
374
- if (previous !== undefined &&
375
- current !== undefined &&
376
- current.sequence > previous.sequence + 1) {
377
- return true;
378
- }
379
- }
380
- return false;
381
- }
382
- Embassy.hasLifecycleTruncation = hasLifecycleTruncation;
383
- function deliveriesTruncated(model) {
384
- return (model.omissions.messageEvents > 0 ||
385
- model.activity.some(hasLifecycleTruncation));
386
- }
387
- Embassy.deliveriesTruncated = deliveriesTruncated;
388
- function deliveryGroupKey(group) {
389
- const suffix = group.messageIdSuffix ?? `seq${group.events[0]?.sequence ?? 0}`;
390
- return `${group.direction}|${group.sourceAlias}|${group.targetAlias}|${suffix}`;
391
- }
392
- Embassy.deliveryGroupKey = deliveryGroupKey;
393
- function guidanceCopyKey(guidance) {
394
- return window.EMBASSY_BOOT.semantics.guidanceCopyKeys[guidance];
395
- }
396
- Embassy.guidanceCopyKey = guidanceCopyKey;
397
- function attentionCommand(item) {
398
- const semantics = window.EMBASSY_BOOT.semantics;
399
- const fallbacks = semantics.attentionCommandFallbacks;
400
- return semantics.attentionCommands[item.guidance].replaceAll("{alias}", item.alias ?? fallbacks[item.guidance] ?? "<alias>");
401
- }
402
- Embassy.attentionCommand = attentionCommand;
403
- function attentionViews(model) {
404
- return model.attention.map((item) => ({
405
- item,
406
- guidanceKey: guidanceCopyKey(item.guidance),
407
- command: attentionCommand(item),
408
- }));
409
- }
410
- Embassy.attentionViews = attentionViews;
411
- function overviewProps(model, nowMs) {
412
- const nonReadyConsentEdgeCount = model.graph.consentEdgeCount - model.graph.readyConsentEdgeCount;
413
- return {
414
- generatedAt: model.generatedAt,
415
- inboundMode: model.inboundMode,
416
- overall: model.overall,
417
- statusStrip: {
418
- broker: model.health,
419
- providers: Embassy.GATEWAY_PROVIDERS.map((provider) => ({ provider, health: worstConnectorHealth(model, provider) })),
420
- },
421
- exchange: model.exchange,
422
- providerQueues: Embassy.GATEWAY_PROVIDERS.map((provider) => ({ provider, queue: queueSplit(model, provider, nowMs) })),
423
- graph: model.graph,
424
- degradedConsentEdgeCopyKey: nonReadyConsentEdgeCount <= 0
425
- ? undefined
426
- : nonReadyConsentEdgeCount === 1 &&
427
- !model.graph.consentEdgeCountIsLowerBound
428
- ? "app.overview.degradedEdge"
429
- : "app.overview.degradedEdges",
430
- attention: attentionViews(model),
431
- attentionOmitted: model.omissions.attentionItems,
432
- pulse: pulse(model),
433
- };
434
- }
435
- Embassy.overviewProps = overviewProps;
436
- function deliveriesGroups(model) {
437
- return model.activity.map((group) => {
438
- const parsed = parseDirection(group.direction);
439
- if (parsed === undefined)
440
- throw new Error("INVALID_MESSAGE_DIRECTION");
441
- return {
442
- key: deliveryGroupKey(group),
443
- group,
444
- routePair: `${group.sourceAlias} → ${group.targetAlias}`,
445
- sourceProvider: parsed.sourceProvider,
446
- targetProvider: parsed.targetProvider,
447
- eventsTruncated: hasLifecycleTruncation(group),
448
- };
449
- });
450
- }
451
- Embassy.deliveriesGroups = deliveriesGroups;
452
- function matchesProviderFilters(view, from, to) {
453
- const parsed = parseDirection(view.group.direction);
454
- return parsed !== undefined && (from === "all" || parsed.sourceProvider === from) &&
455
- (to === "all" || parsed.targetProvider === to);
456
- }
457
- Embassy.matchesProviderFilters = matchesProviderFilters;
458
- function routesProps(model, nowMs) {
459
- const routes = model.routes.map((route) => ({
460
- route,
461
- oldestAgeMs: routeOldestAgeMs(route, nowMs),
462
- }));
463
- return {
464
- inboundMode: model.inboundMode,
465
- peers: model.peers,
466
- peersOmitted: model.omissions.availablePeers,
467
- routes,
468
- routesOmitted: model.omissions.routes,
469
- consentEdges: model.consentEdges,
470
- consentEdgesOmitted: model.omissions.consentEdges,
471
- graph: model.graph,
472
- };
473
- }
474
- Embassy.routesProps = routesProps;
475
- function activityRowSource(row) {
476
- if (row.kind === "delivery")
477
- return row.group.sourceAlias;
478
- if (row.kind === "operation")
479
- return row.event.aliases[0] ?? "";
480
- return row.item.alias ?? row.item.host ?? "";
481
- }
482
- function activityRows(model) {
483
- const rows = [];
484
- for (const group of model.activity) {
485
- if (!isTerminalDeliveryState(group.state))
486
- continue;
487
- rows.push({ kind: "delivery", timestamp: group.timestamp, group });
488
- }
489
- for (const item of model.attention) {
490
- if (item.timestamp === undefined)
491
- continue;
492
- rows.push({
493
- kind: "alert",
494
- timestamp: item.timestamp,
495
- item,
496
- guidanceKey: guidanceCopyKey(item.guidance),
497
- });
498
- }
499
- for (const event of model.brokerActivity ?? []) {
500
- rows.push({ kind: "operation", timestamp: event.timestamp, event });
501
- }
502
- return rows.sort((left, right) => compareText(right.timestamp ?? "", left.timestamp ?? "") ||
503
- compareText(activityRowSource(left), activityRowSource(right)));
504
- }
505
- Embassy.activityRows = activityRows;
506
- function diagnosticsProps(model) {
507
- return {
508
- connectors: model.connectors,
509
- connectorsOmitted: model.omissions.connectors,
510
- expiredCount: model.accounting.expired,
511
- ...(model.deadlinePressure === undefined
512
- ? {}
513
- : { deadlinePressure: model.deadlinePressure }),
514
- queuedMessages: model.exchange.queuedMessages,
515
- queueCountIsLowerBound: model.exchange.queueCountIsLowerBound,
516
- accounting: model.accounting,
517
- omissions: model.omissions,
518
- };
519
- }
520
- Embassy.diagnosticsProps = diagnosticsProps;
521
- Embassy.adapter = {
522
- overviewProps,
523
- deliveriesGroups,
524
- matchesProviderFilters,
525
- routesProps,
526
- activityRows,
527
- diagnosticsProps,
528
- queueSplit,
529
- routeOldestAgeMs,
530
- pulse,
531
- worstConnectorHealth,
532
- hasLifecycleTruncation,
533
- deliveriesTruncated,
534
- deliveryGroupKey,
535
- guidanceCopyKey,
536
- attentionCommand,
537
- attentionViews,
538
- isTerminalDeliveryState,
539
- parseTimestampMs,
540
- };
541
- })(Embassy || (Embassy = {}));
542
- var Embassy;
543
- (function (Embassy) {
544
- const HEARTBEAT_WATCHDOG_MS = 35_000;
545
- const AUTO_RECONNECT_DELAY_MS = 5_000;
546
- const MAX_AUTO_RECONNECT_ATTEMPTS = 3;
547
- const MAX_STREAM_BUFFER_LENGTH = 1_048_576;
548
- function createProtocol(options) {
549
- const base = location.pathname.endsWith("/")
550
- ? location.pathname.slice(0, -1)
551
- : location.pathname;
552
- const api = (name) => base + "/" + name;
553
- let connectionState = "connecting";
554
- let controller;
555
- let watchdogTimer;
556
- let reconnectTimer;
557
- let reconnectAttempts = 0;
558
- let lastStreamRevision;
559
- let gapRecoveryInFlight = false;
560
- function apiFetch(name, fetchOptions = {}) {
561
- const init = {
562
- method: "POST",
563
- credentials: "omit",
564
- cache: "no-store",
565
- headers: { "X-Embassy-Request": "1", ...(fetchOptions.headers ?? {}) },
566
- };
567
- if (fetchOptions.body !== undefined) {
568
- init.body = fetchOptions.body;
569
- }
570
- if (fetchOptions.signal !== undefined) {
571
- init.signal = fetchOptions.signal;
572
- }
573
- return fetch(api(name), init);
574
- }
575
- function notify(kind) {
576
- options.onNotice?.(kind);
577
- }
578
- function setConnectionState(next) {
579
- connectionState = next;
580
- if (next === "connected") {
581
- reconnectAttempts = 0;
582
- }
583
- options.onConnectionState(next);
584
- if (next === "disconnected") {
585
- scheduleAutoReconnect();
586
- }
587
- }
588
- function clearWatchdog() {
589
- if (watchdogTimer !== undefined) {
590
- window.clearTimeout(watchdogTimer);
591
- watchdogTimer = undefined;
592
- }
593
- }
594
- function armWatchdog(current) {
595
- clearWatchdog();
596
- watchdogTimer = window.setTimeout(() => {
597
- watchdogTimer = undefined;
598
- if (connectionState !== "connected" || controller !== current) {
599
- return;
600
- }
601
- current.abort();
602
- setConnectionState("disconnected");
603
- }, HEARTBEAT_WATCHDOG_MS);
604
- }
605
- function clearReconnectTimer() {
606
- if (reconnectTimer !== undefined) {
607
- window.clearTimeout(reconnectTimer);
608
- reconnectTimer = undefined;
609
- }
610
- }
611
- function scheduleAutoReconnect() {
612
- if (reconnectTimer !== undefined || reconnectAttempts >= MAX_AUTO_RECONNECT_ATTEMPTS) {
613
- return;
614
- }
615
- reconnectTimer = window.setTimeout(() => {
616
- reconnectTimer = undefined;
617
- if (connectionState !== "disconnected") {
618
- return;
619
- }
620
- reconnectAttempts += 1;
621
- void connect();
622
- }, AUTO_RECONNECT_DELAY_MS);
623
- }
624
- function ingest(event, source) {
625
- const previous = lastStreamRevision;
626
- const gap = previous !== undefined && event.streamRevision > previous + 1;
627
- lastStreamRevision = event.streamRevision;
628
- options.onEvent(event);
629
- if (event.reset) {
630
- notify("reset");
631
- }
632
- if (gap) {
633
- notify("missedFrames");
634
- if (source === "stream" && !gapRecoveryInFlight) {
635
- gapRecoveryInFlight = true;
636
- void fetchSnapshotNow().finally(() => {
637
- gapRecoveryInFlight = false;
638
- });
639
- }
640
- }
641
- }
642
- function consumeSseBlock(block) {
643
- let event = "message";
644
- const data = [];
645
- for (const line of block.split("\n")) {
646
- if (line.startsWith("event:")) {
647
- event = line.slice(6).trim();
648
- }
649
- if (line.startsWith("data:")) {
650
- data.push(line.slice(5).trimStart());
651
- }
652
- }
653
- if (event === "snapshot" && data.length > 0) {
654
- const parsed = JSON.parse(data.join("\n"));
655
- setConnectionState("connected");
656
- ingest(parsed, "stream");
657
- }
658
- else if (event === "observer_unavailable") {
659
- setConnectionState("unavailable");
660
- }
661
- else if (event === "shutdown") {
662
- setConnectionState("stopped");
663
- }
664
- }
665
- async function connect() {
666
- if (controller) {
667
- controller.abort();
668
- }
669
- const current = new AbortController();
670
- controller = current;
671
- setConnectionState("connecting");
672
- try {
673
- const response = await apiFetch("stream", { signal: current.signal });
674
- if (response.status === 429) {
675
- setConnectionState("capacity");
676
- return;
677
- }
678
- if (!response.ok || !response.body) {
679
- throw new Error("stream");
680
- }
681
- const reader = response.body.getReader();
682
- const decoder = new TextDecoder();
683
- let buffer = "";
684
- armWatchdog(current);
685
- while (true) {
686
- const result = await reader.read();
687
- if (result.done) {
688
- break;
689
- }
690
- armWatchdog(current);
691
- buffer += decoder.decode(result.value, { stream: true }).replaceAll("\r\n", "\n");
692
- if (buffer.length > MAX_STREAM_BUFFER_LENGTH) {
693
- throw new Error("frame");
694
- }
695
- let boundary;
696
- while ((boundary = buffer.indexOf("\n\n")) >= 0) {
697
- consumeSseBlock(buffer.slice(0, boundary));
698
- buffer = buffer.slice(boundary + 2);
699
- }
700
- }
701
- if (!current.signal.aborted && connectionState !== "stopped") {
702
- setConnectionState("disconnected");
703
- }
704
- }
705
- catch {
706
- if (!current.signal.aborted) {
707
- setConnectionState("disconnected");
708
- }
709
- }
710
- finally {
711
- clearWatchdog();
712
- }
713
- }
714
- async function fetchSnapshotNow() {
715
- try {
716
- const response = await apiFetch("snapshot");
717
- if (response.ok) {
718
- const event = (await response.json());
719
- ingest(event, "snapshot");
720
- }
721
- else {
722
- setConnectionState(connectionState === "connected" ? "unavailable" : "disconnected");
723
- }
724
- }
725
- catch {
726
- setConnectionState(connectionState === "connected" ? "unavailable" : "disconnected");
727
- }
728
- }
729
- function isActionResult(value) {
730
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
731
- return false;
732
- }
733
- const record = value;
734
- return (Object.keys(record).length === 2 &&
735
- typeof record.ok === "boolean" &&
736
- typeof record.code === "string" &&
737
- /^(?:ok|not_found|conflict|watch_owner_conflict|route_mismatch|busy|unavailable|rejected|rate_limited)$/u.test(record.code) &&
738
- ((record.ok && record.code === "ok") ||
739
- (!record.ok && record.code !== "ok")));
740
- }
741
- async function executeAction(action) {
742
- let result = {
743
- ok: false,
744
- code: "unavailable",
745
- };
746
- try {
747
- const response = await apiFetch("action", {
748
- headers: { "Content-Type": "application/json" },
749
- body: JSON.stringify(action),
750
- });
751
- const parsed = await response.json();
752
- if (isActionResult(parsed))
753
- result = parsed;
754
- }
755
- catch {
756
- }
757
- await fetchSnapshotNow();
758
- return result;
759
- }
760
- async function start() {
761
- await connect();
762
- }
763
- return {
764
- start: () => {
765
- void start();
766
- },
767
- pause: () => {
768
- clearReconnectTimer();
769
- if (controller) {
770
- controller.abort();
771
- }
772
- setConnectionState("paused");
773
- },
774
- reconnect: () => {
775
- clearReconnectTimer();
776
- reconnectAttempts = 0;
777
- void connect();
778
- },
779
- readNow: () => {
780
- void fetchSnapshotNow();
781
- },
782
- executeAction,
783
- };
784
- }
785
- Embassy.createProtocol = createProtocol;
786
- })(Embassy || (Embassy = {}));
787
- var Embassy;
788
- (function (Embassy) {
789
- function StateChip(props) {
790
- const t = Embassy.useT();
791
- const domain = props.domain ?? "delivery";
792
- const kind = domain === "delivery"
793
- ? Embassy.chipKindFor(props.state, props.direction, props.safeErrorCode)
794
- : Embassy.chipKindByDomain(domain, props.state, props.direction);
795
- const meaning = props.note ??
796
- t(Embassy.meaningKeyFor(domain, props.state, props.safeErrorCode, props.direction));
797
- return (React.createElement("span", { className: props.small === true ? "chip chip--small" : "chip", "data-kind": kind, title: meaning, "aria-description": meaning }, props.label ?? props.state));
798
- }
799
- Embassy.StateChip = StateChip;
800
- function MonoLabel(props) {
801
- const className = props.className === undefined
802
- ? "mono-label"
803
- : `mono-label ${props.className}`;
804
- return React.createElement("div", { className: className }, props.children);
805
- }
806
- Embassy.MonoLabel = MonoLabel;
807
- function CopyCmd(props) {
808
- const t = Embassy.useT();
809
- const [feedback, setFeedback] = React.useState("idle");
810
- React.useEffect(() => {
811
- if (feedback === "idle")
812
- return undefined;
813
- const handle = window.setTimeout(() => {
814
- setFeedback("idle");
815
- }, 1_500);
816
- return () => {
817
- window.clearTimeout(handle);
818
- };
819
- }, [feedback]);
820
- const copy = () => {
821
- const clipboard = navigator.clipboard;
822
- if (clipboard === undefined) {
823
- setFeedback("failed");
824
- return;
825
- }
826
- clipboard.writeText(props.cmd).then(() => {
827
- setFeedback("copied");
828
- }, () => {
829
- setFeedback("failed");
830
- });
831
- };
832
- const status = feedback === "copied"
833
- ? t("app.copied")
834
- : feedback === "failed"
835
- ? t("app.copyFailed")
836
- : "";
837
- return (React.createElement("div", { className: "copy-cmd" },
838
- React.createElement("code", { className: "copy-cmd__code" }, props.cmd),
839
- React.createElement("span", { className: "copy-cmd__status", "data-tone": feedback === "failed" ? "error" : undefined, "aria-live": "polite" }, status),
840
- React.createElement("button", { type: "button", className: "copy-cmd__button", onClick: copy }, t("app.copy"))));
841
- }
842
- Embassy.CopyCmd = CopyCmd;
843
- function Rule(props) {
844
- const className = props.className === undefined ? "rule" : `rule ${props.className}`;
845
- return React.createElement("hr", { className: className });
846
- }
847
- Embassy.Rule = Rule;
848
- function FilterPill(props) {
849
- return (React.createElement("button", { type: "button", className: "pill", "aria-pressed": props.active, onClick: props.onClick }, props.children));
850
- }
851
- Embassy.FilterPill = FilterPill;
852
- function TimeAgo(props) {
853
- const t = Embassy.useT();
854
- const [locale] = Embassy.useLocale();
855
- const nowMs = Embassy.useNowMs();
856
- const absolute = Embassy.fmtAbs(props.iso, locale);
857
- const parsedMs = Date.parse(props.iso);
858
- const ageMs = Number.isFinite(parsedMs) ? nowMs - parsedMs : undefined;
859
- const relative = ageMs !== undefined && ageMs >= 3_600_000
860
- ? t("time.ago", { duration: Embassy.fmtAge(ageMs) })
861
- : Embassy.fmtRel(props.iso, nowMs, locale);
862
- return (React.createElement("span", { className: "time-ago", title: absolute },
863
- relative,
864
- React.createElement("span", { className: "sr-only" },
865
- " (",
866
- absolute,
867
- ")")));
868
- }
869
- Embassy.TimeAgo = TimeAgo;
870
- function Tooltip(props) {
871
- const descriptionId = React.useId();
872
- return (React.createElement("span", { className: "tooltip", tabIndex: 0, "data-tip": props.tip, "data-tip-wrap": props.wrap === true ? "" : undefined, "aria-describedby": descriptionId },
873
- props.children ?? React.createElement("span", { className: "info-dot" }, "i"),
874
- React.createElement("span", { className: "sr-only", id: descriptionId }, props.tip)));
875
- }
876
- Embassy.Tooltip = Tooltip;
877
- function AbsentFeature(props) {
878
- const t = Embassy.useT();
879
- return (React.createElement("section", { className: "absent-feature" },
880
- React.createElement(MonoLabel, null, t("app.notLanded.title")),
881
- React.createElement("h3", { className: "absent-feature__title" }, props.title),
882
- React.createElement("p", { className: "absent-feature__body" }, props.body),
883
- React.createElement("p", { className: "absent-feature__note" }, t("app.notLanded.body")),
884
- props.cmd === undefined ? null : React.createElement(CopyCmd, { cmd: props.cmd })));
885
- }
886
- Embassy.AbsentFeature = AbsentFeature;
887
- })(Embassy || (Embassy = {}));
888
- var Embassy;
889
- (function (Embassy) {
890
- const PULSE_BAR_BASE_PX = 6;
891
- const PULSE_BAR_RANGE_PX = 38;
892
- const QUEUE_DEPTH_COMMAND = "EMBASSY_MAX_QUEUE_MESSAGES=<n> embassy serve";
893
- function LowerBound(props) {
894
- const t = Embassy.useT();
895
- const sentence = t("app.lowerBound", { count: props.count });
896
- return (React.createElement("span", { className: "lower-bound", title: sentence },
897
- React.createElement("span", { "aria-hidden": "true" }, t("count.atLeast", { count: props.count })),
898
- React.createElement("span", { className: "sr-only" }, sentence)));
899
- }
900
- function StatusCard(props) {
901
- return (React.createElement("div", { className: "status-card" },
902
- React.createElement("div", { className: "status-card__head" },
903
- React.createElement("span", { className: "status-card__label" }, props.label),
904
- props.chip),
905
- props.detail === undefined ? null : (React.createElement("span", { className: "status-card__detail" }, props.detail))));
906
- }
907
- function HealthStatusCard(props) {
908
- const t = Embassy.useT();
909
- const health = props.health;
910
- if (health === undefined) {
911
- return (React.createElement(StatusCard, { label: props.label, detail: undefined, chip: React.createElement("span", { className: "chip chip--small", "data-kind": "inert" }, t("app.overview.connectorMissing")) }));
912
- }
913
- return (React.createElement(StatusCard, { label: props.label, detail: t(`health.meaning.${Embassy.camelCaseToken(health)}`), chip: React.createElement(Embassy.StateChip, { domain: "health", state: health, small: true }) }));
914
- }
915
- function ExchangeNode(props) {
916
- const party = props.party;
917
- return (React.createElement("div", { className: "exchange-node" },
918
- React.createElement("div", null,
919
- React.createElement("div", { className: "exchange-node__title" }, props.title),
920
- React.createElement("div", { className: "exchange-node__sub" }, props.sub)),
921
- React.createElement("div", { className: "chip-row" },
922
- React.createElement(Embassy.StateChip, { domain: "party", state: party.status, small: true }),
923
- party.countIsLowerBound ? React.createElement(LowerBound, { count: party.total }) : null),
924
- React.createElement("div", { className: "exchange-node__line" }, props.countLine),
925
- party.primaryAlias === undefined ? null : (React.createElement("div", { className: "exchange-node__line mono" }, party.primaryAlias))));
926
- }
927
- function liveQueueAgeMs(queue, nowMs) {
928
- const queuedMs = Embassy.parseTimestampMs(queue.oldestQueuedAt);
929
- if (queuedMs === undefined)
930
- return queue.oldestAgeMs;
931
- return Math.max(0, nowMs - queuedMs);
932
- }
933
- function ExchangeEdge(props) {
934
- const t = Embassy.useT();
935
- const nowMs = Embassy.useNowMs();
936
- const queue = props.queue;
937
- const inbound = props.direction === "in";
938
- const ageMs = liveQueueAgeMs(queue, nowMs);
939
- const oldest = queue.depth > 0 && ageMs !== undefined
940
- ? ` · ${t("app.overview.oldest")} ${Embassy.fmtAge(ageMs)}`
941
- : "";
942
- return (React.createElement("div", { className: "exchange-edge" },
943
- React.createElement("div", { className: "edge-label" }, props.label),
944
- React.createElement("div", { className: "edge-line" },
945
- React.createElement("span", { className: inbound ? "edge-arrow edge-arrow--in" : "edge-arrow edge-arrow--out", "aria-hidden": "true" }, inbound ? "←" : "→")),
946
- React.createElement("div", { className: "edge-queue", "data-active": queue.depth > 0 ? "true" : "false" }, `${t("app.overview.depth")} ${queue.depth}${oldest}`),
947
- queue.depthIsLowerBound ? React.createElement(LowerBound, { count: queue.depth }) : null));
948
- }
949
- function attentionScopeParts(item, t) {
950
- const parts = [];
951
- if (item.provider !== undefined) {
952
- parts.push(t(`provider.${item.provider}`));
953
- }
954
- if (item.alias !== undefined)
955
- parts.push(item.alias);
956
- if (item.host !== undefined)
957
- parts.push(item.host);
958
- if (item.code !== undefined)
959
- parts.push(item.code);
960
- return parts;
961
- }
962
- function attentionQueueDepthLine(item, t, locale) {
963
- return item.queueDepth === undefined
964
- ? undefined
965
- : `${t("app.routes.queueDepth")}: ${item.queueDepth.toLocaleString(locale)}`;
966
- }
967
- Embassy.attentionQueueDepthLine = attentionQueueDepthLine;
968
- function AttentionItem(props) {
969
- const t = Embassy.useT();
970
- const [locale] = Embassy.useLocale();
971
- const { item, guidanceKey, command } = props.view;
972
- const scope = attentionScopeParts(item, t).join(" · ");
973
- const queueDepthLine = attentionQueueDepthLine(item, t, locale);
974
- return (React.createElement("div", { className: "attention-item" },
975
- React.createElement("div", { className: "attention-item__head" },
976
- React.createElement(Embassy.StateChip, { domain: "severity", state: item.severity, small: true }),
977
- React.createElement("h3", { className: "attention-item__title" }, t(`guidance.${guidanceKey}.title`)),
978
- item.timestamp === undefined ? null : (React.createElement(Embassy.TimeAgo, { iso: item.timestamp }))),
979
- React.createElement("p", { className: "attention-item__body" }, t(`guidance.${guidanceKey}.body`)),
980
- React.createElement("p", { className: "attention-item__body" }, t(`guidance.${guidanceKey}.action`, {
981
- alias: item.alias ?? "<alias>",
982
- })),
983
- queueDepthLine === undefined ? null : (React.createElement("p", { className: "attention-item__body" }, queueDepthLine)),
984
- scope === "" ? null : (React.createElement("div", { className: "attention-item__scope" }, `${t("attention.scope")}: ${scope}`)),
985
- React.createElement("div", { className: "attention-item__action" },
986
- React.createElement(Embassy.CopyCmd, { cmd: command })),
987
- item.guidance === "queue_stalled" ? (React.createElement(Embassy.AbsentFeature, { title: t("app.diag.queue.title"), body: t("app.diag.editable.note"), cmd: QUEUE_DEPTH_COMMAND })) : null));
988
- }
989
- function PulseBarButton(props) {
990
- const t = Embassy.useT();
991
- const { bar } = props;
992
- const viewIn = t("app.overview.viewIn");
993
- const heightPx = PULSE_BAR_BASE_PX + (PULSE_BAR_RANGE_PX * bar.count) / props.maxCount;
994
- return (React.createElement("button", { type: "button", className: "pulse-bar-btn", title: viewIn, "aria-label": `${bar.state}: ${bar.count} — ${viewIn}`, onClick: () => {
995
- props.onSelect(bar.state);
996
- } },
997
- React.createElement("span", { className: "pulse-count" }, bar.count),
998
- React.createElement("span", { className: "pulse-bar", "data-kind": Embassy.chipKindFor(bar.state), "data-empty": bar.count === 0 ? "true" : "false", style: { height: `${heightPx}px` } }),
999
- React.createElement(Embassy.StateChip, { state: bar.state, small: true })));
1000
- }
1001
- function OverviewTab(props) {
1002
- const t = Embassy.useT();
1003
- const { data, onViewDeliveries } = props;
1004
- const openInbound = data.inboundMode === "open";
1005
- const hasConsentEdge = data.graph.consentEdgeCount > 0 ||
1006
- data.graph.consentEdgeCountIsLowerBound;
1007
- const showAttention = data.attention.length > 0 || data.attentionOmitted > 0;
1008
- const maxPulse = data.pulse.bars.reduce((largest, bar) => Math.max(largest, bar.count), 1);
1009
- const selectPulseState = React.useCallback((state) => {
1010
- onViewDeliveries({ state });
1011
- }, [onViewDeliveries]);
1012
- const idPrefix = React.useId();
1013
- const stripId = `${idPrefix}-strip`;
1014
- const exchangeId = `${idPrefix}-exchange`;
1015
- const attentionId = `${idPrefix}-attention`;
1016
- const pulseId = `${idPrefix}-pulse`;
1017
- return (React.createElement("div", { className: "tab-panel" },
1018
- React.createElement("section", { className: "section", "aria-labelledby": stripId },
1019
- React.createElement("h2", { className: "mono-label section-label", id: stripId }, t("app.overview.statusStrip")),
1020
- React.createElement("div", { className: "status-strip" },
1021
- React.createElement(HealthStatusCard, { label: t("app.overview.broker"), health: data.statusStrip.broker }),
1022
- data.statusStrip.providers.map(({ provider, health }) => (React.createElement(HealthStatusCard, { key: provider, label: t(`provider.${provider}`), health: health }))))),
1023
- React.createElement("section", { className: "section", "aria-labelledby": exchangeId },
1024
- React.createElement("div", { className: "row section-label section-head" },
1025
- React.createElement("h2", { className: "mono-label", id: exchangeId }, t("exchange.eyebrow")),
1026
- React.createElement(Embassy.Tooltip, { tip: t("exchange.note"), wrap: true },
1027
- React.createElement("span", { className: "info-dot" }, "i"))),
1028
- React.createElement("div", { className: "inbound-policy", "data-inbound-mode": data.inboundMode },
1029
- React.createElement("span", { className: "chip chip--small", "data-kind": openInbound ? "warning" : "inert" }, t(openInbound
1030
- ? "inbound.open.badge"
1031
- : "inbound.paired.badge")),
1032
- React.createElement("p", null, t(openInbound
1033
- ? "inbound.open.body"
1034
- : hasConsentEdge
1035
- ? "inbound.paired.body"
1036
- : "inbound.noPair.body"))),
1037
- React.createElement("div", { className: "exchange-board" }, data.exchange.parties.map((party) => (React.createElement(ExchangeNode, { key: party.kind, title: t(`provider.${party.kind}`), sub: party.kind, party: party, countLine: `${party.ready}/${party.total}` })))),
1038
- React.createElement("div", { className: "exchange-edges" }, data.providerQueues.map(({ provider, queue }) => (React.createElement(ExchangeEdge, { key: provider, direction: "in", label: t(`provider.${provider}`), queue: queue })))),
1039
- data.degradedConsentEdgeCopyKey === undefined ? null : (React.createElement("p", { className: "text-xs text-body-muted" }, t(data.degradedConsentEdgeCopyKey)))),
1040
- showAttention ? (React.createElement("section", { className: "section", "aria-labelledby": attentionId },
1041
- React.createElement("div", { className: "row section-label section-head" },
1042
- React.createElement("h2", { className: "mono-label", id: attentionId }, t("attention.eyebrow")),
1043
- data.attentionOmitted > 0 ? (React.createElement(LowerBound, { count: data.attention.length })) : null),
1044
- React.createElement("div", { className: "attention-list" }, data.attention.map((view, index) => (React.createElement(AttentionItem, { key: `${view.item.kind}|${view.item.guidance}|${view.item.alias ?? ""}|${view.item.host ?? ""}|${index}`, view: view })))),
1045
- data.attentionOmitted > 0 ? (React.createElement("p", { className: "footnote" }, t("attention.projectionOnly"))) : null)) : null,
1046
- React.createElement("section", { className: "section", "aria-labelledby": pulseId },
1047
- React.createElement("h2", { className: "mono-label section-label", id: pulseId }, t("app.overview.pulse.title")),
1048
- data.pulse.total === 0 ? (React.createElement("p", { className: "pulse-empty" }, t("app.overview.pulse.empty"))) : null,
1049
- React.createElement("div", { className: "pulse" }, data.pulse.bars.map((bar) => (React.createElement(PulseBarButton, { key: bar.state, bar: bar, maxCount: maxPulse, onSelect: selectPulseState })))),
1050
- React.createElement("p", { className: "pulse-caption" },
1051
- t("app.overview.pulse.caption"),
1052
- data.pulse.isLowerBound ? " " : null,
1053
- data.pulse.isLowerBound ? (React.createElement(LowerBound, { count: data.pulse.total })) : null))));
1054
- }
1055
- Embassy.OverviewTab = OverviewTab;
1056
- })(Embassy || (Embassy = {}));
1057
- var Embassy;
1058
- (function (Embassy) {
1059
- const DELIVERY_STATE_FILTERS = [
1060
- "queued",
1061
- "dispatching",
1062
- "transport_written",
1063
- "held",
1064
- "duplicate",
1065
- "delivered",
1066
- "unconfirmed",
1067
- "ambiguous",
1068
- "failed",
1069
- "expired",
1070
- "rejected",
1071
- "cancelled",
1072
- "abandoned",
1073
- ];
1074
- const DELIVERY_STATE_FILTERS_COVER_THE_UNION = true;
1075
- const SEND_TO_CODEX_CMD = "embassy send --from <alias> --to <alias>";
1076
- const countFormatters = new Map();
1077
- function fmtCount(locale, value) {
1078
- const existing = countFormatters.get(locale);
1079
- if (existing !== undefined)
1080
- return existing.format(value);
1081
- const formatter = new Intl.NumberFormat(locale);
1082
- countFormatters.set(locale, formatter);
1083
- return formatter.format(value);
1084
- }
1085
- function includesFold(haystack, needleLower) {
1086
- return haystack !== undefined && haystack.toLowerCase().includes(needleLower);
1087
- }
1088
- function equalsFold(value, wantedLower) {
1089
- return value !== undefined && value.toLowerCase() === wantedLower;
1090
- }
1091
- function matchesQuery(view, needleLower) {
1092
- if (needleLower === "")
1093
- return true;
1094
- const group = view.group;
1095
- return (includesFold(group.messageIdSuffix, needleLower) ||
1096
- includesFold(group.conversationIdSuffix, needleLower) ||
1097
- includesFold(group.sourceAlias, needleLower) ||
1098
- includesFold(group.targetAlias, needleLower) ||
1099
- includesFold(group.safeErrorCode, needleLower) ||
1100
- group.events.some((event) => includesFold(event.safeErrorCode, needleLower)));
1101
- }
1102
- function findPresetTarget(groups, token) {
1103
- const wanted = token.trim().toLowerCase();
1104
- if (wanted === "")
1105
- return undefined;
1106
- const bySuffix = groups.find((view) => equalsFold(view.group.messageIdSuffix, wanted) ||
1107
- equalsFold(view.group.conversationIdSuffix, wanted));
1108
- if (bySuffix !== undefined)
1109
- return bySuffix;
1110
- return groups.find((view) => equalsFold(view.group.safeErrorCode, wanted) ||
1111
- view.group.events.some((event) => equalsFold(event.safeErrorCode, wanted)));
1112
- }
1113
- function groupByRoutePair(views) {
1114
- const order = [];
1115
- const buckets = new Map();
1116
- for (const view of views) {
1117
- const bucket = buckets.get(view.routePair);
1118
- if (bucket === undefined) {
1119
- buckets.set(view.routePair, [view]);
1120
- order.push(view.routePair);
1121
- }
1122
- else {
1123
- bucket.push(view);
1124
- }
1125
- }
1126
- return order.map((pair) => ({ pair, rows: buckets.get(pair) ?? [] }));
1127
- }
1128
- function droppedEventCount(group) {
1129
- let dropped = 0;
1130
- for (let index = 1; index < group.events.length; index += 1) {
1131
- const previous = group.events[index - 1];
1132
- const current = group.events[index];
1133
- if (previous === undefined || current === undefined)
1134
- continue;
1135
- const gap = current.sequence - previous.sequence - 1;
1136
- if (gap > 0)
1137
- dropped += gap;
1138
- }
1139
- return dropped;
1140
- }
1141
- function DetailRow(props) {
1142
- return (React.createElement("div", { className: "detail-row" },
1143
- React.createElement("span", { className: "detail-label" }, props.label),
1144
- " ",
1145
- React.createElement("span", { className: props.mono === true
1146
- ? "detail-value detail-value--mono"
1147
- : "detail-value" }, props.children)));
1148
- }
1149
- function DeliveryLifecycle(props) {
1150
- const t = Embassy.useT();
1151
- const [locale] = Embassy.useLocale();
1152
- const events = props.group.events;
1153
- return (React.createElement("div", { className: "lifecycle" }, events.map((event, index) => {
1154
- const isLast = index === events.length - 1;
1155
- const kind = Embassy.chipKindFor(event.state, props.group.direction, event.safeErrorCode);
1156
- const noteParts = [];
1157
- if (event.safeErrorCode !== undefined) {
1158
- noteParts.push(event.safeErrorCode);
1159
- }
1160
- if (event.latencyMs !== undefined) {
1161
- noteParts.push(`${fmtCount(locale, event.latencyMs)} ms`);
1162
- }
1163
- return (React.createElement("div", { className: "lifecycle-item", key: event.sequence },
1164
- React.createElement("div", { className: "lifecycle-rail", "aria-hidden": true },
1165
- React.createElement("span", { className: "lifecycle-dot", "data-kind": isLast ? kind : undefined }),
1166
- isLast ? null : React.createElement("span", { className: "lifecycle-connector" })),
1167
- React.createElement("div", { className: "lifecycle-body" },
1168
- React.createElement("div", { className: "lifecycle-head" },
1169
- React.createElement(Embassy.StateChip, { small: true, state: event.state, direction: props.group.direction, safeErrorCode: event.safeErrorCode }),
1170
- event.timestamp === undefined ? (React.createElement("span", { className: "lifecycle-abs" }, t("time.unavailable"))) : (React.createElement(React.Fragment, null,
1171
- React.createElement(Embassy.TimeAgo, { iso: event.timestamp }),
1172
- React.createElement("span", { className: "lifecycle-abs" }, Embassy.fmtAbs(event.timestamp, locale))))),
1173
- noteParts.length === 0 ? null : (React.createElement("div", { className: "lifecycle-note" }, noteParts.join(" · "))),
1174
- React.createElement("div", { className: "lifecycle-meaning" }, t(Embassy.deliveryMeaningKey(event.state, props.group.direction, event.safeErrorCode))))));
1175
- })));
1176
- }
1177
- function DeliveryDetail(props) {
1178
- const t = Embassy.useT();
1179
- const [locale] = Embassy.useLocale();
1180
- const [rawOpen, setRawOpen] = React.useState(false);
1181
- const rawId = React.useId();
1182
- const group = props.view.group;
1183
- const dropped = droppedEventCount(group);
1184
- const frames = group.events.filter((event) => event.safeErrorCode !== undefined);
1185
- const earliest = group.events[0]?.timestamp;
1186
- return (React.createElement("div", { className: "detail-pane" },
1187
- React.createElement("div", { className: "detail-pane__head" },
1188
- React.createElement("span", { className: "detail-pane__id" },
1189
- group.messageIdSuffix ?? "—",
1190
- group.conversationIdSuffix === undefined
1191
- ? ""
1192
- : ` · conv …${group.conversationIdSuffix}`,
1193
- group.steer === true ? " · STEER" : ""),
1194
- React.createElement("div", { className: "detail-pane__sub" },
1195
- props.view.routePair,
1196
- " \u00B7 ",
1197
- t(`provider.${props.view.sourceProvider}`),
1198
- " \u2192 ",
1199
- t(`provider.${props.view.targetProvider}`))),
1200
- React.createElement("div", null,
1201
- React.createElement(Embassy.MonoLabel, { className: "section-label section-label--lifecycle" }, t("app.deliveries.lifecycle")),
1202
- React.createElement(DeliveryLifecycle, { group: group }),
1203
- props.view.eventsTruncated ? (React.createElement("p", { className: "truncation-note" }, t("app.deliveries.eventsTruncated", {
1204
- count: fmtCount(locale, dropped),
1205
- }))) : null),
1206
- React.createElement("div", null,
1207
- React.createElement(Embassy.MonoLabel, { className: "section-label section-label--body" }, t("app.deliveries.bodyLabel")),
1208
- group.body === undefined ? (React.createElement("p", { className: "detail-pane__sub" }, t("app.deliveries.bodiesNote"))) : (React.createElement("pre", { className: "detail-pane__message-body" }, group.body))),
1209
- React.createElement("div", null,
1210
- React.createElement(Embassy.MonoLabel, { className: "section-label section-label--frames" }, t("app.deliveries.frames")),
1211
- frames.length === 0 ? (React.createElement("p", { className: "detail-pane__sub" }, t("app.deliveries.noFrames"))) : (React.createElement("div", { className: "frames-list" }, frames.map((event) => (React.createElement("div", { className: "frame-item", key: event.sequence },
1212
- React.createElement("span", { className: "frame-code" }, event.safeErrorCode),
1213
- event.timestamp === undefined ? (React.createElement("span", { className: "lifecycle-abs" }, t("time.unavailable"))) : (React.createElement(Embassy.TimeAgo, { iso: event.timestamp })))))))),
1214
- React.createElement("div", { className: "detail-list" },
1215
- group.latencyMs === undefined ? null : (React.createElement(DetailRow, { label: t("activity.column.elapsed"), mono: true }, `${fmtCount(locale, group.latencyMs)} ms`)),
1216
- React.createElement(DetailRow, { label: t("activity.column.size"), mono: true }, `${fmtCount(locale, group.bytes)} B`),
1217
- React.createElement(DetailRow, { label: t("app.deliveries.earliestRetained") }, earliest === undefined ? (t("time.unavailable")) : (React.createElement(React.Fragment, null,
1218
- React.createElement(Embassy.TimeAgo, { iso: earliest }),
1219
- " \u00B7 ",
1220
- Embassy.fmtAbs(earliest, locale)))),
1221
- group.safeErrorCode === undefined ? null : (React.createElement(DetailRow, { label: t("column.issue"), mono: true }, group.safeErrorCode))),
1222
- React.createElement("div", null,
1223
- React.createElement("button", { type: "button", className: "toggle-link", "aria-expanded": rawOpen, "aria-controls": rawOpen ? rawId : undefined, onClick: () => {
1224
- setRawOpen(!rawOpen);
1225
- } }, rawOpen ? t("app.deliveries.hideRaw") : t("app.deliveries.raw")),
1226
- rawOpen ? (React.createElement("pre", { className: "raw-json", id: rawId, tabIndex: 0 }, JSON.stringify(group, null, 2))) : null)));
1227
- }
1228
- function DeliveriesEmpty(props) {
1229
- return (React.createElement("div", { className: "empty-state" },
1230
- React.createElement("p", { className: "empty-state__text" }, props.text),
1231
- React.createElement(Embassy.CopyCmd, { cmd: SEND_TO_CODEX_CMD })));
1232
- }
1233
- const WATCH_EVENT_COPY_KEYS = {
1234
- opened: "watches.event.opened",
1235
- replaced: "watches.event.replaced",
1236
- settled: "watches.event.settled",
1237
- };
1238
- const WATCH_ACTOR_COPY_KEYS = {
1239
- owner: "watches.actor.owner",
1240
- worker: "watches.actor.worker",
1241
- operator: "watches.actor.operator",
1242
- gateway: "watches.actor.gateway",
1243
- unknown: "watches.actor.unknown",
1244
- };
1245
- const WATCH_REASON_COPY_KEYS = {
1246
- done: "watches.reason.done",
1247
- untracked: "watches.reason.untracked",
1248
- idle_timeout: "watches.reason.idleTimeout",
1249
- pair_removed: "watches.reason.pairRemoved",
1250
- endpoint_retired: "watches.reason.endpointRetired",
1251
- tracking_disabled: "watches.reason.trackingDisabled",
1252
- };
1253
- function ProgressWatchRegister(props) {
1254
- const t = Embassy.useT();
1255
- const [locale] = Embassy.useLocale();
1256
- return (React.createElement("section", { className: "watch-register", "aria-labelledby": "watch-register-title" },
1257
- React.createElement("div", { className: "row-baseline section-label" },
1258
- React.createElement("div", null,
1259
- React.createElement(Embassy.MonoLabel, null, t("watches.eyebrow")),
1260
- React.createElement("h2", { id: "watch-register-title" }, t("watches.title"))),
1261
- React.createElement("span", { className: "mono text-muted" }, fmtCount(locale, props.watches.length))),
1262
- React.createElement("p", { className: "footnote" }, t("watches.note")),
1263
- props.watches.length === 0 ? (React.createElement("p", { className: "watch-register__empty" }, t("watches.empty"))) : (React.createElement("div", { className: "watch-grid" }, props.watches.map((watch) => (React.createElement("article", { className: "watch-card", key: `${watch.ownerAlias}|${watch.workerAlias}|${watch.conversationIdSuffix}` },
1264
- React.createElement("div", { className: "watch-card__head" },
1265
- React.createElement("code", null,
1266
- "\u2026",
1267
- watch.conversationIdSuffix)),
1268
- React.createElement("strong", null,
1269
- watch.ownerAlias,
1270
- " \u2192 ",
1271
- watch.workerAlias),
1272
- React.createElement("dl", { className: "watch-card__facts" },
1273
- React.createElement("div", null,
1274
- React.createElement("dt", null, t("watches.column.quietFor")),
1275
- React.createElement("dd", null,
1276
- React.createElement(Embassy.TimeAgo, { iso: watch.lastActivityAt }))),
1277
- React.createElement("div", null,
1278
- React.createElement("dt", null, t("watches.column.nextAction")),
1279
- React.createElement("dd", null,
1280
- React.createElement(Embassy.TimeAgo, { iso: watch.nextActionAt }))),
1281
- React.createElement("div", null,
1282
- React.createElement("dt", null, t("watches.column.nudges")),
1283
- React.createElement("dd", null, fmtCount(locale, watch.nudgeCount))))))))),
1284
- props.events.length === 0 ? null : (React.createElement("details", { className: "watch-history" },
1285
- React.createElement("summary", null, t("watches.history.title")),
1286
- React.createElement("ol", null, props.events.map((event) => (React.createElement("li", { key: event.sequence },
1287
- React.createElement(Embassy.TimeAgo, { iso: event.timestamp }),
1288
- " \u00B7 ",
1289
- React.createElement("code", null,
1290
- "\u2026",
1291
- event.conversationIdSuffix),
1292
- " \u00B7 ",
1293
- t(WATCH_ACTOR_COPY_KEYS[event.actor]),
1294
- event.actor === "owner" ? React.createElement(React.Fragment, null,
1295
- " ",
1296
- React.createElement("code", null, event.ownerAlias)) : null,
1297
- event.actor === "worker" ? React.createElement(React.Fragment, null,
1298
- " ",
1299
- React.createElement("code", null, event.workerAlias)) : null,
1300
- " · ",
1301
- t(WATCH_EVENT_COPY_KEYS[event.kind]),
1302
- event.reason === undefined ? "" : ` · ${t(WATCH_REASON_COPY_KEYS[event.reason])}`))))))));
1303
- }
1304
- function DeliveriesTab(props) {
1305
- const t = Embassy.useT();
1306
- const [locale] = Embassy.useLocale();
1307
- const captionId = React.useId();
1308
- const searchId = React.useId();
1309
- const [fromProvider, setFromProvider] = React.useState("all");
1310
- const [toProvider, setToProvider] = React.useState("all");
1311
- const [stateFilter, setStateFilter] = React.useState("all");
1312
- const [viewMode, setViewMode] = React.useState("byRoute");
1313
- const [query, setQuery] = React.useState("");
1314
- const [selectedKey, setSelectedKey] = React.useState(undefined);
1315
- const rowRefs = React.useRef(new Map());
1316
- const groups = props.groups;
1317
- const preset = props.preset;
1318
- const clearPreset = props.clearPreset;
1319
- React.useEffect(() => {
1320
- if (preset === undefined)
1321
- return;
1322
- if (preset.state !== undefined)
1323
- setStateFilter(preset.state);
1324
- const token = preset.token === undefined ? "" : preset.token.trim();
1325
- if (token !== "") {
1326
- setQuery(token);
1327
- const target = findPresetTarget(groups, token);
1328
- if (target !== undefined)
1329
- setSelectedKey(target.key);
1330
- }
1331
- clearPreset?.();
1332
- }, [preset]);
1333
- const needle = query.trim().toLowerCase();
1334
- const filtered = React.useMemo(() => groups.filter((view) => Embassy.adapter.matchesProviderFilters(view, fromProvider, toProvider) &&
1335
- (stateFilter === "all" || view.group.state === stateFilter) &&
1336
- matchesQuery(view, needle)), [groups, fromProvider, toProvider, stateFilter, needle]);
1337
- const pairs = React.useMemo(() => groupByRoutePair(filtered), [filtered]);
1338
- const ordered = React.useMemo(() => (viewMode === "flat" ? filtered : pairs.flatMap((pair) => pair.rows)), [viewMode, filtered, pairs]);
1339
- const selectedView = ordered.find((view) => view.key === selectedKey);
1340
- const activeKey = selectedView?.key ?? ordered[0]?.key;
1341
- const moveFocus = (event) => {
1342
- const keys = ordered.map((view) => view.key);
1343
- if (keys.length === 0)
1344
- return;
1345
- const currentIndex = Math.max(0, activeKey === undefined ? 0 : keys.indexOf(activeKey));
1346
- let nextIndex;
1347
- switch (event.key) {
1348
- case "ArrowDown":
1349
- nextIndex = Math.min(keys.length - 1, currentIndex + 1);
1350
- break;
1351
- case "ArrowUp":
1352
- nextIndex = Math.max(0, currentIndex - 1);
1353
- break;
1354
- case "Home":
1355
- nextIndex = 0;
1356
- break;
1357
- case "End":
1358
- nextIndex = keys.length - 1;
1359
- break;
1360
- default:
1361
- return;
1362
- }
1363
- const nextKey = keys[nextIndex];
1364
- if (nextKey === undefined)
1365
- return;
1366
- event.preventDefault();
1367
- setSelectedKey(nextKey);
1368
- rowRefs.current.get(nextKey)?.focus();
1369
- };
1370
- const renderRow = (view) => {
1371
- const group = view.group;
1372
- const isSelected = view.key === selectedView?.key;
1373
- return (React.createElement("button", { type: "button", key: view.key, className: "delivery-row", role: "option", "aria-selected": isSelected, tabIndex: view.key === activeKey ? 0 : -1, ref: (element) => {
1374
- if (element === null)
1375
- rowRefs.current.delete(view.key);
1376
- else
1377
- rowRefs.current.set(view.key, element);
1378
- }, onClick: () => {
1379
- setSelectedKey(view.key);
1380
- } },
1381
- React.createElement("span", { className: "delivery-row__id" }, group.messageIdSuffix ?? "—"),
1382
- React.createElement("span", { className: "delivery-row__route", title: view.routePair }, view.routePair),
1383
- React.createElement(Embassy.StateChip, { small: true, state: group.state, direction: group.direction, safeErrorCode: group.safeErrorCode }),
1384
- group.steer === true ? (React.createElement("span", { className: "mono text-muted" }, "STEER")) : null,
1385
- React.createElement("span", { className: "delivery-row__time" }, group.timestamp === undefined ? (t("time.unavailable")) : (React.createElement(Embassy.TimeAgo, { iso: group.timestamp })))));
1386
- };
1387
- const list = ordered.length === 0 ? null : (React.createElement("div", { className: "delivery-list", role: "listbox", "aria-labelledby": captionId, onKeyDown: moveFocus }, viewMode === "flat"
1388
- ? filtered.map(renderRow)
1389
- : pairs.map((pair) => (React.createElement("div", { className: "delivery-group", role: "group", "aria-label": pair.pair, key: pair.pair },
1390
- React.createElement("div", { className: "delivery-group__head", "aria-hidden": true },
1391
- React.createElement(Embassy.MonoLabel, null, t("activity.column.route")),
1392
- React.createElement("span", { className: "delivery-group__key" }, pair.pair)),
1393
- pair.rows.map(renderRow),
1394
- React.createElement(Embassy.Rule, null))))));
1395
- return (React.createElement("div", { className: "deliveries-layout" },
1396
- React.createElement("div", { className: "deliveries-main" },
1397
- React.createElement("div", { className: "row-baseline section-label" },
1398
- React.createElement("h2", { className: "text-xs text-muted", id: captionId }, t("app.deliveries.caption")),
1399
- React.createElement("span", { className: "mono text-muted" }, t("attention.countVisible", {
1400
- count: fmtCount(locale, ordered.length),
1401
- })),
1402
- props.omissions.messageGroups > 0 ? (React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
1403
- count: fmtCount(locale, groups.length),
1404
- }))) : null),
1405
- React.createElement("div", { className: "filters" },
1406
- React.createElement("div", { className: "filter-group" },
1407
- React.createElement(Embassy.MonoLabel, null, t("app.deliveries.fromProvider")),
1408
- React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("app.deliveries.fromProvider") },
1409
- React.createElement(Embassy.FilterPill, { active: fromProvider === "all", onClick: () => {
1410
- setFromProvider("all");
1411
- } }, t("app.deliveries.providerAll")),
1412
- Embassy.GATEWAY_PROVIDERS.map((provider) => (React.createElement(Embassy.FilterPill, { key: provider, active: fromProvider === provider, onClick: () => {
1413
- setFromProvider(provider);
1414
- } }, t(`provider.${provider}`)))))),
1415
- React.createElement("div", { className: "filter-group" },
1416
- React.createElement(Embassy.MonoLabel, null, t("app.deliveries.toProvider")),
1417
- React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("app.deliveries.toProvider") },
1418
- React.createElement(Embassy.FilterPill, { active: toProvider === "all", onClick: () => { setToProvider("all"); } }, t("app.deliveries.providerAll")),
1419
- Embassy.GATEWAY_PROVIDERS.map((provider) => (React.createElement(Embassy.FilterPill, { key: provider, active: toProvider === provider, onClick: () => { setToProvider(provider); } }, t(`provider.${provider}`)))))),
1420
- React.createElement("div", { className: "filter-group" },
1421
- React.createElement(Embassy.MonoLabel, null, t("app.deliveries.view.label")),
1422
- React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("app.deliveries.view.label") },
1423
- React.createElement(Embassy.FilterPill, { active: viewMode === "byRoute", onClick: () => {
1424
- setViewMode("byRoute");
1425
- } }, t("app.deliveries.view.byRoute")),
1426
- React.createElement(Embassy.FilterPill, { active: viewMode === "flat", onClick: () => {
1427
- setViewMode("flat");
1428
- } }, t("app.deliveries.view.flat"))))),
1429
- React.createElement("p", { className: "footnote section-label" }, t("app.deliveries.noConv")),
1430
- React.createElement(ProgressWatchRegister, { watches: props.watches, events: props.watchEvents }),
1431
- React.createElement("div", { className: "filter-group filters-block" },
1432
- React.createElement(Embassy.MonoLabel, null, t("column.state")),
1433
- React.createElement("div", { className: "pill-row", role: "group", "aria-label": t("column.state") },
1434
- React.createElement(Embassy.FilterPill, { active: stateFilter === "all", onClick: () => {
1435
- setStateFilter("all");
1436
- } }, t("app.deliveries.state.all")),
1437
- DELIVERY_STATE_FILTERS.map((state) => (React.createElement(Embassy.FilterPill, { key: state, active: stateFilter === state, onClick: () => {
1438
- setStateFilter(stateFilter === state ? "all" : state);
1439
- } }, state))))),
1440
- React.createElement("div", { className: "filter-group filters-search" },
1441
- React.createElement("label", { className: "sr-only", htmlFor: searchId }, t("app.deliveries.search")),
1442
- React.createElement("input", { id: searchId, className: "text-input filter-input", type: "text", value: query, placeholder: t("app.deliveries.search"), onChange: (event) => {
1443
- setQuery(event.target.value);
1444
- } })),
1445
- props.omissions.messageEvents > 0 ? (React.createElement("p", { className: "footnote section-label" }, t("app.deliveries.eventsTruncated", {
1446
- count: fmtCount(locale, props.omissions.messageEvents),
1447
- }))) : null,
1448
- groups.length === 0 ? (React.createElement(DeliveriesEmpty, { text: t("activity.empty") })) : ordered.length === 0 ? (React.createElement(DeliveriesEmpty, { text: t("app.deliveries.noMatch") })) : (React.createElement(React.Fragment, null,
1449
- list,
1450
- viewMode === "flat" ? React.createElement(Embassy.Rule, null) : null))),
1451
- React.createElement("div", { className: "deliveries-rail" }, selectedView === undefined ? (React.createElement("p", { className: "detail-pane__empty" }, t("app.deliveries.pickRow"))) : (React.createElement(DeliveryDetail, { key: selectedView.key, view: selectedView })))));
1452
- }
1453
- Embassy.DeliveriesTab = DeliveriesTab;
1454
- })(Embassy || (Embassy = {}));
1455
- var Embassy;
1456
- (function (Embassy) {
1457
- function canRequestCodexRegistrationRemoval(route) {
1458
- return route.provider === "codex" && route.mutable !== false;
1459
- }
1460
- Embassy.canRequestCodexRegistrationRemoval = canRequestCodexRegistrationRemoval;
1461
- Embassy.canOfferConsentEdgeCandidate = (route) => route.enabled && (route.state === "idle" || route.state === "busy" || route.state === "awaiting_approval");
1462
- function sameEdge(edge, aliases) {
1463
- const actual = edge.endpoints.map(({ alias }) => alias);
1464
- return aliases.every((alias) => actual.includes(alias));
1465
- }
1466
- function ActionButton(props) {
1467
- const t = Embassy.useT();
1468
- const [confirming, setConfirming] = React.useState(false);
1469
- const [pending, setPending] = React.useState(false);
1470
- const [result, setResult] = React.useState();
1471
- const run = () => {
1472
- setPending(true);
1473
- props.onAction(props.action).then((next) => {
1474
- setResult(next);
1475
- setPending(false);
1476
- setConfirming(false);
1477
- });
1478
- };
1479
- if (confirming)
1480
- return React.createElement("span", { className: "stack-sm" },
1481
- props.consequence === undefined ? null : React.createElement("span", { className: "footnote" }, props.consequence),
1482
- React.createElement("span", { className: "row-baseline" },
1483
- React.createElement("button", { type: "button", disabled: pending, onClick: run }, pending ? t("live.action.pending") : t("live.action.confirm")),
1484
- React.createElement("button", { type: "button", disabled: pending, onClick: () => { setConfirming(false); } }, t("live.action.cancel"))));
1485
- return React.createElement("span", { className: "stack-sm" },
1486
- React.createElement("button", { type: "button", disabled: !props.enabled, onClick: () => { setResult(undefined); setConfirming(true); } }, props.label),
1487
- result === undefined ? null : React.createElement("span", { className: "footnote" }, t(result.ok ? "live.action.succeeded" : "live.action.failed", { code: result.code })));
1488
- }
1489
- function edgeLabel(edge, t) {
1490
- const labels = edge.endpoints.map(({ provider, alias }) => t("app.routes.consentEndpoint", { provider: t(`provider.${provider}`), alias }));
1491
- return t("app.routes.consentEdge", { left: labels[0] ?? "", right: labels[1] ?? "" });
1492
- }
1493
- function RoutesTab(props) {
1494
- const t = Embassy.useT();
1495
- const { data } = props;
1496
- const candidates = [];
1497
- for (let left = 0; left < data.routes.length; left += 1) {
1498
- for (let right = left + 1; right < data.routes.length; right += 1) {
1499
- const a = data.routes[left]?.route;
1500
- const b = data.routes[right]?.route;
1501
- if (a === undefined || b === undefined || !Embassy.canOfferConsentEdgeCandidate(a) || !Embassy.canOfferConsentEdgeCandidate(b) ||
1502
- (a.provider === b.provider && a.host === b.host) || [a, b].find((route) => route.host === [a.host, b.host].sort()[0])?.mutable === false)
1503
- continue;
1504
- if (!data.consentEdges.some((edge) => sameEdge(edge, [a.alias, b.alias])))
1505
- candidates.push([a, b]);
1506
- }
1507
- }
1508
- return React.createElement("div", { className: "tab-panel" },
1509
- React.createElement("section", { className: "section" },
1510
- React.createElement("h2", { className: "mono-label section-label" }, t("app.routes.topology")),
1511
- React.createElement("p", null, t("app.routes.pairSummary", { ready: data.graph.readyConsentEdgeCount, total: data.graph.consentEdgeCount })),
1512
- React.createElement("ul", { className: "pair-list" }, data.consentEdges.map((edge) => {
1513
- const aliases = edge.endpoints.map(({ alias }) => alias);
1514
- return React.createElement("li", { key: aliases.join("\0") },
1515
- React.createElement("code", null, edgeLabel(edge, t)),
1516
- " ",
1517
- React.createElement(Embassy.StateChip, { domain: "route", state: edge.state === "ready" ? "idle" : "stale", small: true }),
1518
- React.createElement(ActionButton, { action: { action: "unpair", aliases }, label: t("live.action.unpair"), enabled: props.actionsEnabled && edge.mutable !== false, onAction: props.onAction }));
1519
- })),
1520
- data.consentEdgesOmitted > 0 ? React.createElement("p", { className: "footnote" }, t("app.omitted.pairs", { count: data.consentEdgesOmitted })) : null),
1521
- Embassy.GATEWAY_PROVIDERS.map((provider) => {
1522
- const routes = data.routes.filter((view) => view.route.provider === provider);
1523
- return React.createElement("section", { className: "section", key: provider, "aria-label": t(`provider.${provider}`) },
1524
- React.createElement("h2", { className: "mono-label section-label" }, t(`provider.${provider}`)),
1525
- React.createElement("ul", null, routes.map(({ route, oldestAgeMs }) => React.createElement("li", { key: `${route.alias}\0${route.host}` },
1526
- React.createElement("code", null, route.alias),
1527
- " \u00B7 ",
1528
- React.createElement(Embassy.StateChip, { domain: "route", state: route.state, small: true }),
1529
- " \u00B7 ",
1530
- t("app.routes.queueDepth"),
1531
- ": ",
1532
- route.queueDepth,
1533
- oldestAgeMs === undefined ? "" : ` · ${Embassy.fmtAge(oldestAgeMs)}`,
1534
- canRequestCodexRegistrationRemoval(route) ? React.createElement(ActionButton, { action: { action: "remove_codex_registration", alias: route.alias }, label: t("live.action.removeCodexRegistration"), consequence: t("app.routes.removeCodex.consequence", { alias: route.alias }), enabled: props.actionsEnabled, onAction: props.onAction }) : null))),
1535
- React.createElement("p", { className: "footnote" }, t("app.routes.unpairedProvider", { provider: t(`provider.${provider}`), count: data.graph.unpairedReadyByProvider[provider] })));
1536
- }),
1537
- React.createElement("section", { className: "section" },
1538
- React.createElement("h2", { className: "mono-label section-label" }, t("app.routes.candidates")),
1539
- React.createElement("ul", null, candidates.map(([left, right]) => React.createElement("li", { key: `${left.alias}\0${right.alias}` },
1540
- React.createElement("code", null,
1541
- left.alias,
1542
- " \u2194 ",
1543
- right.alias),
1544
- React.createElement(ActionButton, { action: { action: "pair", aliases: [left.alias, right.alias] }, label: t("live.action.pair"), enabled: props.actionsEnabled, onAction: props.onAction })))),
1545
- React.createElement(ActionButton, { action: { action: "refresh_dashboard" }, label: t("live.action.refresh"), enabled: props.actionsEnabled, onAction: props.onAction })));
1546
- }
1547
- Embassy.RoutesTab = RoutesTab;
1548
- })(Embassy || (Embassy = {}));
1549
- var Embassy;
1550
- (function (Embassy) {
1551
- const ACTIVITY_KINDS = [
1552
- "delivery",
1553
- "operation",
1554
- "alert",
1555
- ];
1556
- const ACTIVITY_KIND_LABEL_KEYS = {
1557
- delivery: "app.activity.kinds.delivery",
1558
- operation: "app.activity.kinds.operation",
1559
- alert: "app.activity.kinds.alert",
1560
- };
1561
- const OPERATION_COPY_KEYS = {
1562
- discovery_refreshed: "app.activity.operation.discoveryRefreshed",
1563
- claude_selected: "app.activity.operation.claudeSelected",
1564
- claude_unselected: "app.activity.operation.claudeUnselected",
1565
- codex_registered: "app.activity.operation.codexRegistered",
1566
- codex_succeeded: "app.activity.operation.codexSucceeded",
1567
- codex_unregistered: "app.activity.operation.codexUnregistered",
1568
- routes_paired: "app.activity.operation.routesPaired",
1569
- routes_unpaired: "app.activity.operation.routesUnpaired",
1570
- watch_ended: "app.activity.operation.watchEnded",
1571
- };
1572
- const ACTIVITY_EMPTY_COMMAND = "embassy send --from <alias> --to <alias>";
1573
- const activityCountFormatters = new Map();
1574
- function activityAuthority(event) {
1575
- return event.operatorAction ? "operator" : "automatic";
1576
- }
1577
- Embassy.activityAuthority = activityAuthority;
1578
- function formatCount(count, locale) {
1579
- const existing = activityCountFormatters.get(locale);
1580
- if (existing !== undefined)
1581
- return existing.format(count);
1582
- const formatter = new Intl.NumberFormat(locale);
1583
- activityCountFormatters.set(locale, formatter);
1584
- return formatter.format(count);
1585
- }
1586
- function countByKind(rows) {
1587
- let delivery = 0;
1588
- let operation = 0;
1589
- let alert = 0;
1590
- for (const row of rows) {
1591
- if (row.kind === "delivery")
1592
- delivery += 1;
1593
- else if (row.kind === "operation")
1594
- operation += 1;
1595
- else
1596
- alert += 1;
1597
- }
1598
- return { all: rows.length, delivery, operation, alert };
1599
- }
1600
- function kindIsLowerBound(kind, omissions) {
1601
- if (kind === "delivery") {
1602
- return omissions.messageGroups > 0 || omissions.upstreamMessageEvents > 0;
1603
- }
1604
- if (kind === "operation") {
1605
- return omissions.activityEvents > 0 || omissions.upstreamActivityEvents > 0;
1606
- }
1607
- return omissions.attentionItems > 0 || omissions.upstreamAlerts > 0;
1608
- }
1609
- function filterIsLowerBound(filter, omissions) {
1610
- return filter === "all"
1611
- ? ACTIVITY_KINDS.some((kind) => kindIsLowerBound(kind, omissions))
1612
- : kindIsLowerBound(filter, omissions);
1613
- }
1614
- function activityRowIdentity(row) {
1615
- if (row.kind === "delivery") {
1616
- return `delivery|${Embassy.deliveryGroupKey(row.group)}`;
1617
- }
1618
- if (row.kind === "operation") {
1619
- return `operation|${row.event.sequence}|${row.event.action}`;
1620
- }
1621
- const item = row.item;
1622
- return `alert|${row.timestamp}|${item.guidance}|${item.alias ?? ""}|${item.host ?? ""}|${item.code ?? ""}`;
1623
- }
1624
- function activityRowKeys(rows) {
1625
- const seen = new Map();
1626
- return rows.map((row) => {
1627
- const identity = activityRowIdentity(row);
1628
- const occurrence = seen.get(identity) ?? 0;
1629
- seen.set(identity, occurrence + 1);
1630
- return occurrence === 0 ? identity : `${identity}#${occurrence}`;
1631
- });
1632
- }
1633
- function alertScope(item, t) {
1634
- const parts = [];
1635
- if (item.provider !== undefined) {
1636
- parts.push(t(`provider.${item.provider}`));
1637
- }
1638
- if (item.alias !== undefined)
1639
- parts.push(item.alias);
1640
- if (item.host !== undefined)
1641
- parts.push(item.host);
1642
- if (item.code !== undefined)
1643
- parts.push(item.code);
1644
- return parts.join(" · ");
1645
- }
1646
- function DeliveryEntryText(props) {
1647
- const group = props.group;
1648
- return (React.createElement("div", { className: "activity-text chip-row" },
1649
- React.createElement("span", { className: "mono" },
1650
- group.sourceAlias,
1651
- " \u2192 ",
1652
- group.targetAlias),
1653
- React.createElement(Embassy.StateChip, { small: true, state: group.state, direction: group.direction, safeErrorCode: group.safeErrorCode }),
1654
- group.steer === true ? (React.createElement("span", { className: "mono text-muted" }, "STEER")) : null,
1655
- group.messageIdSuffix === undefined ? null : (React.createElement("span", { className: "mono text-muted" }, group.messageIdSuffix))));
1656
- }
1657
- function AlertEntryText(props) {
1658
- const t = Embassy.useT();
1659
- const scope = alertScope(props.item, t);
1660
- return (React.createElement("div", { className: "activity-text stack-sm" },
1661
- React.createElement("div", { className: "chip-row" },
1662
- React.createElement(Embassy.StateChip, { small: true, domain: "severity", state: props.item.severity }),
1663
- React.createElement("span", null, t(`guidance.${props.guidanceKey}.title`))),
1664
- scope === "" ? null : (React.createElement("span", { className: "attention-item__scope" },
1665
- t("attention.scope"),
1666
- ": ",
1667
- scope))));
1668
- }
1669
- function OperationEntryText(props) {
1670
- const t = Embassy.useT();
1671
- const event = props.event;
1672
- const authority = activityAuthority(event);
1673
- return (React.createElement("div", { className: "activity-text stack-sm", "data-activity-authority": authority },
1674
- React.createElement("div", { className: "chip-row" },
1675
- React.createElement("span", { className: "chip chip--small", "data-kind": "inert" }, t(authority === "operator"
1676
- ? "app.activity.operation.operator"
1677
- : "app.activity.operation.automatic")),
1678
- React.createElement("span", null, t(OPERATION_COPY_KEYS[event.action])),
1679
- React.createElement("span", { className: "mono text-muted" }, t(event.outcome === "accepted"
1680
- ? "app.activity.operation.accepted"
1681
- : "app.activity.operation.rejected"))),
1682
- event.aliases.length === 0 && event.safeErrorCode === undefined ? null : (React.createElement("span", { className: "attention-item__scope" }, [...event.aliases, event.safeErrorCode]
1683
- .filter((part) => part !== undefined)
1684
- .join(" · ")))));
1685
- }
1686
- function ActivityEntry(props) {
1687
- const t = Embassy.useT();
1688
- const row = props.row;
1689
- return (React.createElement("div", { className: "activity-row", role: "listitem" },
1690
- row.timestamp === undefined ? (React.createElement("span", { className: "time-ago text-muted", title: t("transit.unavailable") },
1691
- "\u2014",
1692
- React.createElement("span", { className: "sr-only" }, t("transit.unavailable")))) : (React.createElement(Embassy.TimeAgo, { iso: row.timestamp })),
1693
- React.createElement("span", { className: "activity-kind" }, t(ACTIVITY_KIND_LABEL_KEYS[row.kind])),
1694
- row.kind === "delivery" ? (React.createElement(DeliveryEntryText, { group: row.group })) : row.kind === "operation" ? (React.createElement(OperationEntryText, { event: row.event })) : (React.createElement(AlertEntryText, { item: row.item, guidanceKey: row.guidanceKey }))));
1695
- }
1696
- function ActivityTab(props) {
1697
- const t = Embassy.useT();
1698
- const [locale] = Embassy.useLocale();
1699
- const headingId = React.useId();
1700
- const [kindFilter, setKindFilter] = React.useState("all");
1701
- const rows = props.rows;
1702
- const counts = React.useMemo(() => countByKind(rows), [rows]);
1703
- const shown = React.useMemo(() => kindFilter === "all"
1704
- ? rows
1705
- : rows.filter((row) => row.kind === kindFilter), [rows, kindFilter]);
1706
- const keys = React.useMemo(() => activityRowKeys(shown), [shown]);
1707
- const showAll = React.useCallback(() => {
1708
- setKindFilter("all");
1709
- }, []);
1710
- const toggleKind = React.useCallback((kind) => {
1711
- setKindFilter((current) => (current === kind ? "all" : kind));
1712
- }, []);
1713
- const isLowerBound = filterIsLowerBound(kindFilter, props.omissions);
1714
- return (React.createElement("section", { className: "section tab-panel--narrow", "aria-labelledby": headingId },
1715
- React.createElement("h2", { id: headingId, className: "mono-label section-label" }, t("app.activity.title")),
1716
- React.createElement("div", { className: "filters" },
1717
- React.createElement("div", { className: "filter-group" },
1718
- React.createElement("div", { className: "pill-row" },
1719
- React.createElement(Embassy.FilterPill, { active: kindFilter === "all", onClick: showAll },
1720
- t("app.activity.kinds.all"),
1721
- " ",
1722
- React.createElement("span", { className: "pill__count" }, formatCount(counts.all, locale))),
1723
- ACTIVITY_KINDS.map((kind) => (React.createElement(Embassy.FilterPill, { key: kind, active: kindFilter === kind, onClick: () => {
1724
- toggleKind(kind);
1725
- } },
1726
- t(ACTIVITY_KIND_LABEL_KEYS[kind]),
1727
- " ",
1728
- React.createElement("span", { className: "pill__count" }, formatCount(counts[kind], locale)))))),
1729
- isLowerBound ? (React.createElement("div", { className: "chip-row" },
1730
- React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
1731
- count: formatCount(shown.length, locale),
1732
- })))) : null)),
1733
- React.createElement("div", { className: "stack" },
1734
- shown.length === 0 ? (React.createElement("div", { className: "empty-state empty-state--activity" },
1735
- React.createElement("p", { className: "empty-state__text" }, t("app.activity.empty")),
1736
- React.createElement(Embassy.CopyCmd, { cmd: ACTIVITY_EMPTY_COMMAND }))) : (React.createElement("div", null,
1737
- React.createElement("div", { className: "activity-list", role: "list" }, shown.map((row, index) => (React.createElement(ActivityEntry, { key: keys[index] ?? index, row: row })))),
1738
- React.createElement(Embassy.Rule, null))),
1739
- React.createElement("p", { className: "footnote" }, t("app.activity.limited")))));
1740
- }
1741
- Embassy.ActivityTab = ActivityTab;
1742
- })(Embassy || (Embassy = {}));
1743
- var Embassy;
1744
- (function (Embassy) {
1745
- const DIAGNOSTICS_ABSENT_FIELD = "—";
1746
- const DIAGNOSTICS_CONNECTOR_COLUMNS = 8;
1747
- const diagnosticsNumberFormatters = new Map();
1748
- function diagnosticsNumberFormatter(locale) {
1749
- const existing = diagnosticsNumberFormatters.get(locale);
1750
- if (existing !== undefined)
1751
- return existing;
1752
- const formatter = new Intl.NumberFormat(locale);
1753
- diagnosticsNumberFormatters.set(locale, formatter);
1754
- return formatter;
1755
- }
1756
- function formatDiagnosticsCount(value, locale) {
1757
- return diagnosticsNumberFormatter(locale).format(value);
1758
- }
1759
- function formatDiagnosticsBytes(value, locale) {
1760
- if (value < 1_024) {
1761
- return `${formatDiagnosticsCount(value, locale)} B`;
1762
- }
1763
- if (value < 1_048_576) {
1764
- const kibibytes = value / 1_024;
1765
- const rounded = kibibytes < 10 ? Math.round(kibibytes * 10) / 10 : Math.round(kibibytes);
1766
- return `${formatDiagnosticsCount(rounded, locale)} KiB`;
1767
- }
1768
- const mebibytes = value / 1_048_576;
1769
- const rounded = mebibytes < 10 ? Math.round(mebibytes * 10) / 10 : Math.round(mebibytes);
1770
- return `${formatDiagnosticsCount(rounded, locale)} MiB`;
1771
- }
1772
- function DiagnosticsLowerBound(props) {
1773
- const t = Embassy.useT();
1774
- const [locale] = Embassy.useLocale();
1775
- return (React.createElement("span", { className: "lower-bound" }, t("app.lowerBound", {
1776
- count: formatDiagnosticsCount(props.count, locale),
1777
- })));
1778
- }
1779
- function DiagnosticsConnectorTable(props) {
1780
- const t = Embassy.useT();
1781
- const [locale] = Embassy.useLocale();
1782
- const { connectors } = props;
1783
- return (React.createElement("div", { className: "table-wrap" },
1784
- React.createElement("table", { className: "data-table" },
1785
- React.createElement("caption", { className: "sr-only" }, t("app.diag.versions.caption")),
1786
- React.createElement("thead", null,
1787
- React.createElement("tr", null,
1788
- React.createElement("th", { scope: "col" }, t("app.diag.col.provider")),
1789
- React.createElement("th", { scope: "col" }, t("app.diag.col.host")),
1790
- React.createElement("th", { scope: "col" }, t("app.diag.col.protocol")),
1791
- React.createElement("th", { scope: "col" }, t("app.diag.col.version")),
1792
- React.createElement("th", { scope: "col" }, t("diagnostics.health")),
1793
- React.createElement("th", { scope: "col" }, t("column.observed")),
1794
- React.createElement("th", { scope: "col" }, t("diagnostics.registry.title")),
1795
- React.createElement("th", { scope: "col" }, t("column.issue")))),
1796
- React.createElement("tbody", null, connectors.length === 0 ? (React.createElement("tr", null,
1797
- React.createElement("td", { colSpan: DIAGNOSTICS_CONNECTOR_COLUMNS }, t("diagnostics.connectors.empty")))) : (connectors.map((connector, index) => {
1798
- const observation = connector.registry;
1799
- const stateKey = observation === undefined
1800
- ? undefined
1801
- : observation.parseableRecordSeenSinceBoot
1802
- ? "diagnostics.registry.state.parseableRecordObserved"
1803
- : observation.entriesScanned === 0
1804
- ? "diagnostics.registry.state.emptySinceBoot"
1805
- : "diagnostics.registry.state.noParseableRecordSinceBoot";
1806
- return (React.createElement("tr", { key: `${connector.provider}|${connector.host}|${index}` },
1807
- React.createElement("th", { scope: "row" }, t(`provider.${connector.provider}`)),
1808
- React.createElement("td", { className: "cell-mono" }, connector.host),
1809
- React.createElement("td", { className: "cell-mono" }, connector.protocol ?? DIAGNOSTICS_ABSENT_FIELD),
1810
- React.createElement("td", { className: "cell-mono" }, connector.protocolVersion ?? DIAGNOSTICS_ABSENT_FIELD),
1811
- React.createElement("td", null,
1812
- React.createElement(Embassy.StateChip, { domain: "health", state: connector.health, small: true })),
1813
- React.createElement("td", null, connector.lastSeenAt === undefined ? (DIAGNOSTICS_ABSENT_FIELD) : (React.createElement(Embassy.TimeAgo, { iso: connector.lastSeenAt }))),
1814
- React.createElement("td", null, observation === undefined || stateKey === undefined ? (DIAGNOSTICS_ABSENT_FIELD) : (React.createElement("div", { className: "stack-sm" },
1815
- React.createElement("span", null, t(stateKey)),
1816
- React.createElement("span", { className: "cell-note" },
1817
- t("diagnostics.registry.entriesScanned"),
1818
- ": ",
1819
- formatDiagnosticsCount(observation.entriesScanned, locale),
1820
- " \u00B7 ",
1821
- t("diagnostics.registry.parseableRecords"),
1822
- ": ",
1823
- formatDiagnosticsCount(observation.parseableRecords, locale)),
1824
- React.createElement("span", { className: "cell-note" },
1825
- t("diagnostics.registry.rejected"),
1826
- ": ",
1827
- observation.rejected.length === 0
1828
- ? t("diagnostics.registry.rejectedNone")
1829
- : observation.rejected.map((row, rejectionIndex) => (React.createElement(React.Fragment, { key: row.safeErrorCode },
1830
- rejectionIndex === 0 ? null : " · ",
1831
- React.createElement("code", null, row.safeErrorCode),
1832
- " \u00D7 ",
1833
- formatDiagnosticsCount(row.count, locale))))),
1834
- observation.rejectedCodesOmitted === 0 ? null : (React.createElement("span", { className: "cell-note" }, t("diagnostics.registry.rejectedCodesOmitted", {
1835
- count: formatDiagnosticsCount(observation.rejectedCodesOmitted, locale),
1836
- })))))),
1837
- React.createElement("td", { className: "cell-mono" }, connector.safeErrorCode === undefined &&
1838
- connector.codexDoctor === undefined
1839
- ? DIAGNOSTICS_ABSENT_FIELD
1840
- : (React.createElement("div", { className: "stack-sm" },
1841
- connector.safeErrorCode === undefined
1842
- ? null
1843
- : React.createElement("code", null, connector.safeErrorCode),
1844
- connector.codexDoctor?.conditions.map((condition) => (React.createElement("span", { key: condition, className: "cell-note" }, t(`diagnostics.codexDoctor.${condition}`)))))))));
1845
- }))))));
1846
- }
1847
- function DiagnosticsCounterTable(props) {
1848
- return (React.createElement("div", { className: "table-wrap counters-list" },
1849
- React.createElement("table", { className: "data-table data-table--counters" },
1850
- React.createElement("caption", { className: "table-caption" }, props.caption),
1851
- React.createElement("tbody", null, props.rows.map((row) => (React.createElement("tr", { key: row.id },
1852
- React.createElement("th", { scope: "row" }, row.label),
1853
- React.createElement("td", { className: "cell-mono" }, row.value)))))),
1854
- React.createElement(Embassy.Rule, null)));
1855
- }
1856
- function DiagnosticsOmissionTable(props) {
1857
- return (React.createElement("div", { className: "table-wrap" },
1858
- React.createElement("table", { className: "data-table" },
1859
- React.createElement("caption", { className: "table-caption" }, props.caption),
1860
- React.createElement("tbody", null, props.rows.map((row) => (React.createElement("tr", { key: row.field },
1861
- React.createElement("th", { scope: "row", className: "cell-mono" }, row.field),
1862
- React.createElement("td", null, row.text)))))),
1863
- React.createElement(Embassy.Rule, null)));
1864
- }
1865
- function DiagnosticsDisclosure(props) {
1866
- const t = Embassy.useT();
1867
- const [open, setOpen] = React.useState(false);
1868
- return (React.createElement("details", { className: "details-block", onToggle: (event) => {
1869
- setOpen(event.currentTarget.open);
1870
- } },
1871
- React.createElement("summary", null,
1872
- props.label,
1873
- " \u00B7 ",
1874
- open ? t("app.hide") : t("app.show")),
1875
- props.children));
1876
- }
1877
- function DiagnosticsSetting(props) {
1878
- const { setting } = props;
1879
- return (React.createElement("div", { className: "setting-row" },
1880
- React.createElement("div", { className: "row-baseline" },
1881
- React.createElement("span", { className: setting.nameIsToken
1882
- ? "detail-value detail-value--mono"
1883
- : "detail-value" }, setting.name),
1884
- React.createElement("span", { className: "mono-label" }, setting.where)),
1885
- React.createElement("div", { className: "env-name" }, setting.envName ?? DIAGNOSTICS_ABSENT_FIELD),
1886
- setting.note === undefined ? null : (React.createElement("p", { className: "footnote" }, setting.note))));
1887
- }
1888
- function DiagnosticsTab(props) {
1889
- const t = Embassy.useT();
1890
- const [locale] = Embassy.useLocale();
1891
- const { data } = props;
1892
- const versionsHeadingId = React.useId();
1893
- const limitsHeadingId = React.useId();
1894
- const deadlineHeadingId = React.useId();
1895
- const queueHeadingId = React.useId();
1896
- const bytesHeadingId = React.useId();
1897
- const settingsHeadingId = React.useId();
1898
- const deadlineIsWarn = data.expiredCount > 0;
1899
- const deadlineBucketKeys = {
1900
- under_1m: "app.diag.deadline.bucket.under1m",
1901
- "1m_to_5m": "app.diag.deadline.bucket.1to5m",
1902
- "5m_to_15m": "app.diag.deadline.bucket.5to15m",
1903
- "15m_to_60m": "app.diag.deadline.bucket.15to60m",
1904
- over_60m: "app.diag.deadline.bucket.over60m",
1905
- };
1906
- const accounting = data.accounting;
1907
- const accountingRows = [
1908
- {
1909
- id: "accepted",
1910
- label: t("diagnostics.accepted"),
1911
- value: formatDiagnosticsCount(accounting.accepted, locale),
1912
- },
1913
- {
1914
- id: "duplicates",
1915
- label: t("diagnostics.duplicates"),
1916
- value: formatDiagnosticsCount(accounting.duplicates, locale),
1917
- },
1918
- {
1919
- id: "delivered",
1920
- label: t("diagnostics.delivered"),
1921
- value: formatDiagnosticsCount(accounting.delivered, locale),
1922
- },
1923
- {
1924
- id: "unconfirmed",
1925
- label: t("diagnostics.unconfirmed"),
1926
- value: formatDiagnosticsCount(accounting.unconfirmed, locale),
1927
- },
1928
- {
1929
- id: "ambiguous",
1930
- label: t("diagnostics.ambiguous"),
1931
- value: formatDiagnosticsCount(accounting.ambiguous, locale),
1932
- },
1933
- {
1934
- id: "failed",
1935
- label: t("diagnostics.failed"),
1936
- value: formatDiagnosticsCount(accounting.failed, locale),
1937
- },
1938
- {
1939
- id: "expired",
1940
- label: t("diagnostics.expired"),
1941
- value: formatDiagnosticsCount(accounting.expired, locale),
1942
- },
1943
- {
1944
- id: "cancelled",
1945
- label: t("diagnostics.cancelled"),
1946
- value: formatDiagnosticsCount(accounting.cancelled, locale),
1947
- },
1948
- {
1949
- id: "abandoned",
1950
- label: t("diagnostics.abandoned"),
1951
- value: formatDiagnosticsCount(accounting.abandoned, locale),
1952
- },
1953
- {
1954
- id: "rejected",
1955
- label: t("diagnostics.rejected"),
1956
- value: formatDiagnosticsCount(accounting.rejected, locale),
1957
- },
1958
- {
1959
- id: "bytesAccepted",
1960
- label: t("diagnostics.bytesAccepted"),
1961
- value: formatDiagnosticsBytes(accounting.bytesAccepted, locale),
1962
- },
1963
- {
1964
- id: "queuedBytes",
1965
- label: t("diagnostics.queuedBytes"),
1966
- value: formatDiagnosticsBytes(accounting.queuedBytes, locale),
1967
- },
1968
- ];
1969
- const omissions = data.omissions;
1970
- const omissionRows = [
1971
- {
1972
- field: "connectors",
1973
- text: t("diagnostics.omissions.connectors", {
1974
- count: formatDiagnosticsCount(omissions.connectors, locale),
1975
- }),
1976
- },
1977
- {
1978
- field: "availablePeers",
1979
- text: t("diagnostics.omissions.peers", {
1980
- count: formatDiagnosticsCount(omissions.availablePeers, locale),
1981
- }),
1982
- },
1983
- {
1984
- field: "routes",
1985
- text: t("diagnostics.omissions.routes", {
1986
- count: formatDiagnosticsCount(omissions.routes, locale),
1987
- }),
1988
- },
1989
- {
1990
- field: "consentEdges",
1991
- text: t("diagnostics.omissions.consentEdges", {
1992
- count: formatDiagnosticsCount(omissions.consentEdges, locale),
1993
- }),
1994
- },
1995
- {
1996
- field: "upstreamMessageEvents",
1997
- text: t("diagnostics.omissions.upstreamMessageEvents", {
1998
- count: formatDiagnosticsCount(omissions.upstreamMessageEvents, locale),
1999
- }),
2000
- },
2001
- {
2002
- field: "messageGroups",
2003
- text: t("diagnostics.omissions.messageGroups", {
2004
- count: formatDiagnosticsCount(omissions.messageGroups, locale),
2005
- }),
2006
- },
2007
- {
2008
- field: "messageEvents",
2009
- text: t("diagnostics.omissions.messageEvents", {
2010
- count: formatDiagnosticsCount(omissions.messageEvents, locale),
2011
- }),
2012
- },
2013
- {
2014
- field: "upstreamAlerts",
2015
- text: t("diagnostics.omissions.upstreamAlerts", {
2016
- count: formatDiagnosticsCount(omissions.upstreamAlerts, locale),
2017
- }),
2018
- },
2019
- {
2020
- field: "attentionItems",
2021
- text: t("diagnostics.omissions.attentionItems", {
2022
- count: formatDiagnosticsCount(omissions.attentionItems, locale),
2023
- }),
2024
- },
2025
- ];
2026
- const nothingOmitted = omissions.connectors === 0 &&
2027
- omissions.availablePeers === 0 &&
2028
- omissions.routes === 0 &&
2029
- omissions.consentEdges === 0 &&
2030
- omissions.upstreamMessageEvents === 0 &&
2031
- omissions.messageGroups === 0 &&
2032
- omissions.messageEvents === 0 &&
2033
- omissions.upstreamAlerts === 0 &&
2034
- omissions.attentionItems === 0;
2035
- const settings = [
2036
- {
2037
- id: "deadline",
2038
- name: t("app.diag.deadline.title"),
2039
- nameIsToken: false,
2040
- envName: "EMBASSY_MESSAGE_DEADLINE_MS",
2041
- where: t("app.tab.diagnostics"),
2042
- note: undefined,
2043
- },
2044
- {
2045
- id: "queueDepth",
2046
- name: t("app.diag.queue.title"),
2047
- nameIsToken: false,
2048
- envName: "EMBASSY_MAX_QUEUE_MESSAGES",
2049
- where: t("app.tab.overview"),
2050
- note: undefined,
2051
- },
2052
- {
2053
- id: "deliveryNotices",
2054
- name: t("app.diag.deliveryNotices.title"),
2055
- nameIsToken: false,
2056
- envName: "EMBASSY_DELIVERY_NOTICES",
2057
- where: t("app.tab.diagnostics"),
2058
- note: t("app.diag.deliveryNotices.note"),
2059
- },
2060
- {
2061
- id: "steering",
2062
- name: "steering",
2063
- nameIsToken: true,
2064
- envName: undefined,
2065
- where: t("app.tab.diagnostics"),
2066
- note: undefined,
2067
- },
2068
- {
2069
- id: "inboundMode",
2070
- name: "inbound mode",
2071
- nameIsToken: true,
2072
- envName: undefined,
2073
- where: t("app.tab.routes"),
2074
- note: t("app.routes.detail.absent"),
2075
- },
2076
- ];
2077
- return (React.createElement("div", { className: "tab-panel tab-panel--narrow" },
2078
- React.createElement("section", { className: "section", "aria-labelledby": settingsHeadingId },
2079
- React.createElement("h2", { className: "mono-label section-label", id: settingsHeadingId }, t("app.diag.editable.title")),
2080
- React.createElement("div", { className: "stack-lg" },
2081
- React.createElement("div", { className: "grid-2" },
2082
- React.createElement("section", { className: deadlineIsWarn ? "card card--warn" : "card", "aria-labelledby": deadlineHeadingId },
2083
- React.createElement("div", { className: "card__head" },
2084
- React.createElement("h3", { className: "card__title", id: deadlineHeadingId }, t("app.diag.deadline.title")),
2085
- deadlineIsWarn ? (React.createElement(Embassy.StateChip, { domain: "severity", state: "warning", small: true })) : null),
2086
- React.createElement("p", { className: "card__body" }, t("app.diag.deadline.body", {
2087
- count: formatDiagnosticsCount(data.expiredCount, locale),
2088
- })),
2089
- data.deadlinePressure === undefined ? null : (React.createElement("div", { className: "stack-sm" },
2090
- React.createElement("p", { className: "footnote" }, t("app.diag.deadline.retained", {
2091
- terminal: formatDiagnosticsCount(data.deadlinePressure.terminalEvents, locale),
2092
- expired: formatDiagnosticsCount(data.deadlinePressure.expiredEvents, locale),
2093
- deadline: Embassy.fmtAge(data.deadlinePressure.configuredDeadlineMs),
2094
- })),
2095
- React.createElement("dl", { className: "detail-list" }, data.deadlinePressure.buckets.map((bucket) => (React.createElement(React.Fragment, { key: bucket.bucket },
2096
- React.createElement("dt", null, t(deadlineBucketKeys[bucket.bucket])),
2097
- React.createElement("dd", null,
2098
- formatDiagnosticsCount(bucket.settled, locale),
2099
- " \u00B7 ",
2100
- formatDiagnosticsCount(bucket.expired, locale),
2101
- " ",
2102
- t("delivery.expired")))))))),
2103
- React.createElement("p", { className: "env-name" }, "EMBASSY_MESSAGE_DEADLINE_MS"),
2104
- React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MESSAGE_DEADLINE_MS=<ms> embassy serve" })),
2105
- React.createElement(Embassy.AbsentFeature, { title: t("app.diag.steering.title"), body: t("app.diag.steering.absent") })),
2106
- React.createElement("div", { className: "setting-list" }, settings.map((setting) => (React.createElement(DiagnosticsSetting, { key: setting.id, setting: setting })))),
2107
- React.createElement("p", { className: "footnote" }, t("app.diag.editable.note")))),
2108
- React.createElement("section", { className: "section", "aria-labelledby": versionsHeadingId },
2109
- React.createElement("div", { className: "row-baseline section-label" },
2110
- React.createElement("h2", { className: "mono-label", id: versionsHeadingId }, t("app.diag.versions")),
2111
- data.connectorsOmitted > 0 ? (React.createElement(DiagnosticsLowerBound, { count: data.connectors.length })) : null),
2112
- React.createElement("div", { className: "stack" },
2113
- React.createElement(DiagnosticsConnectorTable, { connectors: data.connectors }),
2114
- React.createElement("p", { className: "footnote" }, t("app.diag.versions.rangeAbsent")))),
2115
- React.createElement("div", { className: "grid-2" },
2116
- React.createElement(Embassy.AbsentFeature, { title: t("app.diag.lease.title"), body: t("app.diag.lease.absent"), cmd: "embassy status" })),
2117
- React.createElement("section", { className: "section", "aria-labelledby": limitsHeadingId },
2118
- React.createElement("h2", { className: "mono-label section-label", id: limitsHeadingId }, t("app.diag.limits")),
2119
- React.createElement("div", { className: "stack" },
2120
- React.createElement("section", { className: "card", "aria-labelledby": queueHeadingId },
2121
- React.createElement("div", { className: "card__head" },
2122
- React.createElement("h3", { className: "card__title", id: queueHeadingId }, t("app.diag.queue.title")),
2123
- data.queueCountIsLowerBound ? (React.createElement(DiagnosticsLowerBound, { count: data.queuedMessages })) : null),
2124
- React.createElement("p", { className: "card__body" }, t("app.diag.queue.body", {
2125
- count: formatDiagnosticsCount(data.queuedMessages, locale),
2126
- })),
2127
- React.createElement("p", { className: "env-name" }, "EMBASSY_MAX_QUEUE_MESSAGES"),
2128
- React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MAX_QUEUE_MESSAGES=<n> embassy serve" })),
2129
- React.createElement("section", { className: "card", "aria-labelledby": bytesHeadingId },
2130
- React.createElement("div", { className: "card__head" },
2131
- React.createElement("h3", { className: "card__title", id: bytesHeadingId }, t("app.diag.bytes.title"))),
2132
- React.createElement("p", { className: "card__body" }, t("app.diag.bytes.body", {
2133
- queued: formatDiagnosticsBytes(accounting.queuedBytes, locale),
2134
- accepted: formatDiagnosticsBytes(accounting.bytesAccepted, locale),
2135
- })),
2136
- React.createElement("p", { className: "env-name" }, "EMBASSY_MAX_MESSAGE_BYTES"),
2137
- React.createElement(Embassy.CopyCmd, { cmd: "EMBASSY_MAX_MESSAGE_BYTES=<n> embassy serve" })),
2138
- React.createElement("p", { className: "footnote" }, t("app.diag.limits.hint")))),
2139
- React.createElement("section", { className: "section" },
2140
- React.createElement(DiagnosticsDisclosure, { label: t("app.diag.counters") },
2141
- React.createElement("div", { className: "details-block__content" },
2142
- React.createElement(DiagnosticsCounterTable, { caption: t("app.diag.counters.caption"), rows: accountingRows }))),
2143
- React.createElement(DiagnosticsDisclosure, { label: t("app.diag.omissions") },
2144
- React.createElement("div", { className: "details-block__content stack" },
2145
- React.createElement(DiagnosticsOmissionTable, { caption: t("app.diag.omissions.caption"), rows: omissionRows }),
2146
- nothingOmitted ? (React.createElement("p", { className: "footnote" }, t("diagnostics.omissions.none"))) : null)))));
2147
- }
2148
- Embassy.DiagnosticsTab = DiagnosticsTab;
2149
- })(Embassy || (Embassy = {}));
2150
- var Embassy;
2151
- (function (Embassy) {
2152
- const PREFS_STORAGE_KEY = "embassy-live-prefs";
2153
- const STALE_NOTICE_MS = 35_000;
2154
- const TAB_KEYS = [
2155
- "overview",
2156
- "deliveries",
2157
- "routes",
2158
- "activity",
2159
- "diagnostics",
2160
- ];
2161
- function isTabKey(value) {
2162
- return (typeof value === "string" &&
2163
- TAB_KEYS.includes(value));
2164
- }
2165
- function loadPrefs(fallbackLocale) {
2166
- try {
2167
- const raw = window.localStorage.getItem(PREFS_STORAGE_KEY);
2168
- if (raw !== null) {
2169
- const parsed = JSON.parse(raw);
2170
- if (typeof parsed === "object" && parsed !== null) {
2171
- const record = parsed;
2172
- const locale = record["locale"];
2173
- const tab = record["tab"];
2174
- return {
2175
- locale: locale === "en" || locale === "zh-CN" ? locale : fallbackLocale,
2176
- tab: isTabKey(tab) ? tab : "overview",
2177
- };
2178
- }
2179
- }
2180
- }
2181
- catch {
2182
- }
2183
- return { locale: fallbackLocale, tab: "overview" };
2184
- }
2185
- function savePrefs(prefs) {
2186
- try {
2187
- window.localStorage.setItem(PREFS_STORAGE_KEY, JSON.stringify(prefs));
2188
- }
2189
- catch {
2190
- }
2191
- }
2192
- function Notice(props) {
2193
- const t = Embassy.useT();
2194
- return (React.createElement("div", { className: "notice", "data-tone": props.tone, role: "status" },
2195
- React.createElement("span", null, props.text),
2196
- React.createElement("button", { type: "button", className: "toggle-link", onClick: props.onDismiss }, t("app.hide"))));
2197
- }
2198
- function Shell(props) {
2199
- const t = Embassy.useT();
2200
- const [locale, setLocale] = Embassy.useLocale();
2201
- const [tab, setTab] = React.useState(props.initialTab);
2202
- const [latest, setLatest] = React.useState(undefined);
2203
- const [connectionState, setConnectionState] = React.useState("connecting");
2204
- const [lastFrameAtMs, setLastFrameAtMs] = React.useState(undefined);
2205
- const [notices, setNotices] = React.useState({
2206
- reset: false,
2207
- missedFrames: false,
2208
- });
2209
- const [preset, setPreset] = React.useState(undefined);
2210
- const [query, setQuery] = React.useState("");
2211
- const nowMs = Embassy.useNowMs();
2212
- const protocolRef = React.useRef(undefined);
2213
- const tabRefs = React.useRef([]);
2214
- React.useEffect(() => {
2215
- const protocol = Embassy.createProtocol({
2216
- onEvent: (event) => {
2217
- setLatest(event);
2218
- setLastFrameAtMs(Date.now());
2219
- },
2220
- onConnectionState: setConnectionState,
2221
- onNotice: (kind) => {
2222
- setNotices((previous) => ({ ...previous, [kind]: true }));
2223
- },
2224
- });
2225
- protocolRef.current = protocol;
2226
- protocol.start();
2227
- return () => {
2228
- protocolRef.current = undefined;
2229
- protocol.pause();
2230
- };
2231
- }, []);
2232
- React.useEffect(() => {
2233
- document.documentElement.lang = locale;
2234
- }, [locale]);
2235
- React.useEffect(() => {
2236
- savePrefs({ locale, tab });
2237
- }, [locale, tab]);
2238
- const goDeliveries = React.useCallback((next) => {
2239
- setPreset(next);
2240
- setTab("deliveries");
2241
- }, []);
2242
- const clearPreset = React.useCallback(() => {
2243
- setPreset(undefined);
2244
- }, []);
2245
- const runAction = React.useCallback(async (action) => protocolRef.current?.executeAction(action) ?? {
2246
- ok: false,
2247
- code: "unavailable",
2248
- }, []);
2249
- const dismissNotice = (kind) => {
2250
- setNotices((previous) => ({ ...previous, [kind]: false }));
2251
- };
2252
- const onSearchKeyDown = (event) => {
2253
- if (event.key !== "Enter")
2254
- return;
2255
- const token = query.trim();
2256
- if (token === "")
2257
- return;
2258
- setPreset({ token });
2259
- setTab("deliveries");
2260
- setQuery("");
2261
- };
2262
- const onTabKeyDown = (event) => {
2263
- const currentIndex = TAB_KEYS.indexOf(tab);
2264
- let nextIndex;
2265
- if (event.key === "ArrowRight") {
2266
- nextIndex = (currentIndex + 1) % TAB_KEYS.length;
2267
- }
2268
- else if (event.key === "ArrowLeft") {
2269
- nextIndex = (currentIndex - 1 + TAB_KEYS.length) % TAB_KEYS.length;
2270
- }
2271
- else if (event.key === "Home") {
2272
- nextIndex = 0;
2273
- }
2274
- else if (event.key === "End") {
2275
- nextIndex = TAB_KEYS.length - 1;
2276
- }
2277
- if (nextIndex === undefined)
2278
- return;
2279
- event.preventDefault();
2280
- const nextTab = TAB_KEYS[nextIndex];
2281
- if (nextTab === undefined)
2282
- return;
2283
- setTab(nextTab);
2284
- tabRefs.current[nextIndex]?.focus();
2285
- };
2286
- const generatedAt = latest?.model.generatedAt;
2287
- const staleAgeMs = lastFrameAtMs === undefined ? undefined : nowMs - lastFrameAtMs;
2288
- const hasNotices = notices.reset || notices.missedFrames;
2289
- function renderActiveTab() {
2290
- if (latest === undefined) {
2291
- return (React.createElement("div", { className: "empty-state" },
2292
- React.createElement("p", { className: "empty-state__text" }, t(`live.connection.${connectionState}`))));
2293
- }
2294
- const model = latest.model;
2295
- switch (tab) {
2296
- case "overview":
2297
- return (React.createElement(Embassy.OverviewTab, { data: Embassy.adapter.overviewProps(model, nowMs), onViewDeliveries: goDeliveries }));
2298
- case "deliveries":
2299
- return (React.createElement(Embassy.DeliveriesTab, { groups: Embassy.adapter.deliveriesGroups(model), watches: model.watches ?? [], watchEvents: model.watchEvents ?? [], omissions: model.omissions, preset: preset, clearPreset: clearPreset }));
2300
- case "routes":
2301
- return (React.createElement(Embassy.RoutesTab, { data: Embassy.adapter.routesProps(model, nowMs), actionsEnabled: connectionState === "connected", onAction: runAction }));
2302
- case "activity":
2303
- return (React.createElement(Embassy.ActivityTab, { rows: Embassy.adapter.activityRows(model), omissions: model.omissions }));
2304
- case "diagnostics":
2305
- return React.createElement(Embassy.DiagnosticsTab, { data: Embassy.adapter.diagnosticsProps(model) });
2306
- }
2307
- }
2308
- return (React.createElement("div", { className: "app-shell" },
2309
- React.createElement("header", { className: "app-header" },
2310
- React.createElement("div", { className: "header-bar" },
2311
- React.createElement("div", { className: "brand" },
2312
- React.createElement("svg", { width: "26", height: "26", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", className: "brand-mark" },
2313
- React.createElement("path", { d: "M32 16 V5 L41 7.5 L32 10", fill: "#f4a259" }),
2314
- React.createElement("path", { d: "M21.5 55 V32 C21.5 23 42.5 23 42.5 32 V55 Z", fill: "#f4a259" }),
2315
- React.createElement("path", { d: "M14 55 V30.5 C14 17 50 17 50 30.5 V55", stroke: "#17171c", strokeWidth: "3.4", strokeLinecap: "round" }),
2316
- React.createElement("path", { d: "M10 55.5 H54", stroke: "#17171c", strokeWidth: "3", strokeLinecap: "round" })),
2317
- React.createElement("span", { className: "brand-wordmark" }, "Embassy"),
2318
- React.createElement("span", { className: "mono-label" }, t("live.label")),
2319
- latest === undefined ? null : (React.createElement("span", { className: "brand-posture" },
2320
- React.createElement(Embassy.StateChip, { domain: "overall", state: latest.model.overall, small: true })))),
2321
- React.createElement("div", { className: "header-spacer" }),
2322
- React.createElement("input", { type: "search", className: "text-input header-search", value: query, onChange: (event) => {
2323
- setQuery(event.target.value);
2324
- }, onKeyDown: onSearchKeyDown, "aria-label": t("app.search.label"), placeholder: t("app.search.placeholder") }),
2325
- React.createElement("div", { className: "header-meta" },
2326
- React.createElement("span", { className: "as-of", title: generatedAt }, t("app.asOf", {
2327
- time: generatedAt === undefined
2328
- ? t("time.unavailable")
2329
- : Embassy.fmtAbs(generatedAt, locale),
2330
- })),
2331
- React.createElement("span", { className: "conn-status", "aria-live": "polite" },
2332
- React.createElement(Embassy.StateChip, { domain: "connection", state: connectionState, label: t(`live.connection.${connectionState}`), small: true })),
2333
- React.createElement("div", { className: "lang-toggle" },
2334
- React.createElement("button", { type: "button", className: "lang-toggle__btn", "aria-pressed": locale === "en", onClick: () => {
2335
- setLocale("en");
2336
- } }, "EN"),
2337
- React.createElement("button", { type: "button", className: "lang-toggle__btn", "aria-pressed": locale === "zh-CN", onClick: () => {
2338
- setLocale("zh-CN");
2339
- } }, "\u4E2D\u6587"))),
2340
- React.createElement("div", { className: "header-subrow" },
2341
- staleAgeMs !== undefined && staleAgeMs >= STALE_NOTICE_MS ? (React.createElement("p", { className: connectionState === "connected"
2342
- ? "staleness staleness--quiet"
2343
- : "staleness", role: "status" }, connectionState === "connected"
2344
- ? t("app.staleQuiet", { age: Embassy.fmtAge(staleAgeMs) })
2345
- : t("app.stale", { age: Embassy.fmtAge(staleAgeMs) }))) : null,
2346
- React.createElement("span", { className: "conn-controls" },
2347
- React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.pause() }, t("live.control.pause")),
2348
- React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.reconnect() }, t("live.control.reconnect")),
2349
- React.createElement("button", { type: "button", className: "btn btn--ghost", onClick: () => protocolRef.current?.readNow() }, t("live.control.refresh"))))),
2350
- React.createElement("nav", { className: "app-nav", role: "tablist", "aria-label": t("live.label") }, TAB_KEYS.map((key, index) => (React.createElement("button", { key: key, type: "button", role: "tab", id: `tab-${key}`, className: "tab", "aria-selected": tab === key, "aria-controls": `panel-${key}`, tabIndex: tab === key ? 0 : -1, ref: (element) => {
2351
- tabRefs.current[index] = element;
2352
- }, onClick: () => {
2353
- setTab(key);
2354
- }, onKeyDown: onTabKeyDown }, t(`app.tab.${key}`)))))),
2355
- React.createElement("main", { className: "app-main" },
2356
- React.createElement("div", { className: "stack-lg" },
2357
- hasNotices ? (React.createElement("div", { className: "stack" },
2358
- notices.reset ? (React.createElement(Notice, { tone: "info", text: t("live.stream.reset"), onDismiss: () => {
2359
- dismissNotice("reset");
2360
- } })) : null,
2361
- notices.missedFrames ? (React.createElement(Notice, { tone: "warning", text: t("app.missedFrames"), onDismiss: () => {
2362
- dismissNotice("missedFrames");
2363
- } })) : null)) : null,
2364
- React.createElement("div", { role: "tabpanel", id: `panel-${tab}`, "aria-labelledby": `tab-${tab}`, tabIndex: 0 }, renderActiveTab()))),
2365
- React.createElement("footer", { className: "app-footer" },
2366
- React.createElement("span", { className: "mono-label" }, t("live.action.authorityLabel")),
2367
- React.createElement("p", null, t("live.action.authorityBody")),
2368
- React.createElement("p", { className: "footer-diagnostic" },
2369
- t("live.metric.revision"),
2370
- ":",
2371
- " ",
2372
- latest === undefined ? "—" : String(latest.streamRevision)))));
2373
- }
2374
- function App() {
2375
- const boot = window.EMBASSY_BOOT;
2376
- const [initialPrefs] = React.useState(() => loadPrefs(boot.locale));
2377
- return (React.createElement(Embassy.I18nProvider, { copy: boot.copy, initialLocale: initialPrefs.locale },
2378
- React.createElement(Shell, { initialTab: initialPrefs.tab })));
2379
- }
2380
- Embassy.App = App;
2381
- const rootElement = document.getElementById("root");
2382
- if (rootElement !== null) {
2383
- ReactDOM.createRoot(rootElement).render(React.createElement(App, null));
2384
- }
2385
- })(Embassy || (Embassy = {}));